options.cc 30.3 KB
Newer Older
B
Behdad Esfahbod 已提交
1
/*
2
 * Copyright © 2011,2012  Google, Inc.
B
Behdad Esfahbod 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *  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
 */

#include "options.hh"

B
Minor  
Behdad Esfahbod 已提交
29
#ifdef HAVE_FREETYPE
30
#include <hb-ft.h>
31
#endif
B
Behdad Esfahbod 已提交
32
#include <hb-ot.h>
B
Behdad Esfahbod 已提交
33

B
Behdad Esfahbod 已提交
34
static struct supported_font_funcs_t {
35 36 37 38 39 40 41 42 43 44
	char name[4];
	void (*func) (hb_font_t *);
} supported_font_funcs[] =
{
#ifdef HAVE_FREETYPE
  {"ft",	hb_ft_font_set_funcs},
#endif
  {"ot",	hb_ot_font_set_funcs},
};

B
Behdad Esfahbod 已提交
45

B
Behdad Esfahbod 已提交
46 47 48 49 50 51 52 53
void
fail (hb_bool_t suggest_help, const char *format, ...)
{
  const char *msg;

  va_list vap;
  va_start (vap, format);
  msg = g_strdup_vprintf (format, vap);
B
Behdad Esfahbod 已提交
54
  va_end (vap);
B
Behdad Esfahbod 已提交
55 56 57 58 59 60 61 62 63
  const char *prgname = g_get_prgname ();
  g_printerr ("%s: %s\n", prgname, msg);
  if (suggest_help)
    g_printerr ("Try `%s --help' for more information.\n", prgname);

  exit (1);
}


64 65 66
static gchar *
shapers_to_string (void)
{
B
Behdad Esfahbod 已提交
67
  GString *shapers = g_string_new (nullptr);
68 69 70 71 72 73 74 75
  const char **shaper_list = hb_shape_list_shapers ();

  for (; *shaper_list; shaper_list++) {
    g_string_append (shapers, *shaper_list);
    g_string_append_c (shapers, ',');
  }
  g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));

76
  return g_string_free (shapers, false);
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
}

static G_GNUC_NORETURN gboolean
show_version (const char *name G_GNUC_UNUSED,
	      const char *arg G_GNUC_UNUSED,
	      gpointer    data G_GNUC_UNUSED,
	      GError    **error G_GNUC_UNUSED)
{
  g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);

  char *shapers = shapers_to_string ();
  g_printf ("Available shapers: %s\n", shapers);
  g_free (shapers);
  if (strcmp (HB_VERSION_STRING, hb_version_string ()))
    g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());

  exit(0);
}


void
option_parser_t::add_main_options (void)
{
  GOptionEntry entries[] =
  {
    {"version",		0, G_OPTION_FLAG_NO_ARG,
B
Behdad Esfahbod 已提交
103 104
			      G_OPTION_ARG_CALLBACK,	(gpointer) &show_version,	"Show version numbers",			nullptr},
    {nullptr}
105
  };
B
Behdad Esfahbod 已提交
106
  g_option_context_add_main_entries (context, entries, nullptr);
107 108 109 110 111 112 113 114 115 116
}

static gboolean
pre_parse (GOptionContext *context G_GNUC_UNUSED,
	   GOptionGroup *group G_GNUC_UNUSED,
	   gpointer data,
	   GError **error)
{
  option_group_t *option_group = (option_group_t *) data;
  option_group->pre_parse (error);
B
Behdad Esfahbod 已提交
117
  return *error == nullptr;
118 119 120 121 122 123 124 125 126 127
}

static gboolean
post_parse (GOptionContext *context G_GNUC_UNUSED,
	    GOptionGroup *group G_GNUC_UNUSED,
	    gpointer data,
	    GError **error)
{
  option_group_t *option_group = static_cast<option_group_t *>(data);
  option_group->post_parse (error);
B
Behdad Esfahbod 已提交
128
  return *error == nullptr;
129 130 131 132 133 134 135 136 137 138
}

void
option_parser_t::add_group (GOptionEntry   *entries,
			    const gchar    *name,
			    const gchar    *description,
			    const gchar    *help_description,
			    option_group_t *option_group)
{
  GOptionGroup *group = g_option_group_new (name, description, help_description,
B
Behdad Esfahbod 已提交
139
					    static_cast<gpointer>(option_group), nullptr);
140 141 142 143 144 145 146 147
  g_option_group_add_entries (group, entries);
  g_option_group_set_parse_hooks (group, pre_parse, post_parse);
  g_option_context_add_group (context, group);
}

void
option_parser_t::parse (int *argc, char ***argv)
{
B
Behdad Esfahbod 已提交
148 149
  setlocale (LC_ALL, "");

B
Behdad Esfahbod 已提交
150
  GError *parse_error = nullptr;
151 152
  if (!g_option_context_parse (context, argc, argv, &parse_error))
  {
B
Behdad Esfahbod 已提交
153
    if (parse_error != nullptr) {
154
      fail (true, "%s", parse_error->message);
B
Behdad Esfahbod 已提交
155 156
      //g_error_free (parse_error);
    } else
157
      fail (true, "Option parse error");
158 159
  }
}
B
Behdad Esfahbod 已提交
160 161 162 163 164


