43ff90ad10345974cca9a831d19b6256bf269017
[platform/upstream/harfbuzz.git] / util / options.cc
1 /*
2  * Copyright © 2011,2012  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26
27 #include "options.hh"
28
29 #ifdef HAVE_FREETYPE
30 #include <hb-ft.h>
31 #endif
32 #include <hb-ot.h>
33
34 #define DELIMITERS "<+>{},;&#\\xXuUnNiI\n\t\v\f\r "
35
36 static struct supported_font_funcs_t {
37         char name[4];
38         void (*func) (hb_font_t *);
39 } supported_font_funcs[] =
40 {
41 #ifdef HAVE_FREETYPE
42   {"ft",        hb_ft_font_set_funcs},
43 #endif
44   {"ot",        hb_ot_font_set_funcs},
45 };
46
47
48 void
49 fail (hb_bool_t suggest_help, const char *format, ...)
50 {
51   const char *msg;
52
53   va_list vap;
54   va_start (vap, format);
55   msg = g_strdup_vprintf (format, vap);
56   va_end (vap);
57   const char *prgname = g_get_prgname ();
58   g_printerr ("%s: %s\n", prgname, msg);
59   if (suggest_help)
60     g_printerr ("Try `%s --help' for more information.\n", prgname);
61
62   exit (1);
63 }
64
65
66 static gchar *
67 shapers_to_string ()
68 {
69   GString *shapers = g_string_new (nullptr);
70   const char **shaper_list = hb_shape_list_shapers ();
71
72   for (; *shaper_list; shaper_list++) {
73     g_string_append (shapers, *shaper_list);
74     g_string_append_c (shapers, ',');
75   }
76   g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
77
78   return g_string_free (shapers, false);
79 }
80
81 static G_GNUC_NORETURN gboolean
82 show_version (const char *name G_GNUC_UNUSED,
83               const char *arg G_GNUC_UNUSED,
84               gpointer    data G_GNUC_UNUSED,
85               GError    **error G_GNUC_UNUSED)
86 {
87   g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
88
89   char *shapers = shapers_to_string ();
90   g_printf ("Available shapers: %s\n", shapers);
91   g_free (shapers);
92   if (strcmp (HB_VERSION_STRING, hb_version_string ()))
93     g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
94
95   exit(0);
96 }
97
98
99 void
100 option_parser_t::add_main_options ()
101 {
102   GOptionEntry entries[] =
103   {
104     {"version",         0, G_OPTION_FLAG_NO_ARG,
105                               G_OPTION_ARG_CALLBACK,    (gpointer) &show_version,       "Show version numbers",                 nullptr},
106     {nullptr}
107   };
108   g_option_context_add_main_entries (context, entries, nullptr);
109 }
110
111 static gboolean
112 pre_parse (GOptionContext *context G_GNUC_UNUSED,
113            GOptionGroup *group G_GNUC_UNUSED,
114            gpointer data,
115            GError **error)
116 {
117   option_group_t *option_group = (option_group_t *) data;
118   option_group->pre_parse (error);
119   return *error == nullptr;
120 }
121
122 static gboolean
123 post_parse (GOptionContext *context G_GNUC_UNUSED,
124             GOptionGroup *group G_GNUC_UNUSED,
125             gpointer data,
126             GError **error)
127 {
128   option_group_t *option_group = static_cast<option_group_t *>(data);
129   option_group->post_parse (error);
130   return *error == nullptr;
131 }
132
133 void
134 option_parser_t::add_group (GOptionEntry   *entries,
135                             const gchar    *name,
136                             const gchar    *description,
137                             const gchar    *help_description,
138                             option_group_t *option_group)
139 {
140   GOptionGroup *group = g_option_group_new (name, description, help_description,
141                                             static_cast<gpointer>(option_group), nullptr);
142   g_option_group_add_entries (group, entries);
143   g_option_group_set_parse_hooks (group, pre_parse, post_parse);
144   g_option_context_add_group (context, group);
145 }
146
147 void
148 option_parser_t::parse (int *argc, char ***argv)
149 {
150   setlocale (LC_ALL, "");
151
152   GError *parse_error = nullptr;
153   if (!g_option_context_parse (context, argc, argv, &parse_error))
154   {
155     if (parse_error != nullptr) {
156       fail (true, "%s", parse_error->message);
157       //g_error_free (parse_error);
158     } else
159       fail (true, "Option parse error");
160   }
161 }
162
163
164 static gboolean
165 parse_margin (const char *name G_GNUC_UNUSED,
166               const char *arg,
167               gpointer    data,
168               GError    **error G_GNUC_UNUSED)
169 {
170   view_options_t *view_opts = (view_options_t *) data;
171   view_options_t::margin_t &m = view_opts->margin;
172   switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) {
173     case 1: m.r = m.t; HB_FALLTHROUGH;
174     case 2: m.b = m.t; HB_FALLTHROUGH;
175     case 3: m.l = m.r; HB_FALLTHROUGH;
176     case 4: return true;
177     default:
178       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
179                    "%s argument should be one to four space-separated numbers",
180                    name);
181       return false;
182   }
183 }
184
185
186 static gboolean
187 parse_shapers (const char *name G_GNUC_UNUSED,
188                const char *arg,
189                gpointer    data,
190                GError    **error)
191 {
192   shape_options_t *shape_opts = (shape_options_t *) data;
193   char **shapers = g_strsplit (arg, ",", 0);
194
195   for (char **shaper = shapers; *shaper; shaper++) {
196     bool found = false;
197     for (const char **hb_shaper = hb_shape_list_shapers (); *hb_shaper; hb_shaper++) {
198       if (strcmp (*shaper, *hb_shaper) == 0) {
199         found = true;
200         break;
201       }
202     }
203     if (!found) {
204       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
205                    "Unknown or unsupported shaper: %s", *shaper);
206       g_strfreev (shapers);
207       return false;
208     }
209   }
210
211   g_strfreev (shape_opts->shapers);
212   shape_opts->shapers = shapers;
213   return true;
214 }
215
216 static G_GNUC_NORETURN gboolean
217 list_shapers (const char *name G_GNUC_UNUSED,
218               const char *arg G_GNUC_UNUSED,
219               gpointer    data G_GNUC_UNUSED,
220               GError    **error G_GNUC_UNUSED)
221 {
222   for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++)
223     g_printf ("%s\n", *shaper);
224
225   exit(0);
226 }
227
228
229 static gboolean
230 parse_features (const char *name G_GNUC_UNUSED,
231                 const char *arg,
232                 gpointer    data,
233                 GError    **error G_GNUC_UNUSED)
234 {
235   shape_options_t *shape_opts = (shape_options_t *) data;
236   char *s = (char *) arg;
237   char *p;
238
239   shape_opts->num_features = 0;
240   g_free (shape_opts->features);
241   shape_opts->features = nullptr;
242
243   if (!*s)
244     return true;
245
246   /* count the features first, so we can allocate memory */
247   p = s;
248   do {
249     shape_opts->num_features++;
250     p = strchr (p, ',');
251     if (p)
252       p++;
253   } while (p);
254
255   shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
256   if (!shape_opts->features)
257     return false;
258
259   /* now do the actual parsing */
260   p = s;
261   shape_opts->num_features = 0;
262   while (p && *p) {
263     char *end = strchr (p, ',');
264     if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features]))
265       shape_opts->num_features++;
266     p = end ? end + 1 : nullptr;
267   }
268
269   return true;
270 }
271
272 static gboolean
273 parse_variations (const char *name G_GNUC_UNUSED,
274                   const char *arg,
275                   gpointer    data,
276                   GError    **error G_GNUC_UNUSED)
277 {
278   font_options_t *font_opts = (font_options_t *) data;
279   char *s = (char *) arg;
280   char *p;
281
282   font_opts->num_variations = 0;
283   g_free (font_opts->variations);
284   font_opts->variations = nullptr;
285
286   if (!*s)
287     return true;
288
289   /* count the variations first, so we can allocate memory */
290   p = s;
291   do {
292     font_opts->num_variations++;
293     p = strchr (p, ',');
294     if (p)
295       p++;
296   } while (p);
297
298   font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations));
299   if (!font_opts->variations)
300     return false;
301
302   /* now do the actual parsing */
303   p = s;
304   font_opts->num_variations = 0;
305   while (p && *p) {
306     char *end = strchr (p, ',');
307     if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations]))
308       font_opts->num_variations++;
309     p = end ? end + 1 : nullptr;
310   }
311
312   return true;
313 }
314
315 static gboolean
316 parse_text (const char *name G_GNUC_UNUSED,
317             const char *arg,
318             gpointer    data,
319             GError    **error G_GNUC_UNUSED)
320 {
321   text_options_t *text_opts = (text_options_t *) data;
322
323   if (text_opts->text)
324   {
325     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
326                  "Either --text or --unicodes can be provided but not both");
327     return false;
328   }
329
330   text_opts->text_len = -1;
331   text_opts->text = g_strdup (arg);
332   return true;
333 }
334
335
336 static gboolean
337 parse_unicodes (const char *name G_GNUC_UNUSED,
338                 const char *arg,
339                 gpointer    data,
340                 GError    **error G_GNUC_UNUSED)
341 {
342   text_options_t *text_opts = (text_options_t *) data;
343
344   if (text_opts->text)
345   {
346     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
347                  "Either --text or --unicodes can be provided but not both");
348     return false;
349   }
350
351   GString *gs = g_string_new (nullptr);
352   if (0 == strcmp (arg, "*"))
353   {
354     g_string_append_c (gs, '*');
355   }
356   else
357   {
358
359     char *s = (char *) arg;
360     char *p;
361
362     while (s && *s)
363     {
364       while (*s && strchr (DELIMITERS, *s))
365         s++;
366       if (!*s)
367         break;
368
369       errno = 0;
370       hb_codepoint_t u = strtoul (s, &p, 16);
371       if (errno || s == p)
372       {
373         g_string_free (gs, TRUE);
374         g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
375                    "Failed parsing Unicode values at: '%s'", s);
376         return false;
377       }
378
379       g_string_append_unichar (gs, u);
380
381       s = p;
382     }
383   }
384
385   text_opts->text_len = gs->len;
386   text_opts->text = g_string_free (gs, FALSE);
387   return true;
388 }
389
390
391 void
392 view_options_t::add_options (option_parser_t *parser)
393 {
394   GOptionEntry entries[] =
395   {
396     {"annotate",        0, 0, G_OPTION_ARG_NONE,        &this->annotate,                "Annotate output rendering",                            nullptr},
397     {"background",      0, 0, G_OPTION_ARG_STRING,      &this->back,                    "Set background color (default: " DEFAULT_BACK ")",     "rrggbb/rrggbbaa"},
398     {"foreground",      0, 0, G_OPTION_ARG_STRING,      &this->fore,                    "Set foreground color (default: " DEFAULT_FORE ")",     "rrggbb/rrggbbaa"},
399     {"line-space",      0, 0, G_OPTION_ARG_DOUBLE,      &this->line_space,              "Set space between lines (default: 0)",                 "units"},
400     {"margin",          0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_margin,       "Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
401     {nullptr}
402   };
403   parser->add_group (entries,
404                      "view",
405                      "View options:",
406                      "Options for output rendering",
407                      this);
408 }
409
410 void
411 shape_options_t::add_options (option_parser_t *parser)
412 {
413   GOptionEntry entries[] =
414   {
415     {"list-shapers",    0, G_OPTION_FLAG_NO_ARG,
416                               G_OPTION_ARG_CALLBACK,    (gpointer) &list_shapers,       "List available shapers and quit",      nullptr},
417     {"shaper",          0, G_OPTION_FLAG_HIDDEN,
418                               G_OPTION_ARG_CALLBACK,    (gpointer) &parse_shapers,      "Hidden duplicate of --shapers",        nullptr},
419     {"shapers",         0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_shapers,      "Set comma-separated list of shapers to try","list"},
420     {"direction",       0, 0, G_OPTION_ARG_STRING,      &this->direction,               "Set text direction (default: auto)",   "ltr/rtl/ttb/btt"},
421     {"language",        0, 0, G_OPTION_ARG_STRING,      &this->language,                "Set text language (default: $LANG)",   "langstr"},
422     {"script",          0, 0, G_OPTION_ARG_STRING,      &this->script,                  "Set text script (default: auto)",      "ISO-15924 tag"},
423     {"bot",             0, 0, G_OPTION_ARG_NONE,        &this->bot,                     "Treat text as beginning-of-paragraph", nullptr},
424     {"eot",             0, 0, G_OPTION_ARG_NONE,        &this->eot,                     "Treat text as end-of-paragraph",       nullptr},
425     {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE,     &this->preserve_default_ignorables,     "Preserve Default-Ignorable characters",        nullptr},
426     {"remove-default-ignorables",0, 0, G_OPTION_ARG_NONE,       &this->remove_default_ignorables,       "Remove Default-Ignorable characters",  nullptr},
427     {"invisible-glyph", 0, 0, G_OPTION_ARG_INT,         &this->invisible_glyph,         "Glyph value to replace Default-Ignorables with",       nullptr},
428     {"utf8-clusters",   0, 0, G_OPTION_ARG_NONE,        &this->utf8_clusters,           "Use UTF8 byte indices, not char indices",      nullptr},
429     {"cluster-level",   0, 0, G_OPTION_ARG_INT,         &this->cluster_level,           "Cluster merging level (default: 0)",   "0/1/2"},
430     {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE,        &this->normalize_glyphs,        "Rearrange glyph clusters in nominal order",    nullptr},
431     {"verify",          0, 0, G_OPTION_ARG_NONE,        &this->verify,                  "Perform sanity checks on shaping results",     nullptr},
432     {"num-iterations", 'n', 0, G_OPTION_ARG_INT,                &this->num_iterations,          "Run shaper N times (default: 1)",      "N"},
433     {nullptr}
434   };
435   parser->add_group (entries,
436                      "shape",
437                      "Shape options:",
438                      "Options for the shaping process",
439                      this);
440
441   const gchar *features_help = "Comma-separated list of font features\n"
442     "\n"
443     "    Features can be enabled or disabled, either globally or limited to\n"
444     "    specific character ranges.  The format for specifying feature settings\n"
445     "    follows.  All valid CSS font-feature-settings values other than 'normal'\n"
446     "    and the global values are also accepted, though not documented below.\n"
447     "    CSS string escapes are not supported."
448     "\n"
449     "    The range indices refer to the positions between Unicode characters,\n"
450     "    unless the --utf8-clusters is provided, in which case range indices\n"
451     "    refer to UTF-8 byte indices. The position before the first character\n"
452     "    is always 0.\n"
453     "\n"
454     "    The format is Python-esque.  Here is how it all works:\n"
455     "\n"
456     "      Syntax:       Value:    Start:    End:\n"
457     "\n"
458     "    Setting value:\n"
459     "      \"kern\"        1         0         ∞         # Turn feature on\n"
460     "      \"+kern\"       1         0         ∞         # Turn feature on\n"
461     "      \"-kern\"       0         0         ∞         # Turn feature off\n"
462     "      \"kern=0\"      0         0         ∞         # Turn feature off\n"
463     "      \"kern=1\"      1         0         ∞         # Turn feature on\n"
464     "      \"aalt=2\"      2         0         ∞         # Choose 2nd alternate\n"
465     "\n"
466     "    Setting index:\n"
467     "      \"kern[]\"      1         0         ∞         # Turn feature on\n"
468     "      \"kern[:]\"     1         0         ∞         # Turn feature on\n"
469     "      \"kern[5:]\"    1         5         ∞         # Turn feature on, partial\n"
470     "      \"kern[:5]\"    1         0         5         # Turn feature on, partial\n"
471     "      \"kern[3:5]\"   1         3         5         # Turn feature on, range\n"
472     "      \"kern[3]\"     1         3         3+1       # Turn feature on, single char\n"
473     "\n"
474     "    Mixing it all:\n"
475     "\n"
476     "      \"aalt[3:5]=2\" 2         3         5         # Turn 2nd alternate on for range";
477
478   GOptionEntry entries2[] =
479   {
480     {"features",        0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_features,     features_help,  "list"},
481     {nullptr}
482   };
483   parser->add_group (entries2,
484                      "features",
485                      "Features options:",
486                      "Options for font features used",
487                      this);
488 }
489
490 static gboolean
491 parse_font_size (const char *name G_GNUC_UNUSED,
492                  const char *arg,
493                  gpointer    data,
494                  GError    **error G_GNUC_UNUSED)
495 {
496   font_options_t *font_opts = (font_options_t *) data;
497   if (0 == strcmp (arg, "upem"))
498   {
499     font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM;
500     return true;
501   }
502   switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) {
503     case 1: font_opts->font_size_y = font_opts->font_size_x; HB_FALLTHROUGH;
504     case 2: return true;
505     default:
506       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
507                    "%s argument should be one or two space-separated numbers",
508                    name);
509       return false;
510   }
511 }
512
513 static gboolean
514 parse_font_ppem (const char *name G_GNUC_UNUSED,
515                  const char *arg,
516                  gpointer    data,
517                  GError    **error G_GNUC_UNUSED)
518 {
519   font_options_t *font_opts = (font_options_t *) data;
520   switch (sscanf (arg, "%d%*[ ,]%d", &font_opts->x_ppem, &font_opts->y_ppem)) {
521     case 1: font_opts->y_ppem = font_opts->x_ppem; HB_FALLTHROUGH;
522     case 2: return true;
523     default:
524       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
525                    "%s argument should be one or two space-separated numbers",
526                    name);
527       return false;
528   }
529 }
530
531 void
532 font_options_t::add_options (option_parser_t *parser)
533 {
534   char *text = nullptr;
535
536   {
537     static_assert ((ARRAY_LENGTH_CONST (supported_font_funcs) > 0),
538                    "No supported font-funcs found.");
539     GString *s = g_string_new (nullptr);
540     g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n    Supported font function implementations are: %s",
541                      supported_font_funcs[0].name,
542                      supported_font_funcs[0].name);
543     for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++)
544     {
545       g_string_append_c (s, '/');
546       g_string_append (s, supported_font_funcs[i].name);
547     }
548     text = g_string_free (s, FALSE);
549     parser->free_later (text);
550   }
551
552   char *font_size_text;
553   if (default_font_size == FONT_SIZE_UPEM)
554     font_size_text = (char *) "Font size (default: upem)";
555   else
556   {
557     font_size_text = g_strdup_printf ("Font size (default: %d)", default_font_size);
558     parser->free_later (font_size_text);
559   }
560
561   GOptionEntry entries[] =
562   {
563     {"font-file",       0, 0, G_OPTION_ARG_STRING,      &this->font_file,               "Set font file-name",                           "filename"},
564     {"face-index",      0, 0, G_OPTION_ARG_INT,         &this->face_index,              "Set face index (default: 0)",                  "index"},
565     {"font-size",       0, default_font_size ? 0 : G_OPTION_FLAG_HIDDEN,
566                               G_OPTION_ARG_CALLBACK,    (gpointer) &parse_font_size,    font_size_text,                                 "1/2 integers or 'upem'"},
567     {"font-ppem",       0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_font_ppem,    "Set x,y pixels per EM (default: 0; disabled)", "1/2 integers"},
568     {"font-ptem",       0, 0, G_OPTION_ARG_DOUBLE,      &this->ptem,                    "Set font point-size (default: 0; disabled)",   "point-size"},
569     {"font-funcs",      0, 0, G_OPTION_ARG_STRING,      &this->font_funcs,              text,                                           "impl"},
570     {"ft-load-flags",   0, 0, G_OPTION_ARG_INT,         &this->ft_load_flags,           "Set FreeType load-flags (default: 2)",         "integer"},
571     {nullptr}
572   };
573   parser->add_group (entries,
574                      "font",
575                      "Font options:",
576                      "Options for the font",
577                      this);
578
579   const gchar *variations_help = "Comma-separated list of font variations\n"
580     "\n"
581     "    Variations are set globally. The format for specifying variation settings\n"
582     "    follows.  All valid CSS font-variation-settings values other than 'normal'\n"
583     "    and 'inherited' are also accepted, though, not documented below.\n"
584     "\n"
585     "    The format is a tag, optionally followed by an equals sign, followed by a\n"
586     "    number. For example:\n"
587     "\n"
588     "      \"wght=500\"\n"
589     "      \"slnt=-7.5\"\n";
590
591   GOptionEntry entries2[] =
592   {
593     {"variations",      0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_variations,   variations_help,        "list"},
594     {nullptr}
595   };
596   parser->add_group (entries2,
597                      "variations",
598                      "Variations options:",
599                      "Options for font variations used",
600                      this);
601 }
602
603 void
604 text_options_t::add_options (option_parser_t *parser)
605 {
606   GOptionEntry entries[] =
607   {
608     {"text",            0, 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_text,         "Set input text",                       "string"},
609     {"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"},
610     {"unicodes",      'u', 0, G_OPTION_ARG_CALLBACK,    (gpointer) &parse_unicodes,             "Set input Unicode codepoints",         "list of hex numbers"},
611     {"text-before",     0, 0, G_OPTION_ARG_STRING,      &this->text_before,             "Set text context before each line",    "string"},
612     {"text-after",      0, 0, G_OPTION_ARG_STRING,      &this->text_after,              "Set text context after each line",     "string"},
613     {nullptr}
614   };
615   parser->add_group (entries,
616                      "text",
617                      "Text options:",
618                      "Options for the input text",
619                      this);
620 }
621
622 void
623 output_options_t::add_options (option_parser_t *parser)
624 {
625   const char *text;
626
627   if (nullptr == supported_formats)
628     text = "Set output serialization format";
629   else
630   {
631     char *items = g_strjoinv ("/", const_cast<char **> (supported_formats));
632     text = g_strdup_printf ("Set output format\n\n    Supported output formats are: %s", items);
633     g_free (items);
634     parser->free_later ((char *) text);
635   }
636
637   GOptionEntry entries[] =
638   {
639     {"output-file",   'o', 0, G_OPTION_ARG_STRING,      &this->output_file,             "Set output file-name (default: stdout)","filename"},
640     {"output-format", 'O', 0, G_OPTION_ARG_STRING,      &this->output_format,           text,                                   "format"},
641     {nullptr}
642   };
643   parser->add_group (entries,
644                      "output",
645                      "Output destination & format options:",
646                      "Options for the destination & form of the output",
647                      this);
648 }
649
650
651
652 hb_font_t *
653 font_options_t::get_font () const
654 {
655   if (font)
656     return font;
657
658   /* Create the blob */
659   if (!font_file)
660     fail (true, "No font file set");
661
662   const char *font_path = font_file;
663
664   if (0 == strcmp (font_path, "-"))
665   {
666 #if defined(_WIN32) || defined(__CYGWIN__)
667     setmode (fileno (stdin), O_BINARY);
668     font_path = "STDIN";
669 #else
670     font_path = "/dev/stdin";
671 #endif
672   }
673
674   blob = hb_blob_create_from_file (font_path);
675
676   if (blob == hb_blob_get_empty ())
677     fail (false, "Couldn't read or find %s, or it was empty.", font_path);
678
679   /* Create the face */
680   hb_face_t *face = hb_face_create (blob, face_index);
681   hb_blob_destroy (blob);
682
683
684   font = hb_font_create (face);
685
686   if (font_size_x == FONT_SIZE_UPEM)
687     font_size_x = hb_face_get_upem (face);
688   if (font_size_y == FONT_SIZE_UPEM)
689     font_size_y = hb_face_get_upem (face);
690
691   hb_font_set_ppem (font, x_ppem, y_ppem);
692   hb_font_set_ptem (font, ptem);
693
694   int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
695   int scale_y = (int) scalbnf (font_size_y, subpixel_bits);
696   hb_font_set_scale (font, scale_x, scale_y);
697   hb_face_destroy (face);
698
699   hb_font_set_variations (font, variations, num_variations);
700
701   void (*set_font_funcs) (hb_font_t *) = nullptr;
702   if (!font_funcs)
703   {
704     set_font_funcs = supported_font_funcs[0].func;
705   }
706   else
707   {
708     for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
709       if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name))
710       {
711         set_font_funcs = supported_font_funcs[i].func;
712         break;
713       }
714     if (!set_font_funcs)
715     {
716       GString *s = g_string_new (nullptr);
717       for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
718       {
719         if (i)
720           g_string_append_c (s, '/');
721         g_string_append (s, supported_font_funcs[i].name);
722       }
723       char *p = g_string_free (s, FALSE);
724       fail (false, "Unknown font function implementation `%s'; supported values are: %s; default is %s",
725             font_funcs,
726             p,
727             supported_font_funcs[0].name);
728       //free (p);
729     }
730   }
731   set_font_funcs (font);
732 #ifdef HAVE_FREETYPE
733   hb_ft_font_set_load_flags (font, ft_load_flags);
734 #endif
735
736   return font;
737 }
738
739
740 const char *
741 text_options_t::get_line (unsigned int *len)
742 {
743   if (text) {
744     if (!line)
745     {
746       line = text;
747       line_len = text_len;
748     }
749     if (line_len == (unsigned int) -1)
750       line_len = strlen (line);
751
752     if (!line_len) {
753       *len = 0;
754       return nullptr;
755     }
756
757     const char *ret = line;
758     const char *p = (const char *) memchr (line, '\n', line_len);
759     unsigned int ret_len;
760     if (!p) {
761       ret_len = line_len;
762       line += ret_len;
763       line_len = 0;
764     } else {
765       ret_len = p - ret;
766       line += ret_len + 1;
767       line_len -= ret_len + 1;
768     }
769
770     *len = ret_len;
771     return ret;
772   }
773
774   if (!fp) {
775     if (!text_file)
776       fail (true, "At least one of text or text-file must be set");
777
778     if (0 != strcmp (text_file, "-"))
779       fp = fopen (text_file, "r");
780     else
781       fp = stdin;
782
783     if (!fp)
784       fail (false, "Failed opening text file `%s': %s",
785             text_file, strerror (errno));
786
787     gs = g_string_new (nullptr);
788   }
789
790   g_string_set_size (gs, 0);
791   char buf[BUFSIZ];
792   while (fgets (buf, sizeof (buf), fp)) {
793     unsigned int bytes = strlen (buf);
794     if (bytes && buf[bytes - 1] == '\n') {
795       bytes--;
796       g_string_append_len (gs, buf, bytes);
797       break;
798     }
799       g_string_append_len (gs, buf, bytes);
800   }
801   if (ferror (fp))
802     fail (false, "Failed reading text: %s",
803           strerror (errno));
804   *len = gs->len;
805   return !*len && feof (fp) ? nullptr : gs->str;
806 }
807
808
809 FILE *
810 output_options_t::get_file_handle ()
811 {
812   if (fp)
813     return fp;
814
815   if (output_file)
816     fp = fopen (output_file, "wb");
817   else {
818 #if defined(_WIN32) || defined(__CYGWIN__)
819     setmode (fileno (stdout), O_BINARY);
820 #endif
821     fp = stdout;
822   }
823   if (!fp)
824     fail (false, "Cannot open output file `%s': %s",
825           g_filename_display_name (output_file), strerror (errno));
826
827   return fp;
828 }
829
830 static gboolean
831 parse_verbose (const char *name G_GNUC_UNUSED,
832                const char *arg G_GNUC_UNUSED,
833                gpointer    data G_GNUC_UNUSED,
834                GError    **error G_GNUC_UNUSED)
835 {
836   format_options_t *format_opts = (format_options_t *) data;
837   format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true;
838   return true;
839 }
840
841 static gboolean
842 parse_ned (const char *name G_GNUC_UNUSED,
843            const char *arg G_GNUC_UNUSED,
844            gpointer    data G_GNUC_UNUSED,
845            GError    **error G_GNUC_UNUSED)
846 {
847   format_options_t *format_opts = (format_options_t *) data;
848   format_opts->show_clusters = format_opts->show_advances = false;
849   return true;
850 }
851
852 void
853 format_options_t::add_options (option_parser_t *parser)
854 {
855   GOptionEntry entries[] =
856   {
857     {"show-text",       0, 0, G_OPTION_ARG_NONE,        &this->show_text,               "Prefix each line of output with its corresponding input text",         nullptr},
858     {"show-unicode",    0, 0, G_OPTION_ARG_NONE,        &this->show_unicode,            "Prefix each line of output with its corresponding input codepoint(s)", nullptr},
859     {"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},
860     {"verbose",       'v', G_OPTION_FLAG_NO_ARG,
861                               G_OPTION_ARG_CALLBACK,    (gpointer) &parse_verbose,      "Prefix each line of output with all of the above",                     nullptr},
862     {"no-glyph-names",  0, G_OPTION_FLAG_REVERSE,
863                               G_OPTION_ARG_NONE,        &this->show_glyph_names,        "Output glyph indices instead of names",                                nullptr},
864     {"no-positions",    0, G_OPTION_FLAG_REVERSE,
865                               G_OPTION_ARG_NONE,        &this->show_positions,          "Do not output glyph positions",                                        nullptr},
866     {"no-advances",     0, G_OPTION_FLAG_REVERSE,
867                               G_OPTION_ARG_NONE,        &this->show_advances,           "Do not output glyph advances",                                         nullptr},
868     {"no-clusters",     0, G_OPTION_FLAG_REVERSE,
869                               G_OPTION_ARG_NONE,        &this->show_clusters,           "Do not output cluster indices",                                        nullptr},
870     {"show-extents",    0, 0, G_OPTION_ARG_NONE,        &this->show_extents,            "Output glyph extents",                                                 nullptr},
871     {"show-flags",      0, 0, G_OPTION_ARG_NONE,        &this->show_flags,              "Output glyph flags",                                                   nullptr},
872     {"ned",           'v', G_OPTION_FLAG_NO_ARG,
873                               G_OPTION_ARG_CALLBACK,    (gpointer) &parse_ned,          "No Extra Data; Do not output clusters or advances",                    nullptr},
874     {"trace",         'V', 0, G_OPTION_ARG_NONE,        &this->trace,                   "Output interim shaping results",                                       nullptr},
875     {nullptr}
876   };
877   parser->add_group (entries,
878                      "output-syntax",
879                      "Output syntax:\n"
880          "    text: [<glyph name or index>=<glyph cluster index within input>@<horizontal displacement>,<vertical displacement>+<horizontal advance>,<vertical advance>|...]\n"
881          "    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"
882          "\nOutput syntax options:",
883                      "Options for the syntax of the output",
884                      this);
885 }
886
887 void
888 format_options_t::serialize_unicode (hb_buffer_t *buffer,
889                                      GString     *gs)
890 {
891   unsigned int num_glyphs = hb_buffer_get_length (buffer);
892   hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, nullptr);
893
894   g_string_append_c (gs, '<');
895   for (unsigned int i = 0; i < num_glyphs; i++)
896   {
897     if (i)
898       g_string_append_c (gs, ',');
899     g_string_append_printf (gs, "U+%04X", info->codepoint);
900     info++;
901   }
902   g_string_append_c (gs, '>');
903 }
904
905 void
906 format_options_t::serialize_glyphs (hb_buffer_t *buffer,
907                                     hb_font_t   *font,
908                                     hb_buffer_serialize_format_t output_format,
909                                     hb_buffer_serialize_flags_t flags,
910                                     GString     *gs)
911 {
912   g_string_append_c (gs, '[');
913   unsigned int num_glyphs = hb_buffer_get_length (buffer);
914   unsigned int start = 0;
915
916   while (start < num_glyphs)
917   {
918     char buf[1024];
919     unsigned int consumed;
920     start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs,
921                                          buf, sizeof (buf), &consumed,
922                                          font, output_format, flags);
923     if (!consumed)
924       break;
925     g_string_append (gs, buf);
926   }
927   g_string_append_c (gs, ']');
928 }
929 void
930 format_options_t::serialize_line_no (unsigned int  line_no,
931                                      GString      *gs)
932 {
933   if (show_line_num)
934     g_string_append_printf (gs, "%d: ", line_no);
935 }
936 void
937 format_options_t::serialize_buffer_of_text (hb_buffer_t  *buffer,
938                                             unsigned int  line_no,
939                                             const char   *text,
940                                             unsigned int  text_len,
941                                             hb_font_t    *font,
942                                             GString      *gs)
943 {
944   if (show_text)
945   {
946     serialize_line_no (line_no, gs);
947     g_string_append_c (gs, '(');
948     g_string_append_len (gs, text, text_len);
949     g_string_append_c (gs, ')');
950     g_string_append_c (gs, '\n');
951   }
952
953   if (show_unicode)
954   {
955     serialize_line_no (line_no, gs);
956     serialize_unicode (buffer, gs);
957     g_string_append_c (gs, '\n');
958   }
959 }
960 void
961 format_options_t::serialize_message (unsigned int  line_no,
962                                      const char   *type,
963                                      const char   *msg,
964                                      GString      *gs)
965 {
966   serialize_line_no (line_no, gs);
967   g_string_append_printf (gs, "%s: %s", type, msg);
968   g_string_append_c (gs, '\n');
969 }
970 void
971 format_options_t::serialize_buffer_of_glyphs (hb_buffer_t  *buffer,
972                                               unsigned int  line_no,
973                                               const char   *text,
974                                               unsigned int  text_len,
975                                               hb_font_t    *font,
976                                               hb_buffer_serialize_format_t output_format,
977                                               hb_buffer_serialize_flags_t format_flags,
978                                               GString      *gs)
979 {
980   serialize_line_no (line_no, gs);
981   serialize_glyphs (buffer, font, output_format, format_flags, gs);
982   g_string_append_c (gs, '\n');
983 }