Home | History | Annotate | Download | only in util
      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 #ifdef HAVE_OT
     33 #include <hb-ot.h>
     34 #endif
     35 
     36 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 #ifdef HAVE_OT
     45   {"ot",	hb_ot_font_set_funcs},
     46 #endif
     47 };
     48 
     49 
     50 void
     51 fail (hb_bool_t suggest_help, const char *format, ...)
     52 {
     53   const char *msg;
     54 
     55   va_list vap;
     56   va_start (vap, format);
     57   msg = g_strdup_vprintf (format, vap);
     58   va_end (vap);
     59   const char *prgname = g_get_prgname ();
     60   g_printerr ("%s: %s\n", prgname, msg);
     61   if (suggest_help)
     62     g_printerr ("Try `%s --help' for more information.\n", prgname);
     63 
     64   exit (1);
     65 }
     66 
     67 
     68 hb_bool_t debug = false;
     69 
     70 static gchar *
     71 shapers_to_string (void)
     72 {
     73   GString *shapers = g_string_new (NULL);
     74   const char **shaper_list = hb_shape_list_shapers ();
     75 
     76   for (; *shaper_list; shaper_list++) {
     77     g_string_append (shapers, *shaper_list);
     78     g_string_append_c (shapers, ',');
     79   }
     80   g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
     81 
     82   return g_string_free (shapers, false);
     83 }
     84 
     85 static G_GNUC_NORETURN gboolean
     86 show_version (const char *name G_GNUC_UNUSED,
     87 	      const char *arg G_GNUC_UNUSED,
     88 	      gpointer    data G_GNUC_UNUSED,
     89 	      GError    **error G_GNUC_UNUSED)
     90 {
     91   g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
     92 
     93   char *shapers = shapers_to_string ();
     94   g_printf ("Available shapers: %s\n", shapers);
     95   g_free (shapers);
     96   if (strcmp (HB_VERSION_STRING, hb_version_string ()))
     97     g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
     98 
     99   exit(0);
    100 }
    101 
    102 
    103 void
    104 option_parser_t::add_main_options (void)
    105 {
    106   GOptionEntry entries[] =
    107   {
    108     {"version",		0, G_OPTION_FLAG_NO_ARG,
    109 			      G_OPTION_ARG_CALLBACK,	(gpointer) &show_version,	"Show version numbers",			NULL},
    110     {"debug",		0, 0, G_OPTION_ARG_NONE,	&debug,				"Free all resources before exit",	NULL},
    111     {NULL}
    112   };
    113   g_option_context_add_main_entries (context, entries, NULL);
    114 }
    115 
    116 static gboolean
    117 pre_parse (GOptionContext *context G_GNUC_UNUSED,
    118 	   GOptionGroup *group G_GNUC_UNUSED,
    119 	   gpointer data,
    120 	   GError **error)
    121 {
    122   option_group_t *option_group = (option_group_t *) data;
    123   option_group->pre_parse (error);
    124   return *error == NULL;
    125 }
    126 
    127 static gboolean
    128 post_parse (GOptionContext *context G_GNUC_UNUSED,
    129 	    GOptionGroup *group G_GNUC_UNUSED,
    130 	    gpointer data,
    131 	    GError **error)
    132 {
    133   option_group_t *option_group = static_cast<option_group_t *>(data);
    134   option_group->post_parse (error);
    135   return *error == NULL;
    136 }
    137 
    138 void
    139 option_parser_t::add_group (GOptionEntry   *entries,
    140 			    const gchar    *name,
    141 			    const gchar    *description,
    142 			    const gchar    *help_description,
    143 			    option_group_t *option_group)
    144 {
    145   GOptionGroup *group = g_option_group_new (name, description, help_description,
    146 					    static_cast<gpointer>(option_group), NULL);
    147   g_option_group_add_entries (group, entries);
    148   g_option_group_set_parse_hooks (group, pre_parse, post_parse);
    149   g_option_context_add_group (context, group);
    150 }
    151 
    152 void
    153 option_parser_t::parse (int *argc, char ***argv)
    154 {
    155   setlocale (LC_ALL, "");
    156 
    157   GError *parse_error = NULL;
    158   if (!g_option_context_parse (context, argc, argv, &parse_error))
    159   {
    160     if (parse_error != NULL) {
    161       fail (true, "%s", parse_error->message);
    162       //g_error_free (parse_error);
    163     } else
    164       fail (true, "Option parse error");
    165   }
    166 }
    167 
    168 
    169 static gboolean
    170 parse_margin (const char *name G_GNUC_UNUSED,
    171 	      const char *arg,
    172 	      gpointer    data,
    173 	      GError    **error G_GNUC_UNUSED)
    174 {
    175   view_options_t *view_opts = (view_options_t *) data;
    176   view_options_t::margin_t &m = view_opts->margin;
    177   switch (sscanf (arg, "%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf", &m.t, &m.r, &m.b, &m.l)) {
    178     case 1: m.r = m.t;
    179     case 2: m.b = m.t;
    180     case 3: m.l = m.r;
    181     case 4: return true;
    182     default:
    183       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
    184 		   "%s argument should be one to four space-separated numbers",
    185 		   name);
    186       return false;
    187   }
    188 }
    189 
    190 
    191 static gboolean
    192 parse_shapers (const char *name G_GNUC_UNUSED,
    193 	       const char *arg,
    194 	       gpointer    data,
    195 	       GError    **error G_GNUC_UNUSED)
    196 {
    197   shape_options_t *shape_opts = (shape_options_t *) data;
    198   g_strfreev (shape_opts->shapers);
    199   shape_opts->shapers = g_strsplit (arg, ",", 0);
    200   return true;
    201 }
    202 
    203 static G_GNUC_NORETURN gboolean
    204 list_shapers (const char *name G_GNUC_UNUSED,
    205 	      const char *arg G_GNUC_UNUSED,
    206 	      gpointer    data G_GNUC_UNUSED,
    207 	      GError    **error G_GNUC_UNUSED)
    208 {
    209   for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++)
    210     g_printf ("%s\n", *shaper);
    211 
    212   exit(0);
    213 }
    214 
    215 
    216 static gboolean
    217 parse_features (const char *name G_GNUC_UNUSED,
    218 	        const char *arg,
    219 	        gpointer    data,
    220 	        GError    **error G_GNUC_UNUSED)
    221 {
    222   shape_options_t *shape_opts = (shape_options_t *) data;
    223   char *s = (char *) arg;
    224   char *p;
    225 
    226   shape_opts->num_features = 0;
    227   g_free (shape_opts->features);
    228   shape_opts->features = NULL;
    229 
    230   if (!*s)
    231     return true;
    232 
    233   /* count the features first, so we can allocate memory */
    234   p = s;
    235   do {
    236     shape_opts->num_features++;
    237     p = strchr (p, ',');
    238     if (p)
    239       p++;
    240   } while (p);
    241 
    242   shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
    243 
    244   /* now do the actual parsing */
    245   p = s;
    246   shape_opts->num_features = 0;
    247   while (p && *p) {
    248     char *end = strchr (p, ',');
    249     if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features]))
    250       shape_opts->num_features++;
    251     p = end ? end + 1 : NULL;
    252   }
    253 
    254   return true;
    255 }
    256 
    257 static gboolean
    258 parse_variations (const char *name G_GNUC_UNUSED,
    259 	        const char *arg,
    260 	        gpointer    data,
    261 	        GError    **error G_GNUC_UNUSED)
    262 {
    263   font_options_t *font_opts = (font_options_t *) data;
    264   char *s = (char *) arg;
    265   char *p;
    266 
    267   font_opts->num_variations = 0;
    268   g_free (font_opts->variations);
    269   font_opts->variations = NULL;
    270 
    271   if (!*s)
    272     return true;
    273 
    274   /* count the variations first, so we can allocate memory */
    275   p = s;
    276   do {
    277     font_opts->num_variations++;
    278     p = strchr (p, ',');
    279     if (p)
    280       p++;
    281   } while (p);
    282 
    283   font_opts->variations = (hb_variation_t *) calloc (font_opts->num_variations, sizeof (*font_opts->variations));
    284 
    285   /* now do the actual parsing */
    286   p = s;
    287   font_opts->num_variations = 0;
    288   while (p && *p) {
    289     char *end = strchr (p, ',');
    290     if (hb_variation_from_string (p, end ? end - p : -1, &font_opts->variations[font_opts->num_variations]))
    291       font_opts->num_variations++;
    292     p = end ? end + 1 : NULL;
    293   }
    294 
    295   return true;
    296 }
    297 
    298 
    299 void
    300 view_options_t::add_options (option_parser_t *parser)
    301 {
    302   GOptionEntry entries[] =
    303   {
    304     {"annotate",	0, 0, G_OPTION_ARG_NONE,	&this->annotate,		"Annotate output rendering",				NULL},
    305     {"background",	0, 0, G_OPTION_ARG_STRING,	&this->back,			"Set background color (default: " DEFAULT_BACK ")",	"rrggbb/rrggbbaa"},
    306     {"foreground",	0, 0, G_OPTION_ARG_STRING,	&this->fore,			"Set foreground color (default: " DEFAULT_FORE ")",	"rrggbb/rrggbbaa"},
    307     {"line-space",	0, 0, G_OPTION_ARG_DOUBLE,	&this->line_space,		"Set space between lines (default: 0)",			"units"},
    308     {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
    309     {NULL}
    310   };
    311   parser->add_group (entries,
    312 		     "view",
    313 		     "View options:",
    314 		     "Options for output rendering",
    315 		     this);
    316 }
    317 
    318 void
    319 shape_options_t::add_options (option_parser_t *parser)
    320 {
    321   GOptionEntry entries[] =
    322   {
    323     {"list-shapers",	0, G_OPTION_FLAG_NO_ARG,
    324 			      G_OPTION_ARG_CALLBACK,	(gpointer) &list_shapers,	"List available shapers and quit",	NULL},
    325     {"shaper",		0, G_OPTION_FLAG_HIDDEN,
    326 			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Hidden duplicate of --shapers",	NULL},
    327     {"shapers",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Set comma-separated list of shapers to try","list"},
    328     {"direction",	0, 0, G_OPTION_ARG_STRING,	&this->direction,		"Set text direction (default: auto)",	"ltr/rtl/ttb/btt"},
    329     {"language",	0, 0, G_OPTION_ARG_STRING,	&this->language,		"Set text language (default: $LANG)",	"langstr"},
    330     {"script",		0, 0, G_OPTION_ARG_STRING,	&this->script,			"Set text script (default: auto)",	"ISO-15924 tag"},
    331     {"bot",		0, 0, G_OPTION_ARG_NONE,	&this->bot,			"Treat text as beginning-of-paragraph",	NULL},
    332     {"eot",		0, 0, G_OPTION_ARG_NONE,	&this->eot,			"Treat text as end-of-paragraph",	NULL},
    333     {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->preserve_default_ignorables,	"Preserve Default-Ignorable characters",	NULL},
    334     {"utf8-clusters",	0, 0, G_OPTION_ARG_NONE,	&this->utf8_clusters,		"Use UTF8 byte indices, not char indices",	NULL},
    335     {"cluster-level",	0, 0, G_OPTION_ARG_INT,		&this->cluster_level,		"Cluster merging level (default: 0)",	"0/1/2"},
    336     {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE,	&this->normalize_glyphs,	"Rearrange glyph clusters in nominal order",	NULL},
    337     {"num-iterations",	0, 0, G_OPTION_ARG_INT,		&this->num_iterations,		"Run shaper N times (default: 1)",	"N"},
    338     {NULL}
    339   };
    340   parser->add_group (entries,
    341 		     "shape",
    342 		     "Shape options:",
    343 		     "Options for the shaping process",
    344 		     this);
    345 
    346   const gchar *features_help = "Comma-separated list of font features\n"
    347     "\n"
    348     "    Features can be enabled or disabled, either globally or limited to\n"
    349     "    specific character ranges.  The format for specifying feature settings\n"
    350     "    follows.  All valid CSS font-feature-settings values other than 'normal'\n"
    351     "    and 'inherited' are also accepted, though, not documented below.\n"
    352     "\n"
    353     "    The range indices refer to the positions between Unicode characters,\n"
    354     "    unless the --utf8-clusters is provided, in which case range indices\n"
    355     "    refer to UTF-8 byte indices. The position before the first character\n"
    356     "    is always 0.\n"
    357     "\n"
    358     "    The format is Python-esque.  Here is how it all works:\n"
    359     "\n"
    360     "      Syntax:       Value:    Start:    End:\n"
    361     "\n"
    362     "    Setting value:\n"
    363     "      \"kern\"        1         0                  # Turn feature on\n"
    364     "      \"+kern\"       1         0                  # Turn feature on\n"
    365     "      \"-kern\"       0         0                  # Turn feature off\n"
    366     "      \"kern=0\"      0         0                  # Turn feature off\n"
    367     "      \"kern=1\"      1         0                  # Turn feature on\n"
    368     "      \"aalt=2\"      2         0                  # Choose 2nd alternate\n"
    369     "\n"
    370     "    Setting index:\n"
    371     "      \"kern[]\"      1         0                  # Turn feature on\n"
    372     "      \"kern[:]\"     1         0                  # Turn feature on\n"
    373     "      \"kern[5:]\"    1         5                  # Turn feature on, partial\n"
    374     "      \"kern[:5]\"    1         0         5         # Turn feature on, partial\n"
    375     "      \"kern[3:5]\"   1         3         5         # Turn feature on, range\n"
    376     "      \"kern[3]\"     1         3         3+1       # Turn feature on, single char\n"
    377     "\n"
    378     "    Mixing it all:\n"
    379     "\n"
    380     "      \"aalt[3:5]=2\" 2         3         5         # Turn 2nd alternate on for range";
    381 
    382   GOptionEntry entries2[] =
    383   {
    384     {"features",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_features,	features_help,	"list"},
    385     {NULL}
    386   };
    387   parser->add_group (entries2,
    388 		     "features",
    389 		     "Features options:",
    390 		     "Options for font features used",
    391 		     this);
    392 }
    393 
    394 static gboolean
    395 parse_font_size (const char *name G_GNUC_UNUSED,
    396 		 const char *arg,
    397 		 gpointer    data,
    398 		 GError    **error G_GNUC_UNUSED)
    399 {
    400   font_options_t *font_opts = (font_options_t *) data;
    401   if (0 == strcmp (arg, "upem"))
    402   {
    403     font_opts->font_size_y = font_opts->font_size_x = FONT_SIZE_UPEM;
    404     return true;
    405   }
    406   switch (sscanf (arg, "%lf%*[ ,]%lf", &font_opts->font_size_x, &font_opts->font_size_y)) {
    407     case 1: font_opts->font_size_y = font_opts->font_size_x;
    408     case 2: return true;
    409     default:
    410       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
    411 		   "%s argument should be one to four space-separated numbers",
    412 		   name);
    413       return false;
    414   }
    415 }
    416 void
    417 font_options_t::add_options (option_parser_t *parser)
    418 {
    419   char *text = NULL;
    420 
    421   {
    422     ASSERT_STATIC (ARRAY_LENGTH_CONST (supported_font_funcs) > 0);
    423     GString *s = g_string_new (NULL);
    424     g_string_printf (s, "Set font functions implementation to use (default: %s)\n\n    Supported font function implementations are: %s",
    425 		     supported_font_funcs[0].name,
    426 		     supported_font_funcs[0].name);
    427     for (unsigned int i = 1; i < ARRAY_LENGTH (supported_font_funcs); i++)
    428     {
    429       g_string_append_c (s, '/');
    430       g_string_append (s, supported_font_funcs[i].name);
    431     }
    432     text = g_string_free (s, FALSE);
    433     parser->free_later (text);
    434   }
    435 
    436   char *font_size_text;
    437   if (default_font_size == FONT_SIZE_UPEM)
    438     font_size_text = (char *) "Font size (default: upem)";
    439   else
    440   {
    441     font_size_text = g_strdup_printf ("Font size (default: %d)", default_font_size);
    442     parser->free_later (font_size_text);
    443   }
    444 
    445   GOptionEntry entries[] =
    446   {
    447     {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Set font file-name",			"filename"},
    448     {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Set face index (default: 0)",		"index"},
    449     {"font-size",	0, default_font_size ? 0 : G_OPTION_FLAG_HIDDEN,
    450 			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_font_size,	font_size_text,				"1/2 numbers or 'upem'"},
    451     {"font-funcs",	0, 0, G_OPTION_ARG_STRING,	&this->font_funcs,		text,					"impl"},
    452     {NULL}
    453   };
    454   parser->add_group (entries,
    455 		     "font",
    456 		     "Font options:",
    457 		     "Options for the font",
    458 		     this);
    459 
    460   const gchar *variations_help = "Comma-separated list of font variations\n"
    461     "\n"
    462     "    Variations are set globally. The format for specifying variation settings\n"
    463     "    follows.  All valid CSS font-variation-settings values other than 'normal'\n"
    464     "    and 'inherited' are also accepted, though, not documented below.\n"
    465     "\n"
    466     "    The format is a tag, optionally followed by an equals sign, followed by a\n"
    467     "    number. For example:\n"
    468     "\n"
    469     "      \"wght=500\"\n"
    470     "      \"slnt=-7.5\"\n";
    471 
    472   GOptionEntry entries2[] =
    473   {
    474     {"variations",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_variations,	variations_help,	"list"},
    475     {NULL}
    476   };
    477   parser->add_group (entries2,
    478 		     "variations",
    479 		     "Varitions options:",
    480 		     "Options for font variations used",
    481 		     this);
    482 }
    483 
    484 void
    485 text_options_t::add_options (option_parser_t *parser)
    486 {
    487   GOptionEntry entries[] =
    488   {
    489     {"text",		0, 0, G_OPTION_ARG_STRING,	&this->text,			"Set input text",			"string"},
    490     {"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"},
    491     {"text-before",	0, 0, G_OPTION_ARG_STRING,	&this->text_before,		"Set text context before each line",	"string"},
    492     {"text-after",	0, 0, G_OPTION_ARG_STRING,	&this->text_after,		"Set text context after each line",	"string"},
    493     {NULL}
    494   };
    495   parser->add_group (entries,
    496 		     "text",
    497 		     "Text options:",
    498 		     "Options for the input text",
    499 		     this);
    500 }
    501 
    502 void
    503 output_options_t::add_options (option_parser_t *parser)
    504 {
    505   const char *text;
    506 
    507   if (NULL == supported_formats)
    508     text = "Set output serialization format";
    509   else
    510   {
    511     char *items = g_strjoinv ("/", const_cast<char **> (supported_formats));
    512     text = g_strdup_printf ("Set output format\n\n    Supported output formats are: %s", items);
    513     g_free (items);
    514     parser->free_later ((char *) text);
    515   }
    516 
    517   GOptionEntry entries[] =
    518   {
    519     {"output-file",	0, 0, G_OPTION_ARG_STRING,	&this->output_file,		"Set output file-name (default: stdout)","filename"},
    520     {"output-format",	0, 0, G_OPTION_ARG_STRING,	&this->output_format,		text,					"format"},
    521     {NULL}
    522   };
    523   parser->add_group (entries,
    524 		     "output",
    525 		     "Output destination & format options:",
    526 		     "Options for the destination & form of the output",
    527 		     this);
    528 }
    529 
    530 
    531 
    532 hb_font_t *
    533 font_options_t::get_font (void) const
    534 {
    535   if (font)
    536     return font;
    537 
    538   hb_blob_t *blob = NULL;
    539 
    540   /* Create the blob */
    541   {
    542     char *font_data;
    543     unsigned int len = 0;
    544     hb_destroy_func_t destroy;
    545     void *user_data;
    546     hb_memory_mode_t mm;
    547 
    548     /* This is a hell of a lot of code for just reading a file! */
    549     if (!font_file)
    550       fail (true, "No font file set");
    551 
    552     if (0 == strcmp (font_file, "-")) {
    553       /* read it */
    554       GString *gs = g_string_new (NULL);
    555       char buf[BUFSIZ];
    556 #if defined(_WIN32) || defined(__CYGWIN__)
    557       setmode (fileno (stdin), O_BINARY);
    558 #endif
    559       while (!feof (stdin)) {
    560 	size_t ret = fread (buf, 1, sizeof (buf), stdin);
    561 	if (ferror (stdin))
    562 	  fail (false, "Failed reading font from standard input: %s",
    563 		strerror (errno));
    564 	g_string_append_len (gs, buf, ret);
    565       }
    566       len = gs->len;
    567       font_data = g_string_free (gs, false);
    568       user_data = font_data;
    569       destroy = (hb_destroy_func_t) g_free;
    570       mm = HB_MEMORY_MODE_WRITABLE;
    571     } else {
    572       GError *error = NULL;
    573       GMappedFile *mf = g_mapped_file_new (font_file, false, &error);
    574       if (mf) {
    575 	font_data = g_mapped_file_get_contents (mf);
    576 	len = g_mapped_file_get_length (mf);
    577 	if (len) {
    578 	  destroy = (hb_destroy_func_t) g_mapped_file_unref;
    579 	  user_data = (void *) mf;
    580 	  mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
    581 	} else
    582 	  g_mapped_file_unref (mf);
    583       } else {
    584 	fail (false, "%s", error->message);
    585 	//g_error_free (error);
    586       }
    587       if (!len) {
    588 	/* GMappedFile is buggy, it doesn't fail if file isn't regular.
    589 	 * Try reading.
    590 	 * https://bugzilla.gnome.org/show_bug.cgi?id=659212 */
    591         GError *error = NULL;
    592 	gsize l;
    593 	if (g_file_get_contents (font_file, &font_data, &l, &error)) {
    594 	  len = l;
    595 	  destroy = (hb_destroy_func_t) g_free;
    596 	  user_data = (void *) font_data;
    597 	  mm = HB_MEMORY_MODE_WRITABLE;
    598 	} else {
    599 	  fail (false, "%s", error->message);
    600 	  //g_error_free (error);
    601 	}
    602       }
    603     }
    604 
    605     if (debug)
    606       mm = HB_MEMORY_MODE_DUPLICATE;
    607 
    608     blob = hb_blob_create (font_data, len, mm, user_data, destroy);
    609   }
    610 
    611   /* Create the face */
    612   hb_face_t *face = hb_face_create (blob, face_index);
    613   hb_blob_destroy (blob);
    614 
    615 
    616   font = hb_font_create (face);
    617 
    618   if (font_size_x == FONT_SIZE_UPEM)
    619     font_size_x = hb_face_get_upem (face);
    620   if (font_size_y == FONT_SIZE_UPEM)
    621     font_size_y = hb_face_get_upem (face);
    622 
    623   int scale_x = (int) scalbnf (font_size_x, subpixel_bits);
    624   int scale_y = (int) scalbnf (font_size_y, subpixel_bits);
    625   hb_font_set_scale (font, scale_x, scale_y);
    626   hb_face_destroy (face);
    627 
    628   hb_font_set_variations (font, variations, num_variations);
    629 
    630   void (*set_font_funcs) (hb_font_t *) = NULL;
    631   if (!font_funcs)
    632   {
    633     set_font_funcs = supported_font_funcs[0].func;
    634   }
    635   else
    636   {
    637     for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
    638       if (0 == g_ascii_strcasecmp (font_funcs, supported_font_funcs[i].name))
    639       {
    640 	set_font_funcs = supported_font_funcs[i].func;
    641 	break;
    642       }
    643     if (!set_font_funcs)
    644     {
    645       GString *s = g_string_new (NULL);
    646       for (unsigned int i = 0; i < ARRAY_LENGTH (supported_font_funcs); i++)
    647       {
    648         if (i)
    649 	  g_string_append_c (s, '/');
    650 	g_string_append (s, supported_font_funcs[i].name);
    651       }
    652       char *p = g_string_free (s, FALSE);
    653       fail (false, "Unknown font function implementation `%s'; supported values are: %s; default is %s",
    654 	    font_funcs,
    655 	    p,
    656 	    supported_font_funcs[0].name);
    657       //free (p);
    658     }
    659   }
    660   set_font_funcs (font);
    661 
    662   return font;
    663 }
    664 
    665 
    666 const char *
    667 text_options_t::get_line (unsigned int *len)
    668 {
    669   if (text) {
    670     if (!line) line = text;
    671     if (line_len == (unsigned int) -1)
    672       line_len = strlen (line);
    673 
    674     if (!line_len) {
    675       *len = 0;
    676       return NULL;
    677     }
    678 
    679     const char *ret = line;
    680     const char *p = (const char *) memchr (line, '\n', line_len);
    681     unsigned int ret_len;
    682     if (!p) {
    683       ret_len = line_len;
    684       line += ret_len;
    685       line_len = 0;
    686     } else {
    687       ret_len = p - ret;
    688       line += ret_len + 1;
    689       line_len -= ret_len + 1;
    690     }
    691 
    692     *len = ret_len;
    693     return ret;
    694   }
    695 
    696   if (!fp) {
    697     if (!text_file)
    698       fail (true, "At least one of text or text-file must be set");
    699 
    700     if (0 != strcmp (text_file, "-"))
    701       fp = fopen (text_file, "r");
    702     else
    703       fp = stdin;
    704 
    705     if (!fp)
    706       fail (false, "Failed opening text file `%s': %s",
    707 	    text_file, strerror (errno));
    708 
    709     gs = g_string_new (NULL);
    710   }
    711 
    712   g_string_set_size (gs, 0);
    713   char buf[BUFSIZ];
    714   while (fgets (buf, sizeof (buf), fp)) {
    715     unsigned int bytes = strlen (buf);
    716     if (bytes && buf[bytes - 1] == '\n') {
    717       bytes--;
    718       g_string_append_len (gs, buf, bytes);
    719       break;
    720     }
    721       g_string_append_len (gs, buf, bytes);
    722   }
    723   if (ferror (fp))
    724     fail (false, "Failed reading text: %s",
    725 	  strerror (errno));
    726   *len = gs->len;
    727   return !*len && feof (fp) ? NULL : gs->str;
    728 }
    729 
    730 
    731 FILE *
    732 output_options_t::get_file_handle (void)
    733 {
    734   if (fp)
    735     return fp;
    736 
    737   if (output_file)
    738     fp = fopen (output_file, "wb");
    739   else {
    740 #if defined(_WIN32) || defined(__CYGWIN__)
    741     setmode (fileno (stdout), O_BINARY);
    742 #endif
    743     fp = stdout;
    744   }
    745   if (!fp)
    746     fail (false, "Cannot open output file `%s': %s",
    747 	  g_filename_display_name (output_file), strerror (errno));
    748 
    749   return fp;
    750 }
    751 
    752 static gboolean
    753 parse_verbose (const char *name G_GNUC_UNUSED,
    754 	       const char *arg G_GNUC_UNUSED,
    755 	       gpointer    data G_GNUC_UNUSED,
    756 	       GError    **error G_GNUC_UNUSED)
    757 {
    758   format_options_t *format_opts = (format_options_t *) data;
    759   format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true;
    760   return true;
    761 }
    762 
    763 void
    764 format_options_t::add_options (option_parser_t *parser)
    765 {
    766   GOptionEntry entries[] =
    767   {
    768     {"show-text",	0, 0, G_OPTION_ARG_NONE,	&this->show_text,		"Prefix each line of output with its corresponding input text",		NULL},
    769     {"show-unicode",	0, 0, G_OPTION_ARG_NONE,	&this->show_unicode,		"Prefix each line of output with its corresponding input codepoint(s)",	NULL},
    770     {"show-line-num",	0, 0, G_OPTION_ARG_NONE,	&this->show_line_num,		"Prefix each line of output with its corresponding input line number",	NULL},
    771     {"verbose",		0, G_OPTION_FLAG_NO_ARG,
    772 			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_verbose,	"Prefix each line of output with all of the above",			NULL},
    773     {"no-glyph-names",	0, G_OPTION_FLAG_REVERSE,
    774 			      G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Output glyph indices instead of names",				NULL},
    775     {"no-positions",	0, G_OPTION_FLAG_REVERSE,
    776 			      G_OPTION_ARG_NONE,	&this->show_positions,		"Do not output glyph positions",					NULL},
    777     {"no-clusters",	0, G_OPTION_FLAG_REVERSE,
    778 			      G_OPTION_ARG_NONE,	&this->show_clusters,		"Do not output cluster indices",					NULL},
    779     {"show-extents",	0, 0, G_OPTION_ARG_NONE,	&this->show_extents,		"Output glyph extents",							NULL},
    780     {NULL}
    781   };
    782   parser->add_group (entries,
    783 		     "output-syntax",
    784 		     "Output syntax:\n"
    785          "    text: [<glyph name or index>=<glyph cluster index within input>@<horizontal displacement>,<vertical displacement>+<horizontal advance>,<vertical advance>|...]\n"
    786          "    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"
    787          "\nOutput syntax options:",
    788 		     "Options for the syntax of the output",
    789 		     this);
    790 }
    791 
    792 void
    793 format_options_t::serialize_unicode (hb_buffer_t *buffer,
    794 				     GString     *gs)
    795 {
    796   unsigned int num_glyphs = hb_buffer_get_length (buffer);
    797   hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
    798 
    799   g_string_append_c (gs, '<');
    800   for (unsigned int i = 0; i < num_glyphs; i++)
    801   {
    802     if (i)
    803       g_string_append_c (gs, ',');
    804     g_string_append_printf (gs, "U+%04X", info->codepoint);
    805     info++;
    806   }
    807   g_string_append_c (gs, '>');
    808 }
    809 
    810 void
    811 format_options_t::serialize_glyphs (hb_buffer_t *buffer,
    812 				    hb_font_t   *font,
    813 				    hb_buffer_serialize_format_t output_format,
    814 				    hb_buffer_serialize_flags_t flags,
    815 				    GString     *gs)
    816 {
    817   g_string_append_c (gs, '[');
    818   unsigned int num_glyphs = hb_buffer_get_length (buffer);
    819   unsigned int start = 0;
    820 
    821   while (start < num_glyphs) {
    822     char buf[1024];
    823     unsigned int consumed;
    824     start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs,
    825 					 buf, sizeof (buf), &consumed,
    826 					 font, output_format, flags);
    827     if (!consumed)
    828       break;
    829     g_string_append (gs, buf);
    830   }
    831   g_string_append_c (gs, ']');
    832 }
    833 void
    834 format_options_t::serialize_line_no (unsigned int  line_no,
    835 				     GString      *gs)
    836 {
    837   if (show_line_num)
    838     g_string_append_printf (gs, "%d: ", line_no);
    839 }
    840 void
    841 format_options_t::serialize_buffer_of_text (hb_buffer_t  *buffer,
    842 					    unsigned int  line_no,
    843 					    const char   *text,
    844 					    unsigned int  text_len,
    845 					    hb_font_t    *font,
    846 					    GString      *gs)
    847 {
    848   if (show_text) {
    849     serialize_line_no (line_no, gs);
    850     g_string_append_c (gs, '(');
    851     g_string_append_len (gs, text, text_len);
    852     g_string_append_c (gs, ')');
    853     g_string_append_c (gs, '\n');
    854   }
    855 
    856   if (show_unicode) {
    857     serialize_line_no (line_no, gs);
    858     serialize_unicode (buffer, gs);
    859     g_string_append_c (gs, '\n');
    860   }
    861 }
    862 void
    863 format_options_t::serialize_message (unsigned int  line_no,
    864 				     const char   *msg,
    865 				     GString      *gs)
    866 {
    867   serialize_line_no (line_no, gs);
    868   g_string_append_printf (gs, "%s", msg);
    869   g_string_append_c (gs, '\n');
    870 }
    871 void
    872 format_options_t::serialize_buffer_of_glyphs (hb_buffer_t  *buffer,
    873 					      unsigned int  line_no,
    874 					      const char   *text,
    875 					      unsigned int  text_len,
    876 					      hb_font_t    *font,
    877 					      hb_buffer_serialize_format_t output_format,
    878 					      hb_buffer_serialize_flags_t format_flags,
    879 					      GString      *gs)
    880 {
    881   serialize_line_no (line_no, gs);
    882   serialize_glyphs (buffer, font, output_format, format_flags, gs);
    883   g_string_append_c (gs, '\n');
    884 }
    885