static gboolean
parse_margin (const char *name G_GNUC_UNUSED,
	      const char *arg,
B
Behdad Esfahbod 已提交
165
	      gpointer    data,
B
Behdad Esfahbod 已提交
166 167
	      GError    **error G_GNUC_UNUSED)
{
B
Behdad Esfahbod 已提交
168
  view_options_t *view_opts = (view_options_t *) data;
B
Behdad Esfahbod 已提交
169
  view_options_t::margin_t &m = view_opts->margin;
170
  switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) {
B
Behdad Esfahbod 已提交
171 172 173
    case 1: m.r = m.t; HB_FALLTHROUGH;
    case 2: m.b = m.t; HB_FALLTHROUGH;
    case 3: m.l = m.r; HB_FALLTHROUGH;
174
    case 4: return true;
B
Behdad Esfahbod 已提交
175 176 177 178
    default:
      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
		   "%s argument should be one to four space-separated numbers",
		   name);
179
      return false;
B
Behdad Esfahbod 已提交
180 181 182 183 184 185 186
  }
}


static gboolean
parse_shapers (const char *name G_GNUC_UNUSED,
	       const char *arg,
B
Behdad Esfahbod 已提交
187
	       gpointer    data,
188
	       GError    **error)
B
Behdad Esfahbod 已提交
189
{
B
Behdad Esfahbod 已提交
190
  shape_options_t *shape_opts = (shape_options_t *) data;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
  char **shapers = g_strsplit (arg, ",", 0);

  for (char **shaper = shapers; *shaper; shaper++) {
    bool found = false;
    for (const char **hb_shaper = hb_shape_list_shapers (); *hb_shaper; hb_shaper++) {
      if (strcmp (*shaper, *hb_shaper) == 0) {
        found = true;
        break;
      }
    }
    if (!found) {
      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
		   "Unknown or unsupported shaper: %s", *shaper);
      g_strfreev (shapers);
      return false;
    }
  }

B
Behdad Esfahbod 已提交
209
  g_strfreev (shape_opts->shapers);
210
  shape_opts->shapers = shapers;
211
  return true;
B
Behdad Esfahbod 已提交
212 213
}

214 215 216 217 218 219 220 221 222 223 224 225 226
static G_GNUC_NORETURN gboolean
list_shapers (const char *name G_GNUC_UNUSED,
	      const char *arg G_GNUC_UNUSED,
	      gpointer    data G_GNUC_UNUSED,
	      GError    **error G_GNUC_UNUSED)
{
  for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++)
    g_printf ("%s\n", *shaper);

  exit(0);
}


B
Behdad Esfahbod 已提交
227 228 229
static gboolean
parse_features (const char *name G_GNUC_UNUSED,
	        const char *arg,
B
Behdad Esfahbod 已提交
230
	        gpointer    data,
B
Behdad Esfahbod 已提交
231 232
	        GError    **error G_GNUC_UNUSED)
{
B
Behdad Esfahbod 已提交
233
  shape_options_t *shape_opts = (shape_options_t *) data;
B
Behdad Esfahbod 已提交
234 235 236 237
  char *s = (char *) arg;
  char *p;

  shape_opts->num_features = 0;
238
  g_free (shape_opts->features);
B
Behdad Esfahbod 已提交
239
  shape_opts->features = nullptr;
B
Behdad Esfahbod 已提交
240 241

  if (!*s)
242
    return true;
B
Behdad Esfahbod 已提交
243 244 245 246 247 248 249 250 251 252 253

  /* count the features first, so we can allocate memory */
  p = s;
  do {
    shape_opts->num_features++;
    p = strchr (p, ',');
    if (p)
      p++;
  } while (p);

  shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
254 255
  if (!shape_opts->features)
    return false;
B
Behdad Esfahbod 已提交
256 257 258 259

  /* now do the actual parsing */
  p = s;
  shape_opts->num_features = 0;
260 261 262
  while (p && *p) {
    char *end = strchr (p, ',');
    if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features]))
B
Behdad Esfahbod 已提交
263
      shape_opts->num_features++;
B
Behdad Esfahbod 已提交
264
    p = end ? end + 1 : nullptr;
B
Behdad Esfahbod 已提交
265 266
  }

267
  return true;
B
Behdad Esfahbod 已提交
268 269
}

B
Behdad Esfahbod 已提交
270 271 272 273 274 275 276 277 278 279 280 281
static gboolean
parse_variations (const char *name G_GNUC_UNUSED,
	        const char *arg,
	        gpointer    data,
	        GError    **error G_GNUC_UNUSED)
{
  font_options_t *font_opts = (font_options_t *) data;
  char *s = (char *) arg;
  char *p;

  font_opts->num_variations = 0;
  g_free (font_opts->variations);
B
Behdad Esfahbod 已提交
282
  font_opts->variations = nullptr;
B
Behdad Esfahbod 已提交
283 284 285 286 287 288 289 290 291 292 293 294 295 296

  if (!*s)
    return true;

  /* count the variations first, so we can allocate memory */
  p = s;
  do {
    font_opts->num_variations++;
    p = strchr (p, ',');
    if (p)
      p++;
  } while (p);

  font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations));
297 298
  if (!font_opts->variations)
    return false;
B
Behdad Esfahbod 已提交
299 300 301 302 303 304 305 306

  /* now do the actual parsing */
  p = s;
  font_opts->num_variations = 0;
  while (p && *p) {
    char *end = strchr (p, ',');
    if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations]))
      font_opts->num_variations++;
