blob: 06dfce7e2fb39513aec3d94d1bbf5d72eb38398d [file] [log] [blame] [edit]
/*
* Copyright © 2011 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod
*/
#ifndef VIEW_OPTIONS_HH
#define VIEW_OPTIONS_HH
#include "options.hh"
#define DEFAULT_MARGIN 16
#define DEFAULT_FORE "#000000"
#define DEFAULT_BACK "#FFFFFF"
#define DEFAULT_STROKE "#888"
#define DEFAULT_STROKE_WIDTH 1
struct view_options_t
{
~view_options_t ()
{
g_free (fore);
g_free (stroke);
g_free (back);
g_free (custom_palette);
if (foreground_palette)
g_array_unref (foreground_palette);
if (custom_palette_entries)
g_array_unref (custom_palette_entries);
}
void add_options (option_parser_t *parser);
bool include_logical_extents () const { return logical || !ink; }
bool include_ink_extents () const { return ink || !logical; }
void post_parse (GError **error G_GNUC_UNUSED)
{
stroke_enabled = false;
stroke_width = DEFAULT_STROKE_WIDTH;
if (!parse_color (DEFAULT_STROKE, stroke_color.r, stroke_color.g, stroke_color.b, stroke_color.a))
fail (false, "Failed parsing default stroke color `%s`", DEFAULT_STROKE);
const bool stroke_specified = stroke != nullptr;
if (stroke_specified)
{
stroke_enabled = true;
char *spec = g_strdup (stroke);
char *s = g_strstrip (spec);
if (*s)
{
char *plus = strchr (s, '+');
if (plus && strchr (plus + 1, '+'))
fail (false, "Failed parsing stroke spec `%s`", stroke);
char *color_spec = s;
char *width_spec = nullptr;
if (plus)
{
*plus = '\0';
width_spec = g_strstrip (plus + 1);
}
color_spec = g_strstrip (color_spec);
if (*color_spec)
{
if (!parse_color (color_spec, stroke_color.r, stroke_color.g, stroke_color.b, stroke_color.a))
fail (false, "Failed parsing stroke color `%s`", color_spec);
}
if (width_spec && *width_spec)
{
char *end = nullptr;
errno = 0;
double w = g_ascii_strtod (width_spec, &end);
if (!end || errno || *g_strstrip (end) || w <= 0)
fail (false, "Failed parsing stroke width `%s`", width_spec);
stroke_width = w;
}
}
g_free (spec);
}
if (!parse_custom_palette_entries (error))
return;
if (!parse_color (DEFAULT_BACK,
background_color.r, background_color.g,
background_color.b, background_color.a))
fail (false, "Failed parsing default background color `%s`", DEFAULT_BACK);
has_background = false;
if (back && *back)
{
if (!parse_color (back,
background_color.r, background_color.g,
background_color.b, background_color.a))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid background color `%s`", back);
return;
}
has_background = true;
}
foreground_use_palette = false;
if (foreground_palette)
{
g_array_unref (foreground_palette);
foreground_palette = nullptr;
}
const char *foreground = fore ? fore : DEFAULT_FORE;
if (!foreground)
return;
if (!parse_color (DEFAULT_FORE,
foreground_color.r, foreground_color.g,
foreground_color.b, foreground_color.a))
fail (false, "Failed parsing default foreground color `%s`", DEFAULT_FORE);
const bool use_rainbow =
foreground_use_rainbow ||
0 == g_ascii_strcasecmp (foreground, "rainbow") ||
0 == g_ascii_strcasecmp (foreground, "lgbtq");
if (use_rainbow)
{
static const char *rainbow[] =
{
"#DA0000BB",
"#FF6200BB",
"#D4B900BB",
"#005100BB",
"#000CFFBB",
"#42005BBB",
nullptr
};
foreground_palette = g_array_new (false, false, sizeof (rgba_color_t));
for (const char **entry = rainbow; *entry; entry++)
{
rgba_color_t color = {0, 0, 0, 255};
if (!parse_color (*entry, color.r, color.g, color.b, color.a))
fail (false, "Failed parsing foreground color `%s`", *entry);
g_array_append_val (foreground_palette, color);
}
if (foreground_palette->len)
foreground_color =
g_array_index (foreground_palette, rgba_color_t, 0);
foreground_use_palette = true;
if (!stroke_specified)
stroke_enabled = true;
return;
}
if (!strchr (foreground, ','))
{
rgba_color_t color = {0, 0, 0, 255};
if (!parse_color (foreground, color.r, color.g, color.b, color.a))
fail (false, "Failed parsing foreground color `%s`", foreground);
foreground_color = color;
return;
}
foreground_palette = g_array_new (false, false, sizeof (rgba_color_t));
char **entries = g_strsplit (foreground, ",", -1);
for (unsigned i = 0; entries[i]; i++)
{
char *entry = g_strstrip (entries[i]);
if (!*entry)
continue;
rgba_color_t color = {0, 0, 0, 255};
if (!parse_color (entry, color.r, color.g, color.b, color.a))
fail (false, "Failed parsing foreground color `%s`", entry);
g_array_append_val (foreground_palette, color);
}
g_strfreev (entries);
if (!foreground_palette->len)
{
g_array_unref (foreground_palette);
foreground_palette = nullptr;
return;
}
foreground_color = g_array_index (foreground_palette, rgba_color_t, 0);
foreground_use_palette = true;
}
struct rgba_color_t {
unsigned r, g, b, a;
};
struct custom_palette_entry_t {
unsigned index;
rgba_color_t color;
};
char *fore = nullptr;
char *stroke = nullptr;
char *back = nullptr;
unsigned int palette = 0;
char *custom_palette = nullptr;
GArray *custom_palette_entries = nullptr;
GArray *foreground_palette = nullptr;
hb_bool_t foreground_use_palette = false;
hb_bool_t foreground_use_rainbow = false;
hb_bool_t stroke_enabled = false;
rgba_color_t stroke_color = {0, 0, 0, 255};
double stroke_width = DEFAULT_STROKE_WIDTH;
rgba_color_t foreground_color = {0, 0, 0, 255};
rgba_color_t background_color = {255, 255, 255, 255};
hb_bool_t has_background = false;
double line_space = 0;
bool have_font_extents = false;
struct font_extents_t {
double ascent, descent, line_gap;
} font_extents = {0., 0., 0.};
struct margin_t {
double t, r, b, l;
} margin = {DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN};
hb_bool_t logical = false;
hb_bool_t ink = false;
hb_bool_t show_extents = false;
bool parse_custom_palette_entries (GError **error)
{
if (custom_palette_entries)
{
g_array_unref (custom_palette_entries);
custom_palette_entries = nullptr;
}
if (!custom_palette || !*custom_palette)
return true;
custom_palette_entries = g_array_new (false, false, sizeof (custom_palette_entry_t));
char **entries = g_strsplit (custom_palette, ",", -1);
unsigned next_index = 0;
for (unsigned i = 0; entries && entries[i]; i++)
{
char *entry = g_strstrip (entries[i]);
if (!*entry)
continue;
unsigned idx = next_index;
char *color_spec = entry;
char *eq = strchr (entry, '=');
if (eq)
{
*eq = '\0';
char *idx_spec = g_strstrip (entry);
color_spec = g_strstrip (eq + 1);
if (!*idx_spec || !*color_spec)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid --custom-palette entry");
g_strfreev (entries);
return false;
}
char *end = nullptr;
errno = 0;
unsigned long parsed = strtoul (idx_spec, &end, 10);
if (errno || !end || *g_strstrip (end))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid --custom-palette index `%s`", idx_spec);
g_strfreev (entries);
return false;
}
idx = (unsigned) parsed;
next_index = idx + 1;
}
rgba_color_t color = {0, 0, 0, 255};
if (!parse_color (color_spec, color.r, color.g, color.b, color.a))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Invalid --custom-palette color `%s`", color_spec);
g_strfreev (entries);
return false;
}
custom_palette_entry_t parsed = {idx, color};
g_array_append_val (custom_palette_entries, parsed);
if (!eq)
next_index++;
}
g_strfreev (entries);
if (!custom_palette_entries->len)
{
g_array_unref (custom_palette_entries);
custom_palette_entries = nullptr;
}
return true;
}
};
static gboolean
parse_font_extents (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
view_options_t *view_opts = (view_options_t *) data;
view_options_t::font_extents_t &e = view_opts->font_extents;
switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf", &e.ascent, &e.descent, &e.line_gap)) {
case 1: HB_FALLTHROUGH;
case 2: HB_FALLTHROUGH;
case 3:
view_opts->have_font_extents = true;
return true;
default:
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"%s argument should be one to three space-separated numbers",
name);
return false;
}
}
static gboolean
parse_margin (const char *name G_GNUC_UNUSED,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
view_options_t *view_opts = (view_options_t *) data;
view_options_t::margin_t &m = view_opts->margin;
if (parse_1to4_doubles (arg, &m.t, &m.r, &m.b, &m.l))
return true;
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"%s argument should be one to four space-separated numbers",
name);
return false;
}
void
view_options_t::add_options (option_parser_t *parser)
{
GOptionEntry entries[] =
{
{"annotate", 0, G_OPTION_FLAG_HIDDEN,
G_OPTION_ARG_NONE, &this->show_extents, "Annotate output rendering", nullptr},
{"background", 0, 0, G_OPTION_ARG_STRING, &this->back, "Set background color (default: " DEFAULT_BACK ")", "rrggbb/rrggbbaa"},
{"foreground", 0, 0, G_OPTION_ARG_STRING, &this->fore, "Set foreground color (default: " DEFAULT_FORE ")", "rrggbb/rrggbbaa[,...]"},
{"rainbow", 0, 0, G_OPTION_ARG_NONE, &this->foreground_use_rainbow, "Rotate glyph foreground colors through a default palette", nullptr},
{"lgbtq", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &this->foreground_use_rainbow, "Hidden alias for --rainbow", nullptr},
{"stroke", 0, 0, G_OPTION_ARG_STRING, &this->stroke, "Stroke glyph outlines; `--stroke=` enables defaults", "rrggbb/rrggbbaa[+width]"},
{"font-palette", 0, 0, G_OPTION_ARG_INT, &this->palette, "Set font palette (default: 0)", "index"},
{"custom-palette", 0, 0, G_OPTION_ARG_STRING, &this->custom_palette, "Custom palette", "comma-separated colors"},
{"line-space", 0, 0, G_OPTION_ARG_DOUBLE, &this->line_space, "Set space between lines (default: 0)", "units"},
{"font-extents", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_font_extents, "Set font ascent/descent/line-gap (default: auto)","one to three numbers"},
{"margin", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_margin, "Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
{"logical", 0, 0, G_OPTION_ARG_NONE, &this->logical, "Render to logical box instead of union of logical and ink boxes", nullptr},
{"ink", 0, 0, G_OPTION_ARG_NONE, &this->ink, "Render to ink box instead of union of logical and ink boxes", nullptr},
{"show-extents", 0, 0, G_OPTION_ARG_NONE, &this->show_extents, "Draw glyph extents", nullptr},
{nullptr}
};
parser->add_group (entries,
"view",
"View options:",
"Options for output rendering",
this);
}
#endif