• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 
34 void
fail(hb_bool_t suggest_help,const char * format,...)35 fail (hb_bool_t suggest_help, const char *format, ...)
36 {
37   const char *msg;
38 
39   va_list vap;
40   va_start (vap, format);
41   msg = g_strdup_vprintf (format, vap);
42   const char *prgname = g_get_prgname ();
43   g_printerr ("%s: %s\n", prgname, msg);
44   if (suggest_help)
45     g_printerr ("Try `%s --help' for more information.\n", prgname);
46 
47   exit (1);
48 }
49 
50 
51 hb_bool_t debug = false;
52 
53 static gchar *
shapers_to_string(void)54 shapers_to_string (void)
55 {
56   GString *shapers = g_string_new (NULL);
57   const char **shaper_list = hb_shape_list_shapers ();
58 
59   for (; *shaper_list; shaper_list++) {
60     g_string_append (shapers, *shaper_list);
61     g_string_append_c (shapers, ',');
62   }
63   g_string_truncate (shapers, MAX (0, (gint)shapers->len - 1));
64 
65   return g_string_free (shapers, false);
66 }
67 
68 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)69 show_version (const char *name G_GNUC_UNUSED,
70 	      const char *arg G_GNUC_UNUSED,
71 	      gpointer    data G_GNUC_UNUSED,
72 	      GError    **error G_GNUC_UNUSED)
73 {
74   g_printf ("%s (%s) %s\n", g_get_prgname (), PACKAGE_NAME, PACKAGE_VERSION);
75 
76   char *shapers = shapers_to_string ();
77   g_printf ("Available shapers: %s\n", shapers);
78   g_free (shapers);
79   if (strcmp (HB_VERSION_STRING, hb_version_string ()))
80     g_printf ("Linked HarfBuzz library has a different version: %s\n", hb_version_string ());
81 
82   exit(0);
83 }
84 
85 
86 void
add_main_options(void)87 option_parser_t::add_main_options (void)
88 {
89   GOptionEntry entries[] =
90   {
91     {"version",		0, G_OPTION_FLAG_NO_ARG,
92 			      G_OPTION_ARG_CALLBACK,	(gpointer) &show_version,	"Show version numbers",			NULL},
93     {"debug",		0, 0, G_OPTION_ARG_NONE,	&debug,				"Free all resources before exit",	NULL},
94     {NULL}
95   };
96   g_option_context_add_main_entries (context, entries, NULL);
97 }
98 
99 static gboolean
pre_parse(GOptionContext * context G_GNUC_UNUSED,GOptionGroup * group G_GNUC_UNUSED,gpointer data,GError ** error)100 pre_parse (GOptionContext *context G_GNUC_UNUSED,
101 	   GOptionGroup *group G_GNUC_UNUSED,
102 	   gpointer data,
103 	   GError **error)
104 {
105   option_group_t *option_group = (option_group_t *) data;
106   option_group->pre_parse (error);
107   return *error == NULL;
108 }
109 
110 static gboolean
post_parse(GOptionContext * context G_GNUC_UNUSED,GOptionGroup * group G_GNUC_UNUSED,gpointer data,GError ** error)111 post_parse (GOptionContext *context G_GNUC_UNUSED,
112 	    GOptionGroup *group G_GNUC_UNUSED,
113 	    gpointer data,
114 	    GError **error)
115 {
116   option_group_t *option_group = static_cast<option_group_t *>(data);
117   option_group->post_parse (error);
118   return *error == NULL;
119 }
120 
121 void
add_group(GOptionEntry * entries,const gchar * name,const gchar * description,const gchar * help_description,option_group_t * option_group)122 option_parser_t::add_group (GOptionEntry   *entries,
123 			    const gchar    *name,
124 			    const gchar    *description,
125 			    const gchar    *help_description,
126 			    option_group_t *option_group)
127 {
128   GOptionGroup *group = g_option_group_new (name, description, help_description,
129 					    static_cast<gpointer>(option_group), NULL);
130   g_option_group_add_entries (group, entries);
131   g_option_group_set_parse_hooks (group, pre_parse, post_parse);
132   g_option_context_add_group (context, group);
133 }
134 
135 void
parse(int * argc,char *** argv)136 option_parser_t::parse (int *argc, char ***argv)
137 {
138   setlocale (LC_ALL, "");
139 
140   GError *parse_error = NULL;
141   if (!g_option_context_parse (context, argc, argv, &parse_error))
142   {
143     if (parse_error != NULL) {
144       fail (true, "%s", parse_error->message);
145       //g_error_free (parse_error);
146     } else
147       fail (true, "Option parse error");
148   }
149 }
150 
151 
152 static gboolean
parse_margin(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)153 parse_margin (const char *name G_GNUC_UNUSED,
154 	      const char *arg,
155 	      gpointer    data,
156 	      GError    **error G_GNUC_UNUSED)
157 {
158   view_options_t *view_opts = (view_options_t *) data;
159   view_options_t::margin_t &m = view_opts->margin;
160   switch (sscanf (arg, "%lf %lf %lf %lf", &m.t, &m.r, &m.b, &m.l)) {
161     case 1: m.r = m.t;
162     case 2: m.b = m.t;
163     case 3: m.l = m.r;
164     case 4: return true;
165     default:
166       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
167 		   "%s argument should be one to four space-separated numbers",
168 		   name);
169       return false;
170   }
171 }
172 
173 
174 static gboolean
parse_shapers(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)175 parse_shapers (const char *name G_GNUC_UNUSED,
176 	       const char *arg,
177 	       gpointer    data,
178 	       GError    **error G_GNUC_UNUSED)
179 {
180   shape_options_t *shape_opts = (shape_options_t *) data;
181   g_strfreev (shape_opts->shapers);
182   shape_opts->shapers = g_strsplit (arg, ",", 0);
183   return true;
184 }
185 
186 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)187 list_shapers (const char *name G_GNUC_UNUSED,
188 	      const char *arg G_GNUC_UNUSED,
189 	      gpointer    data G_GNUC_UNUSED,
190 	      GError    **error G_GNUC_UNUSED)
191 {
192   for (const char **shaper = hb_shape_list_shapers (); *shaper; shaper++)
193     g_printf ("%s\n", *shaper);
194 
195   exit(0);
196 }
197 
198 
199 static gboolean
parse_features(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)200 parse_features (const char *name G_GNUC_UNUSED,
201 	        const char *arg,
202 	        gpointer    data,
203 	        GError    **error G_GNUC_UNUSED)
204 {
205   shape_options_t *shape_opts = (shape_options_t *) data;
206   char *s = (char *) arg;
207   char *p;
208 
209   shape_opts->num_features = 0;
210   g_free (shape_opts->features);
211   shape_opts->features = NULL;
212 
213   if (!*s)
214     return true;
215 
216   /* count the features first, so we can allocate memory */
217   p = s;
218   do {
219     shape_opts->num_features++;
220     p = strchr (p, ',');
221     if (p)
222       p++;
223   } while (p);
224 
225   shape_opts->features = (hb_feature_t *) calloc (shape_opts->num_features, sizeof (*shape_opts->features));
226 
227   /* now do the actual parsing */
228   p = s;
229   shape_opts->num_features = 0;
230   while (p && *p) {
231     char *end = strchr (p, ',');
232     if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features]))
233       shape_opts->num_features++;
234     p = end ? end + 1 : NULL;
235   }
236 
237   return true;
238 }
239 
240 
241 void
add_options(option_parser_t * parser)242 view_options_t::add_options (option_parser_t *parser)
243 {
244   GOptionEntry entries[] =
245   {
246     {"annotate",	0, 0, G_OPTION_ARG_NONE,	&this->annotate,		"Annotate output rendering",				NULL},
247     {"background",	0, 0, G_OPTION_ARG_STRING,	&this->back,			"Set background color (default: " DEFAULT_BACK ")",	"red/#rrggbb/#rrggbbaa"},
248     {"foreground",	0, 0, G_OPTION_ARG_STRING,	&this->fore,			"Set foreground color (default: " DEFAULT_FORE ")",	"red/#rrggbb/#rrggbbaa"},
249     {"line-space",	0, 0, G_OPTION_ARG_DOUBLE,	&this->line_space,		"Set space between lines (default: 0)",			"units"},
250     {"margin",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_margin,	"Margin around output (default: " G_STRINGIFY(DEFAULT_MARGIN) ")","one to four numbers"},
251     {"font-size",	0, 0, G_OPTION_ARG_DOUBLE,	&this->font_size,		"Font size (default: " G_STRINGIFY(DEFAULT_FONT_SIZE) ")","size"},
252     {NULL}
253   };
254   parser->add_group (entries,
255 		     "view",
256 		     "View options:",
257 		     "Options controlling output rendering",
258 		     this);
259 }
260 
261 void
add_options(option_parser_t * parser)262 shape_options_t::add_options (option_parser_t *parser)
263 {
264   GOptionEntry entries[] =
265   {
266     {"list-shapers",	0, G_OPTION_FLAG_NO_ARG,
267 			      G_OPTION_ARG_CALLBACK,	(gpointer) &list_shapers,	"List available shapers and quit",	NULL},
268     {"shaper",		0, G_OPTION_FLAG_HIDDEN,
269 			      G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Hidden duplicate of --shapers",	NULL},
270     {"shapers",		0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_shapers,	"Comma-separated list of shapers to try","list"},
271     {"direction",	0, 0, G_OPTION_ARG_STRING,	&this->direction,		"Set text direction (default: auto)",	"ltr/rtl/ttb/btt"},
272     {"language",	0, 0, G_OPTION_ARG_STRING,	&this->language,		"Set text language (default: $LANG)",	"langstr"},
273     {"script",		0, 0, G_OPTION_ARG_STRING,	&this->script,			"Set text script (default: auto)",	"ISO-15924 tag"},
274     {"bot",		0, 0, G_OPTION_ARG_NONE,	&this->bot,			"Treat text as beginning-of-paragraph",	NULL},
275     {"eot",		0, 0, G_OPTION_ARG_NONE,	&this->eot,			"Treat text as end-of-paragraph",	NULL},
276     {"preserve-default-ignorables",0, 0, G_OPTION_ARG_NONE,	&this->preserve_default_ignorables,	"Preserve Default-Ignorable characters",	NULL},
277     {"utf8-clusters",	0, 0, G_OPTION_ARG_NONE,	&this->utf8_clusters,		"Use UTF8 byte indices, not char indices",	NULL},
278     {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE,	&this->normalize_glyphs,	"Rearrange glyph clusters in nominal order",	NULL},
279     {NULL}
280   };
281   parser->add_group (entries,
282 		     "shape",
283 		     "Shape options:",
284 		     "Options controlling the shaping process",
285 		     this);
286 
287   const gchar *features_help = "Comma-separated list of font features\n"
288     "\n"
289     "    Features can be enabled or disabled, either globally or limited to\n"
290     "    specific character ranges.\n"
291     "\n"
292     "    The range indices refer to the positions between Unicode characters,\n"
293     "    unless the --utf8-clusters is provided, in which case range indices\n"
294     "    refer to UTF-8 byte indices. The position before the first character\n"
295     "    is always 0.\n"
296     "\n"
297     "    The format is Python-esque.  Here is how it all works:\n"
298     "\n"
299     "      Syntax:       Value:    Start:    End:\n"
300     "\n"
301     "    Setting value:\n"
302     "      \"kern\"        1         0         ∞         # Turn feature on\n"
303     "      \"+kern\"       1         0         ∞         # Turn feature on\n"
304     "      \"-kern\"       0         0         ∞         # Turn feature off\n"
305     "      \"kern=0\"      0         0         ∞         # Turn feature off\n"
306     "      \"kern=1\"      1         0         ∞         # Turn feature on\n"
307     "      \"aalt=2\"      2         0         ∞         # Choose 2nd alternate\n"
308     "\n"
309     "    Setting index:\n"
310     "      \"kern[]\"      1         0         ∞         # Turn feature on\n"
311     "      \"kern[:]\"     1         0         ∞         # Turn feature on\n"
312     "      \"kern[5:]\"    1         5         ∞         # Turn feature on, partial\n"
313     "      \"kern[:5]\"    1         0         5         # Turn feature on, partial\n"
314     "      \"kern[3:5]\"   1         3         5         # Turn feature on, range\n"
315     "      \"kern[3]\"     1         3         3+1       # Turn feature on, single char\n"
316     "\n"
317     "    Mixing it all:\n"
318     "\n"
319     "      \"aalt[3:5]=2\" 2         3         5         # Turn 2nd alternate on for range";
320 
321   GOptionEntry entries2[] =
322   {
323     {"features",	0, 0, G_OPTION_ARG_CALLBACK,	(gpointer) &parse_features,	features_help,	"list"},
324     {NULL}
325   };
326   parser->add_group (entries2,
327 		     "features",
328 		     "Features options:",
329 		     "Options controlling font features used",
330 		     this);
331 }
332 
333 void
add_options(option_parser_t * parser)334 font_options_t::add_options (option_parser_t *parser)
335 {
336   GOptionEntry entries[] =
337   {
338     {"font-file",	0, 0, G_OPTION_ARG_STRING,	&this->font_file,		"Font file-name",					"filename"},
339     {"face-index",	0, 0, G_OPTION_ARG_INT,		&this->face_index,		"Face index (default: 0)",                              "index"},
340     {NULL}
341   };
342   parser->add_group (entries,
343 		     "font",
344 		     "Font options:",
345 		     "Options controlling the font",
346 		     this);
347 }
348 
349 void
add_options(option_parser_t * parser)350 text_options_t::add_options (option_parser_t *parser)
351 {
352   GOptionEntry entries[] =
353   {
354     {"text",		0, 0, G_OPTION_ARG_STRING,	&this->text,			"Set input text",			"string"},
355     {"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"},
356     {"text-before",	0, 0, G_OPTION_ARG_STRING,	&this->text_before,		"Set text context before each line",	"string"},
357     {"text-after",	0, 0, G_OPTION_ARG_STRING,	&this->text_after,		"Set text context after each line",	"string"},
358     {NULL}
359   };
360   parser->add_group (entries,
361 		     "text",
362 		     "Text options:",
363 		     "Options controlling the input text",
364 		     this);
365 }
366 
367 void
add_options(option_parser_t * parser)368 output_options_t::add_options (option_parser_t *parser)
369 {
370   const char *text;
371 
372   if (NULL == supported_formats)
373     text = "Set output format";
374   else
375     text = g_strdup_printf ("Set output format\n\n    Supported formats are: %s", supported_formats);
376 
377   GOptionEntry entries[] =
378   {
379     {"output-file",	0, 0, G_OPTION_ARG_STRING,	&this->output_file,		"Set output file-name (default: stdout)","filename"},
380     {"output-format",	0, 0, G_OPTION_ARG_STRING,	&this->output_format,		text,					"format"},
381     {NULL}
382   };
383   parser->add_group (entries,
384 		     "output",
385 		     "Output options:",
386 		     "Options controlling the output",
387 		     this);
388 }
389 
390 
391 
392 hb_font_t *
get_font(void) const393 font_options_t::get_font (void) const
394 {
395   if (font)
396     return font;
397 
398   hb_blob_t *blob = NULL;
399 
400   /* Create the blob */
401   {
402     char *font_data;
403     unsigned int len = 0;
404     hb_destroy_func_t destroy;
405     void *user_data;
406     hb_memory_mode_t mm;
407 
408     /* This is a hell of a lot of code for just reading a file! */
409     if (!font_file)
410       fail (true, "No font file set");
411 
412     if (0 == strcmp (font_file, "-")) {
413       /* read it */
414       GString *gs = g_string_new (NULL);
415       char buf[BUFSIZ];
416 #if defined(_WIN32) || defined(__CYGWIN__)
417       setmode (fileno (stdin), _O_BINARY);
418 #endif
419       while (!feof (stdin)) {
420 	size_t ret = fread (buf, 1, sizeof (buf), stdin);
421 	if (ferror (stdin))
422 	  fail (false, "Failed reading font from standard input: %s",
423 		strerror (errno));
424 	g_string_append_len (gs, buf, ret);
425       }
426       len = gs->len;
427       font_data = g_string_free (gs, false);
428       user_data = font_data;
429       destroy = (hb_destroy_func_t) g_free;
430       mm = HB_MEMORY_MODE_WRITABLE;
431     } else {
432       GError *error = NULL;
433       GMappedFile *mf = g_mapped_file_new (font_file, false, &error);
434       if (mf) {
435 	font_data = g_mapped_file_get_contents (mf);
436 	len = g_mapped_file_get_length (mf);
437 	if (len) {
438 	  destroy = (hb_destroy_func_t) g_mapped_file_unref;
439 	  user_data = (void *) mf;
440 	  mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
441 	} else
442 	  g_mapped_file_unref (mf);
443       } else {
444 	fail (false, "%s", error->message);
445 	//g_error_free (error);
446       }
447       if (!len) {
448 	/* GMappedFile is buggy, it doesn't fail if file isn't regular.
449 	 * Try reading.
450 	 * https://bugzilla.gnome.org/show_bug.cgi?id=659212 */
451         GError *error = NULL;
452 	gsize l;
453 	if (g_file_get_contents (font_file, &font_data, &l, &error)) {
454 	  len = l;
455 	  destroy = (hb_destroy_func_t) g_free;
456 	  user_data = (void *) font_data;
457 	  mm = HB_MEMORY_MODE_WRITABLE;
458 	} else {
459 	  fail (false, "%s", error->message);
460 	  //g_error_free (error);
461 	}
462       }
463     }
464 
465     blob = hb_blob_create (font_data, len, mm, user_data, destroy);
466   }
467 
468   /* Create the face */
469   hb_face_t *face = hb_face_create (blob, face_index);
470   hb_blob_destroy (blob);
471 
472 
473   font = hb_font_create (face);
474 
475   unsigned int upem = hb_face_get_upem (face);
476   hb_font_set_scale (font, upem, upem);
477   hb_face_destroy (face);
478 
479 #ifdef HAVE_FREETYPE
480   hb_ft_font_set_funcs (font);
481 #endif
482 
483   return font;
484 }
485 
486 
487 const char *
get_line(unsigned int * len)488 text_options_t::get_line (unsigned int *len)
489 {
490   if (text) {
491     if (text_len == (unsigned int) -1)
492       text_len = strlen (text);
493 
494     if (!text_len) {
495       *len = 0;
496       return NULL;
497     }
498 
499     const char *ret = text;
500     const char *p = (const char *) memchr (text, '\n', text_len);
501     unsigned int ret_len;
502     if (!p) {
503       ret_len = text_len;
504       text += ret_len;
505       text_len = 0;
506     } else {
507       ret_len = p - ret;
508       text += ret_len + 1;
509       text_len -= ret_len + 1;
510     }
511 
512     *len = ret_len;
513     return ret;
514   }
515 
516   if (!fp) {
517     if (!text_file)
518       fail (true, "At least one of text or text-file must be set");
519 
520     if (0 != strcmp (text_file, "-"))
521       fp = fopen (text_file, "r");
522     else
523       fp = stdin;
524 
525     if (!fp)
526       fail (false, "Failed opening text file `%s': %s",
527 	    text_file, strerror (errno));
528 
529     gs = g_string_new (NULL);
530   }
531 
532   g_string_set_size (gs, 0);
533   char buf[BUFSIZ];
534   while (fgets (buf, sizeof (buf), fp)) {
535     unsigned int bytes = strlen (buf);
536     if (bytes && buf[bytes - 1] == '\n') {
537       bytes--;
538       g_string_append_len (gs, buf, bytes);
539       break;
540     }
541       g_string_append_len (gs, buf, bytes);
542   }
543   if (ferror (fp))
544     fail (false, "Failed reading text: %s",
545 	  strerror (errno));
546   *len = gs->len;
547   return !*len && feof (fp) ? NULL : gs->str;
548 }
549 
550 
551 FILE *
get_file_handle(void)552 output_options_t::get_file_handle (void)
553 {
554   if (fp)
555     return fp;
556 
557   if (output_file)
558     fp = fopen (output_file, "wb");
559   else {
560 #if defined(_WIN32) || defined(__CYGWIN__)
561     setmode (fileno (stdout), _O_BINARY);
562 #endif
563     fp = stdout;
564   }
565   if (!fp)
566     fail (false, "Cannot open output file `%s': %s",
567 	  g_filename_display_name (output_file), strerror (errno));
568 
569   return fp;
570 }
571 
572 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)573 parse_verbose (const char *name G_GNUC_UNUSED,
574 	       const char *arg G_GNUC_UNUSED,
575 	       gpointer    data G_GNUC_UNUSED,
576 	       GError    **error G_GNUC_UNUSED)
577 {
578   format_options_t *format_opts = (format_options_t *) data;
579   format_opts->show_text = format_opts->show_unicode = format_opts->show_line_num = true;
580   return true;
581 }
582 
583 void
add_options(option_parser_t * parser)584 format_options_t::add_options (option_parser_t *parser)
585 {
586   GOptionEntry entries[] =
587   {
588     {"no-glyph-names",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_glyph_names,	"Use glyph indices instead of names",	NULL},
589     {"no-positions",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_positions,		"Do not show glyph positions",		NULL},
590     {"no-clusters",	0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->show_clusters,		"Do not show cluster mapping",		NULL},
591     {"show-text",	0, 0,			  G_OPTION_ARG_NONE,	&this->show_text,		"Show input text",			NULL},
592     {"show-unicode",	0, 0,			  G_OPTION_ARG_NONE,	&this->show_unicode,		"Show input Unicode codepoints",	NULL},
593     {"show-line-num",	0, 0,			  G_OPTION_ARG_NONE,	&this->show_line_num,		"Show line numbers",			NULL},
594     {"verbose",		0, G_OPTION_FLAG_NO_ARG,  G_OPTION_ARG_CALLBACK,(gpointer) &parse_verbose,	"Show everything",			NULL},
595     {NULL}
596   };
597   parser->add_group (entries,
598 		     "format",
599 		     "Format options:",
600 		     "Options controlling the formatting of buffer contents",
601 		     this);
602 }
603 
604 void
serialize_unicode(hb_buffer_t * buffer,GString * gs)605 format_options_t::serialize_unicode (hb_buffer_t *buffer,
606 				     GString     *gs)
607 {
608   unsigned int num_glyphs = hb_buffer_get_length (buffer);
609   hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
610 
611   g_string_append_c (gs, '<');
612   for (unsigned int i = 0; i < num_glyphs; i++)
613   {
614     if (i)
615       g_string_append_c (gs, ',');
616     g_string_append_printf (gs, "U+%04X", info->codepoint);
617     info++;
618   }
619   g_string_append_c (gs, '>');
620 }
621 
622 void
serialize_glyphs(hb_buffer_t * buffer,hb_font_t * font,hb_buffer_serialize_format_t output_format,hb_buffer_serialize_flags_t flags,GString * gs)623 format_options_t::serialize_glyphs (hb_buffer_t *buffer,
624 				    hb_font_t   *font,
625 				    hb_buffer_serialize_format_t output_format,
626 				    hb_buffer_serialize_flags_t flags,
627 				    GString     *gs)
628 {
629   g_string_append_c (gs, '[');
630   unsigned int num_glyphs = hb_buffer_get_length (buffer);
631   unsigned int start = 0;
632 
633   while (start < num_glyphs) {
634     char buf[1024];
635     unsigned int consumed;
636     start += hb_buffer_serialize_glyphs (buffer, start, num_glyphs,
637 					 buf, sizeof (buf), &consumed,
638 					 font, output_format, flags);
639     if (!consumed)
640       break;
641     g_string_append (gs, buf);
642   }
643   g_string_append_c (gs, ']');
644 }
645 void
serialize_line_no(unsigned int line_no,GString * gs)646 format_options_t::serialize_line_no (unsigned int  line_no,
647 				     GString      *gs)
648 {
649   if (show_line_num)
650     g_string_append_printf (gs, "%d: ", line_no);
651 }
652 void
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)653 format_options_t::serialize_buffer_of_text (hb_buffer_t  *buffer,
654 					    unsigned int  line_no,
655 					    const char   *text,
656 					    unsigned int  text_len,
657 					    hb_font_t    *font,
658 					    GString      *gs)
659 {
660   if (show_text) {
661     serialize_line_no (line_no, gs);
662     g_string_append_c (gs, '(');
663     g_string_append_len (gs, text, text_len);
664     g_string_append_c (gs, ')');
665     g_string_append_c (gs, '\n');
666   }
667 
668   if (show_unicode) {
669     serialize_line_no (line_no, gs);
670     serialize_unicode (buffer, gs);
671     g_string_append_c (gs, '\n');
672   }
673 }
674 void
serialize_message(unsigned int line_no,const char * msg,GString * gs)675 format_options_t::serialize_message (unsigned int  line_no,
676 				     const char   *msg,
677 				     GString      *gs)
678 {
679   serialize_line_no (line_no, gs);
680   g_string_append_printf (gs, "%s", msg);
681   g_string_append_c (gs, '\n');
682 }
683 void
serialize_buffer_of_glyphs(hb_buffer_t * buffer,unsigned int line_no,const char * text,unsigned int text_len,hb_font_t * font,hb_buffer_serialize_format_t output_format,hb_buffer_serialize_flags_t format_flags,GString * gs)684 format_options_t::serialize_buffer_of_glyphs (hb_buffer_t  *buffer,
685 					      unsigned int  line_no,
686 					      const char   *text,
687 					      unsigned int  text_len,
688 					      hb_font_t    *font,
689 					      hb_buffer_serialize_format_t output_format,
690 					      hb_buffer_serialize_flags_t format_flags,
691 					      GString      *gs)
692 {
693   serialize_line_no (line_no, gs);
694   serialize_glyphs (buffer, font, output_format, format_flags, gs);
695   g_string_append_c (gs, '\n');
696 }
697