B
Behdad Esfahbod 已提交
307
    p = end ? end + 1 : nullptr;
B
Behdad Esfahbod 已提交
308 309 310 311 312
  }

  return true;
}

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
static gboolean
parse_text (const char *name G_GNUC_UNUSED,
	    const char *arg,
	    gpointer    data,
	    GError    **error G_GNUC_UNUSED)
{
  text_options_t *text_opts = (text_options_t *) data;

  if (text_opts->text)
  {
    g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
		 "Either --text or --unicodes can be provided but not both");
    return false;
  }

  text_opts->text = g_strdup (arg);
  return true;
}


static gboolean
parse_unicodes (const char *name G_GNUC_UNUSED,
	        const char *arg,
	        gpointer    data,
	        GError    **error G_GNUC_UNUSED)
{
  text_options_t *text_opts = (text_options_t *) data;

  if (text_opts->text)
  {
    g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
		 "Either --text or --unicodes can be provided but not both");
    return false;
  }

B
Behdad Esfahbod 已提交
348
  GString *gs = g_string_new (nullptr);
349 350 351 352 353
  char *s = (char *) arg;
  char *p;

  while (s && *s)
  {
354
    while (*s && strchr ("<+>{},;&#\\xXuUnNiI\n\t\v\f\r ", *s))
355
      s++;
356 357
    if (!*s)
      break;
358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376

    errno = 0;
    hb_codepoint_t u = strtoul (s, &p, 16);
    if (errno || s == p)
    {
      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
		   "Failed parsing Unicode values at: '%s'", s);
      return false;
    }

    g_string_append_unichar (gs, u);

    s = p;
  }

  text_opts->text = g_string_free (gs, FALSE);
  return true;
}

B
Behdad Esfahbod 已提交
377 378

void
379
view_options_t::add_options (option_parser_t *parser)
B
Behdad Esfahbod 已提交
380 381 382
{
  GOptionEntry entries[] =
  {
B
Behdad Esfahbod 已提交
383
    {"annotate",	0, 0, G_OPTION_ARG_NONE,	&this->annotate,		"Annotate output rendering",				nullptr},
B
Minor  
Behdad Esfahbod 已提交
384 385
    {"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"},
B
Behdad Esfahbod 已提交
386
    {"line-space",	0, 0, G_OPTION_ARG_DOUBLE,	&this->line_space,		"Set space between lines (default: 0)",			"units"},
B
Behdad Esfahbod 已提交
387
    {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
B
Behdad Esfahbod 已提交
388
    {nullptr}
389
  };
390 391 392
  parser->add_group (entries,
		     "view",
		     "View options:",
393
		     "Options for output rendering",
394
		     this);
395
}
B
Behdad Esfahbod 已提交
396

397
void
398
shape_options_t::add_options (option_parser_t *parser)
399 400 401
{
  GOptionEntry entries[] =
  {
402
    {"list-shapers",	0, G_OPTION_FLAG_NO_ARG,
B
Behdad Esfahbod 已提交
403
			      G_OPTION_ARG_CALLBACK,	(gpointer) &list_shapers,	"List available shapers and quit",	nullptr},
404
    {"shaper",		0, G_OPTION_FLAG_HIDDEN,
B
Behdad Esfahbod 已提交
405
			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Hidden duplicate of --shapers",	nullptr},
406
    {"shapers",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Set comma-separated list of shapers to try","list"},
B
Behdad Esfahbod 已提交
407 408 409
    {"direction",	0, 0, G_OPTION_ARG_STRING,	&this->direction,		"Set text direction (default: auto)",	"ltr/rtl/ttb/btt"},
    {"language",	0, 0, G_OPTION_ARG_STRING,	&this->language,		"Set text language (default: $LANG)",	"langstr"},
    {"script",		0, 0, G_OPTION_ARG_STRING,	&this->script,			"Set text script (default: auto)",	"ISO-15924 tag"},
B
Behdad Esfahbod 已提交
410 411 412
    {"bot",		0, 0, G_OPTION_ARG_NONE,	&this->bot,			"Treat text as beginning-of-paragraph",	nullptr},
    {"eot",		0, 0, G_OPTION_ARG_NONE,	&this->eot,			"Treat text as end-of-paragraph",	nullptr},
    {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->preserve_default_ignorables,	"Preserve Default-Ignorable characters",	nullptr},
413
    {"remove-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->remove_default_ignorables,	"Remove Default-Ignorable characters",	nullptr},
414
    {"invisible-glyph",	0, 0, G_OPTION_ARG_INT,		&this->invisible_glyph,		"Glyph value to replace Default-Ignorables with",	nullptr},
B
Behdad Esfahbod 已提交
415
    {"utf8-clusters",	0, 0, G_OPTION_ARG_NONE,	&this->utf8_clusters,		"Use UTF8 byte indices, not char indices",	nullptr},
416
    {"cluster-level",	0, 0, G_OPTION_ARG_INT,		&this->cluster_level,		"Cluster merging level (default: 0)",	"0/1/2"},
B
Behdad Esfahbod 已提交
417 418
    {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE,	&this->normalize_glyphs,	"Rearrange glyph clusters in nominal order",	nullptr},
    {"verify",		0, 0, G_OPTION_ARG_NONE,	&this->verify,			"Perform sanity checks on shaping results",	nullptr},
419
    {"num-iterations", 'n', 0, G_OPTION_ARG_INT,		&this->num_iterations,		"Run shaper N times (default: 1)",	"N"},
B
Behdad Esfahbod 已提交
420
    {nullptr}
421
  };
422 423 424
  parser->add_group (entries,
		     "shape",
		     "Shape options:",
425
		     "Options for the shaping process",
426
		     this);
B
Behdad Esfahbod 已提交
427

428
  const gchar *features_help = "Comma-separated list of font features\n"
B
Behdad Esfahbod 已提交
429 430
    "\n"
    "    Features can be enabled or disabled, either globally or limited to\n"
431 432 433
    "    specific character ranges.  The format for specifying feature settings\n"
    "    follows.  All valid CSS font-feature-settings values other than 'normal'\n"
    "    and 'inherited' are also accepted, though, not documented below.\n"
B
Behdad Esfahbod 已提交
434 435 436 437 438
    "\n"
    "    The range indices refer to the positions between Unicode characters,\n"
    "    unless the --utf8-clusters is provided, in which case range indices\n"
    "    refer to UTF-8 byte indices. The position before the first character\n"
    "    is always 0.\n"
439 440
    "\n"
    "    The format is Python-esque.  Here is how it all works:\n"
B
Behdad Esfahbod 已提交
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
    "\n"
    "      Syntax:       Value:    Start:    End:\n"
    "\n"
    "    Setting value:\n"
    "      \"kern\"        1         0         ∞         # Turn feature on\n"
    "      \"+kern\"       1         0         ∞         # Turn feature on\n"
    "      \"-kern\"       0         0         ∞         # Turn feature off\n"
    "      \"kern=0\"      0         0         ∞         # Turn feature off\n"
    "      \"kern=1\"      1         0         ∞         # Turn feature on\n"
    "      \"aalt=2\"      2         0         ∞         # Choose 2nd alternate\n"
    "\n"
    "    Setting index:\n"
    "      \"kern[]\"      1         0         ∞         # Turn feature on\n"
    "      \"kern[:]\"     1         0         ∞         # Turn feature on\n"
    "      \"kern[5:]\"    1         5         ∞         # Turn feature on, partial\n"
    "      \"kern[:5]\"    1         0         5         # Turn feature on, partial\n"
    "      \"kern[3:5]\"   1         3         5         # Turn feature on, range\n"
    "      \"kern[3]\"     1         3         3+1       # Turn feature on, single char\n"
    "\n"
    "    Mixing it all:\n"
    "\n"
B
Minor  
Behdad Esfahbod 已提交
462
    "      \"aalt[3:5]=2\" 2         3         5         # Turn 2nd alternate on for range";
B
Behdad Esfahbod 已提交
463 464 465 466

  GOptionEntry entries2[] =
  {
    {"features",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_features,	features_help,	"list"},
B
Behdad Esfahbod 已提交
467
    {nullptr}
B
Behdad Esfahbod 已提交
468 469 470 471
  };
  parser->add_group (entries2,
		     "features",
		     "Features options:",
472
		     "Options for font features used",
B
Behdad Esfahbod 已提交
473
		     this);
474
}
B
Behdad Esfahbod 已提交
475

476 477 478 479 480 481 482 483 484 485 486 487
static gboolean
parse_font_size (const char *name G_GNUC_UNUSED,
		 const char *arg,
		 gpointer    data,
		 GError    **error G_GNUC_UNUSED)
{
  font_options_t *font_opts = (font_options_t *) data;
  if (0 == strcmp (arg, "upem"))
  {
    font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM;
    return true;
  }
488
  switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) {
B
Behdad Esfahbod 已提交
489
    case 1: font_opts->font_size_y = font_opts->font_size_x; HB_FALLTHROUGH;
490 491 492
    case 2: return true;
    default:
      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
B
Minor  
Behdad Esfahbod 已提交
493
		   "%s argument should be one or two space-separated numbers",
494 495 496 497
		   name);
      return false;
  }
}
498 499 500 501 502 503 504 505 506

static gboolean
parse_font_ppem (const char *name G_GNUC_UNUSED,
		 const char *arg,
		 gpointer    data,
		 GError    **error G_GNUC_UNUSED)
{
  font_options_t *font_opts = (font_options_t *) data;
  switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) {
B
Behdad Esfahbod 已提交
507
    case 1: font_opts->y_ppem = font_opts->x_ppem; HB_FALLTHROUGH;
508 509 510 511 512 513 514 515 516
    case 2: return true;
    default:
      g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
		   "%s argument should be one or two space-separated numbers",
		   name);
      return false;
  }
}

517
void
518
font_options_t::add_options (option_parser_t *parser)
519
{
B
Behdad Esfahbod 已提交
520
  char *text = nullptr;
521 522

  {
523 524
    static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0),
		   "No supported font-funcs found.");
B
Behdad Esfahbod 已提交
525
    GString *s = g_string_new (nullptr);
526 527 528 529 530 531 532 533 534 535 536 537
    g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n    Supported font function implementations are: %s",
		     supported_font_funcs[0].name,
		     supported_font_funcs[0].name);
    for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++)
    {
      g_string_append_c (s, '/');
      g_string_append (s, supported_font_funcs[i].name);
    }
    text = g_string_free (s, FALSE);
    parser->free_later (text);
  }

538 539 540 541 542 543 544 545 546
  char *font_size_text;
  if (default_font_size == FONT_SIZE_UPEM)
    font_size_text = (char *) "Font size (default: upem)";
  else
  {
    font_size_text = g_strdup_printf ("Font size (default: %d)", default_font_size);
    parser->free_later (font_size_text);
  }

547 548
  GOptionEntry entries[] =
  {
549 550
    {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Set font file-name",				"filename"},
    {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Set face index (default: 0)",			"index"},
551
    {"font-size",	0, default_font_size ? 0 : G_OPTION_FLAG_HIDDEN,
552 553 554 555
			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_font_size,	font_size_text,					"1/2 integers or 'upem'"},
    {"font-ppem",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_font_ppem,	"Set x,y pixels per EM (default: 0; disabled)",	"1/2 integers"},
    {"font-ptem",	0, 0, G_OPTION_ARG_DOUBLE,	&this->ptem,			"Set font point-size (default: 0; disabled)",	"point-size"},
    {"font-funcs",	0, 0, G_OPTION_ARG_STRING,	&this->font_funcs,		text,						"impl"},
B
Behdad Esfahbod 已提交
556
    {"ft-load-flags",	0, 0, G_OPTION_ARG_INT,		&this->ft_load_flags,		"Set FreeType load-flags (default: 2)",		"integer"},
B
Behdad Esfahbod 已提交
557
    {nullptr}
558
  };
559 560 561
  parser->add_group (entries,
		     "font",
		     "Font options:",
562
		     "Options for the font",
563
		     this);
B
Behdad Esfahbod 已提交
564 565 566

  const gchar *variations_help = "Comma-separated list of font variations\n"
    "\n"
567 568
    "    Variations are set globally. The format for specifying variation settings\n"
    "    follows.  All valid CSS font-variation-settings values other than 'normal'\n"
B
Behdad Esfahbod 已提交
569 570
    "    and 'inherited' are also accepted, though, not documented below.\n"
    "\n"
571 572
    "    The format is a tag, optionally followed by an equals sign, followed by a\n"
    "    number. For example:\n"
B
Behdad Esfahbod 已提交
573
    "\n"
574 575
    "      \"wght=500\"\n"
    "      \"slnt=-7.5\"\n";
B
Behdad Esfahbod 已提交
576 577 578 579

  GOptionEntry entries2[] =
  {
    {"variations",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_variations,	variations_help,	"list"},
B
Behdad Esfahbod 已提交
580
    {nullptr}
B
Behdad Esfahbod 已提交
581 582 583
  };
  parser->add_group (entries2,
		     "variations",
B
Typo  
Behdad Esfahbod 已提交
584
		     "Variations options:",
585
		     "Options for font variations used",
B
Behdad Esfahbod 已提交
586
		     this);
587
}
B
Behdad Esfahbod 已提交
588

589
void
590
text_options_t::add_options (option_parser_t *parser)
591 592 593
{
  GOptionEntry entries[] =
  {
594
    {"text",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_text,		"Set input text",			"string"},
B
Minor  
Behdad Esfahbod 已提交
595
    {"text-file",	0, 0, G_OPTION_ARG_STRING,	&this->text_file,		"Set input text file-name\n\n    If no text is provided, standard input is used for input.\n",		"filename"},
596
    {"unicodes",      'u', 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_unicodes,		"Set input Unicode codepoints",		"list of hex numbers"},
597 598
    {"text-before",	0, 0, G_OPTION_ARG_STRING,	&this->text_before,		"Set text context before each line",	"string"},
    {"text-after",	0, 0, G_OPTION_ARG_STRING,	&this->text_after,		"Set text context after each line",	"string"},
B
Behdad Esfahbod 已提交
599
    {nullptr}
B
Behdad Esfahbod 已提交
600
  };
601 602 603
  parser->add_group (entries,
		     "text",
		     "Text options:",
604
		     "Options for the input text",
605 606 607 608 609 610
		     this);
}

void
output_options_t::add_options (option_parser_t *parser)
{
611 612
  const char *text;

B
Behdad Esfahbod 已提交
613
  if (nullptr == supported_formats)
614
    text = "Set output serialization format";
615
  else
B
Behdad Esfahbod 已提交
616 617
  {
    char *items = g_strjoinv ("/", const_cast<char **> (supported_formats));
B
Behdad Esfahbod 已提交
618
    text = g_strdup_printf ("Set output format\n\n    Supported output formats are: %s", items);
B
Behdad Esfahbod 已提交
619
    g_free (items);
B
Behdad Esfahbod 已提交
620
    parser->free_later ((char *) text);
B
Behdad Esfahbod 已提交
621
  }
622

623 624
  GOptionEntry entries[] =
  {
625 626
    {"output-file",   'o', 0, G_OPTION_ARG_STRING,	&this->output_file,		"Set output file-name (default: stdout)","filename"},
    {"output-format", 'O', 0, G_OPTION_ARG_STRING,	&this->output_format,		text,					"format"},
B
Behdad Esfahbod 已提交
627
    {nullptr}
628 629 630
  };
  parser->add_group (entries,
		     "output",
631
		     "Output destination & format options:",
632
		     "Options for the destination & form of the output",
633 634
		     this);
}
B
Behdad Esfahbod 已提交
635 636 637



638 639 640 641 642 643 644
hb_font_t *
font_options_t::get_font (void) const
{
  if (font)
    return font;

  /* Create the blob */
645 646 647
  if (!font_file)
    fail (true, "No font file set");

648 649 650 651
  const char *font_path = font_file;

  if (0 == strcmp (font_path, "-"))
  {
B
Behdad Esfahbod 已提交
652
#if defined(_WIN32) || defined(__CYGWIN__)
653
    setmode (fileno (stdin), O_BINARY);
654 655 656
    font_path = "STDIN";
#else
    font_path = "/dev/stdin";
657
#endif
658 659
  }

660 661
  blob = hb_blob_create_from_file (font_path);

662
  if (blob == hb_blob_get_empty ())
663
    fail (false, "Couldn't read or find %s, or it was empty.", font_path);
664

665 666 667 668 669 670 671
  /* Create the face */
  hb_face_t *face = hb_face_create (blob, face_index);
  hb_blob_destroy (blob);


  font = hb_font_create (face);

672 673 674 675 676
  if (font_size_x == FONT_SIZE_UPEM)
    font_size_x = hb_face_get_upem (face);
  if (font_size_y == FONT_SIZE_UPEM)
    font_size_y = hb_face_get_upem (face);

677 678 679
  hb_font_set_ppem (font, x_ppem, y_ppem);
  hb_font_set_ptem (font, ptem);

680 681 682
  int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
  int scale_y = (int) scalbnf (font_size_y, subpixel_bits);
  hb_font_set_scale (font, scale_x, scale_y);
683 684
  hb_face_destroy (face);

B
Behdad Esfahbod 已提交
685 686
  hb_font_set_variations (font, variations, num_variations);

B
Behdad Esfahbod 已提交
687
  void (*set_font_funcs) (hb_font_t *) = nullptr;
688 689 690 691 692 693 694
  if (!font_funcs)
  {
    set_font_funcs = supported_font_funcs[0].func;
  }
  else
  {
    for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
C
Chun-wei Fan 已提交
695
      if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name))
696 697 698 699 700 701
      {
	set_font_funcs = supported_font_funcs[i].func;
	break;
      }
    if (!set_font_funcs)
    {
B
Behdad Esfahbod 已提交
702
      GString *s = g_string_new (nullptr);
703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
      for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
      {
        if (i)
	  g_string_append_c (s, '/');
	g_string_append (s, supported_font_funcs[i].name);
      }
      char *p = g_string_free (s, FALSE);
      fail (false, "Unknown font function implementation `%s'; supported values are: %s; default is %s",
	    font_funcs,
	    p,
	    supported_font_funcs[0].name);
      //free (p);
    }
  }
  set_font_funcs (font);
B
Behdad Esfahbod 已提交
718 719 720
#ifdef HAVE_FREETYPE
  hb_ft_font_set_load_flags (font, ft_load_flags);
#endif
721 722 723 724 725 726 727 728

  return font;
}


const char *
text_options_t::get_line (unsigned int *len)
{
B
Behdad Esfahbod 已提交
729
  if (text) {
B
Behdad Esfahbod 已提交
730 731 732
    if (!line) line = text;
    if (line_len == (unsigned int) -1)
      line_len = strlen (line);
B
Behdad Esfahbod 已提交
733

B
Behdad Esfahbod 已提交
734
    if (!line_len) {
B
Behdad Esfahbod 已提交
735
      *len = 0;
B
Behdad Esfahbod 已提交
736
      return nullptr;
B
Behdad Esfahbod 已提交
737 738
    }

B
Behdad Esfahbod 已提交
739 740
    const char *ret = line;
    const char *p = (const char *) memchr (line, '\n', line_len);
B
Behdad Esfahbod 已提交
741 742
    unsigned int ret_len;
    if (!p) {
B
Behdad Esfahbod 已提交
743 744 745
      ret_len = line_len;
      line += ret_len;
      line_len = 0;
B
Behdad Esfahbod 已提交
746 747
    } else {
      ret_len = p - ret;
B
Behdad Esfahbod 已提交
748 749
      line += ret_len + 1;
      line_len -= ret_len + 1;
B
Behdad Esfahbod 已提交
750 751 752 753 754 755 756
    }

    *len = ret_len;
    return ret;
  }

  if (!fp) {
757
    if (!text_file)
758
      fail (true, "At least one of text or text-file must be set");
759

B
Behdad Esfahbod 已提交
760 761 762 763
    if (0 != strcmp (text_file, "-"))
      fp = fopen (text_file, "r");
    else
      fp = stdin;
B
Behdad Esfahbod 已提交
764

B
Behdad Esfahbod 已提交
765
    if (!fp)
766
      fail (false, "Failed opening text file `%s': %s",
B
Behdad Esfahbod 已提交
767
	    text_file, strerror (errno));
768

B
Behdad Esfahbod 已提交
769
    gs = g_string_new (nullptr);
770 771
  }

B
Behdad Esfahbod 已提交
772 773 774 775
  g_string_set_size (gs, 0);
  char buf[BUFSIZ];
  while (fgets (buf, sizeof (buf), fp)) {
    unsigned int bytes = strlen (buf);
B
Behdad Esfahbod 已提交
776
    if (bytes && buf[bytes - 1] == '\n') {
B
Behdad Esfahbod 已提交
777 778 779 780 781
      bytes--;
      g_string_append_len (gs, buf, bytes);
      break;
    }
      g_string_append_len (gs, buf, bytes);
B
Behdad Esfahbod 已提交
782
  }
B
Behdad Esfahbod 已提交
783
  if (ferror (fp))
784
    fail (false, "Failed reading text: %s",
B
Behdad Esfahbod 已提交
785 786
	  strerror (errno));
  *len = gs->len;
B
Behdad Esfahbod 已提交
787
  return !*len && feof (fp) ? nullptr : gs->str;
B
Behdad Esfahbod 已提交
788
}
B
Behdad Esfahbod 已提交
789 790 791 792 793 794 795 796 797 798 799


FILE *
output_options_t::get_file_handle (void)
{
  if (fp)
    return fp;

  if (output_file)
    fp = fopen (output_file, "wb");
  else {
B
Behdad Esfahbod 已提交
800
#if defined(_WIN32) || defined(__CYGWIN__)
801
    setmode (fileno (stdout), O_BINARY);
B
Behdad Esfahbod 已提交
802 803 804 805
#endif
    fp = stdout;
  }
  if (!fp)
806
    fail (false, "Cannot open output file `%s': %s",
B
Behdad Esfahbod 已提交
807 808 809 810
	  g_filename_display_name (output_file), strerror (errno));

  return fp;
}
B
Behdad Esfahbod 已提交
811

B
Behdad Esfahbod 已提交
812 813 814 815 816 817 818
static gboolean
parse_verbose (const char *name G_GNUC_UNUSED,
	       const char *arg G_GNUC_UNUSED,
	       gpointer    data G_GNUC_UNUSED,
	       GError    **error G_GNUC_UNUSED)
{
  format_options_t *format_opts = (format_options_t *) data;
819 820
  format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true;
  return true;
B
Behdad Esfahbod 已提交
821
}
B
Behdad Esfahbod 已提交
822

B
Behdad Esfahbod 已提交
823 824 825 826 827 828 829 830 831 832 833
static gboolean
parse_ned (const char *name G_GNUC_UNUSED,
	   const char *arg G_GNUC_UNUSED,
	   gpointer    data G_GNUC_UNUSED,
	   GError    **error G_GNUC_UNUSED)
{
  format_options_t *format_opts = (format_options_t *) data;
  format_opts->show_clusters = format_opts->show_advances = false;
  return true;
}

B
Behdad Esfahbod 已提交
834 835 836 837 838
void
format_options_t::add_options (option_parser_t *parser)
{
  GOptionEntry entries[] =
  {
B
Behdad Esfahbod 已提交
839 840 841
    {"show-text",	0, 0, G_OPTION_ARG_NONE,	&this->show_text,		"Prefix each line of output with its corresponding input text",		nullptr},
    {"show-unicode",	0, 0, G_OPTION_ARG_NONE,	&this->show_unicode,		"Prefix each line of output with its corresponding input codepoint(s)",	nullptr},
    {"show-line-num",	0, 0, G_OPTION_ARG_NONE,	&this->show_line_num,		"Prefix each line of output with its corresponding input line number",	nullptr},
842
    {"verbose",	      'v', G_OPTION_FLAG_NO_ARG,
B
Behdad Esfahbod 已提交
843
			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_verbose,	"Prefix each line of output with all of the above",			nullptr},
B
Behdad Esfahbod 已提交
844
    {"no-glyph-names",	0, G_OPTION_FLAG_REVERSE,
B
Behdad Esfahbod 已提交
845
			      G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Output glyph indices instead of names",				nullptr},
B
Behdad Esfahbod 已提交
846
    {"no-positions",	0, G_OPTION_FLAG_REVERSE,
B
Behdad Esfahbod 已提交
847
			      G_OPTION_ARG_NONE,	&this->show_positions,		"Do not output glyph positions",					nullptr},
848 849
    {"no-advances",	0, G_OPTION_FLAG_REVERSE,
			      G_OPTION_ARG_NONE,	&this->show_advances,		"Do not output glyph advances",						nullptr},
B
Behdad Esfahbod 已提交
850
    {"no-clusters",	0, G_OPTION_FLAG_REVERSE,
B
Behdad Esfahbod 已提交
851 852 853
			      G_OPTION_ARG_NONE,	&this->show_clusters,		"Do not output cluster indices",					nullptr},
    {"show-extents",	0, 0, G_OPTION_ARG_NONE,	&this->show_extents,		"Output glyph extents",							nullptr},
    {"show-flags",	0, 0, G_OPTION_ARG_NONE,	&this->show_flags,		"Output glyph flags",							nullptr},
B
Behdad Esfahbod 已提交
854 855
    {"ned",	      'v', G_OPTION_FLAG_NO_ARG,
			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_ned,		"No Extra Data; Do not output clusters or advances",			nullptr},
B
Behdad Esfahbod 已提交
856 857
    {"trace",	      'V', 0, G_OPTION_ARG_NONE,	&this->trace,			"Output interim shaping results",					nullptr},
    {nullptr}
B
Behdad Esfahbod 已提交
858 859
  };
  parser->add_group (entries,
860 861 862 863 864
		     "output-syntax",
		     "Output syntax:\n"
         "    text: [<glyph name or index>=<glyph cluster index within input>@<horizontal displacement>,<vertical displacement>+<horizontal advance>,<vertical advance>|...]\n"
         "    json: [{\"g\": <glyph name or index>, \"ax\": <horizontal advance>, \"ay\": <vertical advance>, \"dx\": <horizontal displacement>, \"dy\": <vertical displacement>, \"cl\": <glyph cluster index within input>}, ...]\n"
         "\nOutput syntax options:",
865
		     "Options for the syntax of the output",
B
Behdad Esfahbod 已提交
866 867 868 869
		     this);
}

void
870 871 872 873
format_options_t::serialize_unicode (hb_buffer_t *buffer,
				     GString     *gs)
{
  unsigned int num_glyphs = hb_buffer_get_length (buffer);
B
Behdad Esfahbod 已提交
874
  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
875 876 877 878 879 880 881 882 883 884 885 886 887 888 889

  g_string_append_c (gs, '<');
  for (unsigned int i = 0; i < num_glyphs; i++)
  {
    if (i)
      g_string_append_c (gs, ',');
    g_string_append_printf (gs, "U+%04X", info->codepoint);
    info++;
  }
  g_string_append_c (gs, '>');
}

void
format_options_t::serialize_glyphs (hb_buffer_t *buffer,
				    hb_font_t   *font,
890 891
				    hb_buffer_serialize_format_t output_format,
				    hb_buffer_serialize_flags_t flags,
892
				    GString     *gs)
B
Behdad Esfahbod 已提交
893
{
894
  g_string_append_c (gs, '[');
895 896 897
  unsigned int num_glyphs = hb_buffer_get_length (buffer);
  unsigned int start = 0;

898 899
  while (start < num_glyphs)
  {
900 901 902 903 904 905 906 907
    char buf[1024];
    unsigned int consumed;
    start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs,
					 buf, sizeof (buf), &consumed,
					 font, output_format, flags);
    if (!consumed)
      break;
    g_string_append (gs, buf);
B
Behdad Esfahbod 已提交
908
  }
909
  g_string_append_c (gs, ']');
B
Behdad Esfahbod 已提交
910
}
B
Behdad Esfahbod 已提交
911 912 913 914 915 916 917 918
void
format_options_t::serialize_line_no (unsigned int  line_no,
				     GString      *gs)
{
  if (show_line_num)
    g_string_append_printf (gs, "%d: ", line_no);
}
void
919 920 921 922 923 924
format_options_t::serialize_buffer_of_text (hb_buffer_t  *buffer,
					    unsigned int  line_no,
					    const char   *text,
					    unsigned int  text_len,
					    hb_font_t    *font,
					    GString      *gs)
B
Behdad Esfahbod 已提交
925
{
926 927
  if (show_text)
  {
B
Behdad Esfahbod 已提交
928
    serialize_line_no (line_no, gs);
929
    g_string_append_c (gs, '(');
B
Behdad Esfahbod 已提交
930
    g_string_append_len (gs, text, text_len);
931
    g_string_append_c (gs, ')');
B
Behdad Esfahbod 已提交
932 933 934
    g_string_append_c (gs, '\n');
  }

935 936
  if (show_unicode)
  {
B
Behdad Esfahbod 已提交
937
    serialize_line_no (line_no, gs);
B
Behdad Esfahbod 已提交
938
    serialize_unicode (buffer, gs);
B
Behdad Esfahbod 已提交
939 940
    g_string_append_c (gs, '\n');
  }
941 942 943
}
void
format_options_t::serialize_message (unsigned int  line_no,
944
				     const char   *type,
945 946 947 948
				     const char   *msg,
				     GString      *gs)
{
  serialize_line_no (line_no, gs);
949
  g_string_append_printf (gs, "%s: %s", type, msg);
950 951 952 953 954 955 956 957
  g_string_append_c (gs, '\n');
}
void
format_options_t::serialize_buffer_of_glyphs (hb_buffer_t  *buffer,
					      unsigned int  line_no,
					      const char   *text,
					      unsigned int  text_len,
					      hb_font_t    *font,
958 959
					      hb_buffer_serialize_format_t output_format,
					      hb_buffer_serialize_flags_t format_flags,
960 961
					      GString      *gs)
{
B
Behdad Esfahbod 已提交
962
  serialize_line_no (line_no, gs);
963
  serialize_glyphs (buffer, font, output_format, format_flags, gs);
B
Behdad Esfahbod 已提交
964 965
  g_string_append_c (gs, '\n');
}
R
Rod Sheeter 已提交
966 967 968 969 970 971

void
subset_options_t::add_options (option_parser_t *parser)
{
  GOptionEntry entries[] =
  {
972
    {"no-hinting", 0, 0, G_OPTION_ARG_NONE,  &this->drop_hints,   "Whether to drop hints",   nullptr},
973
    {"desubroutinize", 0, 0, G_OPTION_ARG_NONE,  &this->desubroutinize,   "Remove CFF/CFF2 use of subroutines",   nullptr},
974

R
Rod Sheeter 已提交
975 976 977 978 979 980 981 982
    {nullptr}
  };
  parser->add_group (entries,
         "subset",
         "Subset options:",
         "Options subsetting",
         this);
}