• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2023  Behdad Esfahbod
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 "batch.hh"
28 #include "font-options.hh"
29 
30 #ifdef HB_HAS_GOBJECT
31 #include <hb-gobject.h>
32 #endif
33 
34 #ifdef HAVE_CHAFA
35 # include <chafa.h>
36 #endif
37 
38 const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM;
39 const unsigned SUBPIXEL_BITS = 0;
40 
41 static void
_hb_ot_name_get_utf8(hb_face_t * face,hb_ot_name_id_t name_id,hb_language_t language,unsigned int * text_size,char * text)42 _hb_ot_name_get_utf8 (hb_face_t       *face,
43 		      hb_ot_name_id_t  name_id,
44 		      hb_language_t    language,
45 		      unsigned int    *text_size /* IN/OUT */,
46 		      char            *text      /* OUT */)
47 {
48   static hb_language_t en = hb_language_from_string ("en", -1);
49 
50   unsigned len = *text_size;
51   if (!hb_ot_name_get_utf8 (face, name_id,
52 			    language,
53 			    &len, text))
54   {
55     len = *text_size;
56     hb_ot_name_get_utf8 (face, name_id,
57 			 en,
58 			 &len, text);
59   }
60   *text_size = len;
61 }
62 
63 struct info_t :
64        option_parser_t,
65        font_options_t
66 {
add_optionsinfo_t67   void add_options ()
68   {
69     font_options_t::add_options (this);
70 
71     GOptionEntry misc_entries[] =
72     {
73       {"direction",	0, 0, G_OPTION_ARG_STRING,	&this->direction_str,		"Set direction (default: ltr)",		"ltr/rtl/ttb/btt"},
74       {"script",	0, 0, G_OPTION_ARG_STRING,	&this->script_str,		"Set script (default: none)",		"ISO-15924 tag; eg. 'Latn'"},
75       {"language",	0, 0, G_OPTION_ARG_STRING,	&this->language_str,		"Set language (default: $LANG)",	"BCP 47 tag; eg. 'en'"},
76       {"ot-script",	0, 0, G_OPTION_ARG_STRING,	&this->ot_script_str,		"Set OpenType script tag (default: none)","tag; eg. 'latn'"},
77       {"ot-language",	0, 0, G_OPTION_ARG_STRING,	&this->ot_language_str,		"Set OpenType language tag (default: none)",	"tag; eg. 'ENG'"},
78 
79       {nullptr}
80     };
81     add_group (misc_entries,
82 	       "misc",
83 	       "Miscellaneous options:",
84 	       "Miscellaneous options affecting queries",
85 	       this,
86 	       false /* We add below. */);
87 
88     GOptionEntry query_entries[] =
89     {
90       {"all",		'a', 0, G_OPTION_ARG_NONE,	&this->all,			"Show everything",		nullptr},
91 
92       {"show-all",	0, 0, G_OPTION_ARG_NONE,	&this->show_all,		"Show all short information (default)",	nullptr},
93       {"show-face-count",0, 0, G_OPTION_ARG_NONE,	&this->show_face_count,		"Show face count",		nullptr},
94       {"show-family",	0, 0, G_OPTION_ARG_NONE,	&this->show_family,		"Show family name",		nullptr},
95       {"show-subfamily",0, 0, G_OPTION_ARG_NONE,	&this->show_subfamily,		"Show subfamily name",		nullptr},
96       {"show-unique-name",0, 0, G_OPTION_ARG_NONE,	&this->show_unique_name,	"Show unique name",		nullptr},
97       {"show-full-name",0, 0, G_OPTION_ARG_NONE,	&this->show_full_name,		"Show full name",		nullptr},
98       {"show-postscript-name",0, 0, G_OPTION_ARG_NONE,	&this->show_postscript_name,	"Show Postscript name",		nullptr},
99       {"show-version",	0, 0, G_OPTION_ARG_NONE,	&this->show_version,		"Show version",			nullptr},
100       {"show-technology",0, 0, G_OPTION_ARG_NONE,	&this->show_technology,		"Show technology",		nullptr},
101       {"show-unicode-count",0, 0, G_OPTION_ARG_NONE,	&this->show_unicode_count,	"Show Unicode count",		nullptr},
102       {"show-glyph-count",0, 0, G_OPTION_ARG_NONE,	&this->show_glyph_count,	"Show glyph count",		nullptr},
103       {"show-upem",	0, 0, G_OPTION_ARG_NONE,	&this->show_upem,		"Show Units-Per-EM",		nullptr},
104       {"show-extents",	0, 0, G_OPTION_ARG_NONE,	&this->show_extents,		"Show extents",			nullptr},
105 
106       {"get-name",	0, 0, G_OPTION_ARG_STRING_ARRAY,&this->get_name,		"Get name",			"name id; eg. '13'"},
107       {"get-style",	0, 0, G_OPTION_ARG_STRING_ARRAY,&this->get_style,		"Get style",			"style tag; eg. 'wght'"},
108       {"get-metric",	0, 0, G_OPTION_ARG_STRING_ARRAY,&this->get_metric,		"Get metric",			"metric tag; eg. 'hasc'"},
109       {"get-baseline",	0, 0, G_OPTION_ARG_STRING_ARRAY,&this->get_baseline,		"Get baseline",			"baseline tag; eg. 'hang'"},
110       {"get-meta",	0, 0, G_OPTION_ARG_STRING_ARRAY,&this->get_meta,		"Get meta information",		"tag tag; eg. 'dlng'"},
111       {"get-table",	0, 0, G_OPTION_ARG_STRING,	&this->get_table,		"Get font table",		"table tag; eg. 'cmap'"},
112 
113       {"list-all",	0, 0, G_OPTION_ARG_NONE,	&this->list_all,		"List all long information",	nullptr},
114       {"list-names",	0, 0, G_OPTION_ARG_NONE,	&this->list_names,		"List names",			nullptr},
115 #ifdef HB_HAS_GOBJECT
116       {"list-style",	0, 0, G_OPTION_ARG_NONE,	&this->list_style,		"List style",			nullptr},
117       {"list-metrics",	0, 0, G_OPTION_ARG_NONE,	&this->list_metrics,		"List metrics",			nullptr},
118       {"list-baselines",0, 0, G_OPTION_ARG_NONE,	&this->list_baselines,		"List baselines",		nullptr},
119 #endif
120       {"list-tables",	'l', 0, G_OPTION_ARG_NONE,	&this->list_tables,		"List tables",			nullptr},
121       {"list-unicodes",	0, 0, G_OPTION_ARG_NONE,	&this->list_unicodes,		"List characters",		nullptr},
122       {"list-glyphs",	0, 0, G_OPTION_ARG_NONE,	&this->list_glyphs,		"List glyphs",			nullptr},
123       {"list-scripts",	0, 0, G_OPTION_ARG_NONE,	&this->list_scripts,		"List layout scripts",		nullptr},
124       {"list-features",	0, 0, G_OPTION_ARG_NONE,	&this->list_features,		"List layout features",		nullptr},
125 #ifndef HB_NO_VAR
126       {"list-variations",0, 0, G_OPTION_ARG_NONE,	&this->list_variations,		"List variations",		nullptr},
127 #endif
128       {"list-palettes",	0, 0, G_OPTION_ARG_NONE,	&this->list_palettes,		"List color palettes",		nullptr},
129       {"list-meta",	0, 0, G_OPTION_ARG_NONE,	&this->list_meta,		"List meta information",	nullptr},
130 
131       {nullptr}
132     };
133     add_group (query_entries,
134 	       "query",
135 	       "Query options:",
136 	       "Options to query the font instance",
137 	       this,
138 	       true);
139 
140     GOptionEntry entries[] =
141     {
142       {"quiet",		'q', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE,	&this->verbose,	"Generate machine-readable output",	nullptr},
143       {G_OPTION_REMAINING,	0, G_OPTION_FLAG_IN_MAIN,
144 				G_OPTION_ARG_CALLBACK,	(gpointer) &collect_rest,	nullptr,	"[FONT-FILE]"},
145       {nullptr}
146     };
147     add_main_group (entries, this);
148 
149     option_parser_t::add_options ();
150   }
151 
152   static gboolean
collect_restinfo_t153   collect_rest (const char *name G_GNUC_UNUSED,
154 		const char *arg,
155 		gpointer    data,
156 		GError    **error)
157   {
158     info_t *thiz = (info_t *) data;
159 
160     if (!thiz->font_file)
161     {
162       thiz->font_file = g_strdup (arg);
163       return true;
164     }
165 
166     g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
167 		 "Too many arguments on the command line");
168     return false;
169   }
170 
171 
172   protected:
173 
174   hb_bool_t verbose = true;
175   hb_bool_t first_item = true;
176 
177   char *direction_str = nullptr;
178   char *script_str = nullptr;
179   char *language_str = nullptr;
180   hb_direction_t direction = HB_DIRECTION_LTR;
181   hb_script_t script = HB_SCRIPT_INVALID;
182   hb_language_t language = HB_LANGUAGE_INVALID;
183   char *ot_script_str = nullptr;
184   char *ot_language_str = nullptr;
185 
186   hb_bool_t all = false;
187 
188   hb_bool_t show_all = false;
189   hb_bool_t show_face_count = false;
190   hb_bool_t show_family = false;
191   hb_bool_t show_subfamily = false;
192   hb_bool_t show_unique_name = false;
193   hb_bool_t show_full_name = false;
194   hb_bool_t show_postscript_name = false;
195   hb_bool_t show_version = false;
196   hb_bool_t show_technology = false;
197   hb_bool_t show_unicode_count = false;
198   hb_bool_t show_glyph_count = false;
199   hb_bool_t show_upem = false;
200   hb_bool_t show_extents = false;
201 
202   char **get_name = nullptr;
203   char **get_style = nullptr;
204   char **get_metric = nullptr;
205   char **get_baseline = nullptr;
206   char **get_meta = nullptr;
207   char *get_table = nullptr;
208 
209   hb_bool_t list_all = false;
210   hb_bool_t list_names = false;
211 #ifdef HB_HAS_GOBJECT
212   hb_bool_t list_style = false;
213   hb_bool_t list_metrics = false;
214   hb_bool_t list_baselines = false;
215 #endif
216   hb_bool_t list_tables = false;
217   hb_bool_t list_unicodes = false;
218   hb_bool_t list_glyphs = false;
219   hb_bool_t list_scripts = false;
220   hb_bool_t list_features = false;
221 #ifndef HB_NO_VAR
222   hb_bool_t list_variations = false;
223 #endif
224   hb_bool_t list_palettes = false;
225   hb_bool_t list_meta = false;
226 
227   public:
228 
229   void
post_parseinfo_t230   post_parse (GError **error)
231   {
232     if (direction_str)
233       direction = hb_direction_from_string (direction_str, -1);
234     if (script_str)
235       script = hb_script_from_string (script_str, -1);
236     language = hb_language_get_default ();
237     if (language_str)
238       language = hb_language_from_string (language_str, -1);
239   }
240 
241   int
operator ()info_t242   operator () (int argc, char **argv)
243   {
244     add_options ();
245 
246     if (argc == 2)
247       show_all = true;
248 
249     parse (&argc, &argv);
250 
251     if (all)
252     {
253       show_all =
254       list_all =
255       true;
256     }
257 
258     if (show_all)
259     {
260       show_face_count =
261       show_family =
262       show_subfamily =
263       show_unique_name =
264       show_full_name =
265       show_postscript_name =
266       show_version =
267       show_technology =
268       show_unicode_count =
269       show_glyph_count =
270       show_upem =
271       show_extents =
272       true;
273       first_item = false;
274     }
275 
276     if (list_all)
277     {
278       list_names =
279 #ifdef HB_HAS_GOBJECT
280       list_style =
281       list_metrics =
282       list_baselines =
283 #endif
284       list_tables =
285       list_unicodes =
286       list_glyphs =
287       list_scripts =
288       list_features =
289 #ifndef HB_NO_VAR
290       list_variations =
291 #endif
292       list_palettes =
293       list_meta =
294       true;
295     }
296 
297     if (show_face_count)  _show_face_count ();
298     if (show_family)	  _show_family ();
299     if (show_subfamily)	  _show_subfamily ();
300     if (show_unique_name) _show_unique_name ();
301     if (show_full_name)	  _show_full_name ();
302     if (show_postscript_name)_show_postscript_name ();
303     if (show_version)	  _show_version ();
304     if (show_technology)  _show_technology ();
305     if (show_unicode_count)_show_unicode_count ();
306     if (show_glyph_count) _show_glyph_count ();
307     if (show_upem)	  _show_upem ();
308     if (show_extents)	  _show_extents ();
309 
310     if (get_name)	  _get_name ();
311     if (get_style)	  _get_style ();
312     if (get_metric)	  _get_metric ();
313     if (get_baseline)	  _get_baseline ();
314     if (get_meta)	  _get_meta ();
315     if (get_table)	  _get_table ();
316 
317     if (list_names)	  _list_names ();
318 #ifdef HB_HAS_GOBJECT
319     if (list_style)	  _list_style ();
320     if (list_metrics)	  _list_metrics ();
321     if (list_baselines)	  _list_baselines ();
322 #endif
323     if (list_tables)	  _list_tables ();
324     if (list_unicodes)	  _list_unicodes ();
325     if (list_glyphs)	  _list_glyphs ();
326     if (list_scripts)	  _list_scripts ();
327     if (list_features)	  _list_features ();
328 #ifndef HB_NO_VAR
329     if (list_variations)  _list_variations ();
330 #endif
331     if (list_palettes)	  _list_palettes ();
332     if (list_meta)	  _list_meta ();
333 
334     return 0;
335   }
336 
337   protected:
338 
separatorinfo_t339   void separator ()
340   {
341     if (first_item)
342     {
343       first_item = false;
344       return;
345     }
346     printf ("\n===\n\n");
347   }
348 
349   void
_show_face_countinfo_t350   _show_face_count ()
351   {
352     printf ("Face count: %u\n", hb_face_count (blob));
353   }
354 
355   void
_show_nameinfo_t356   _show_name (const char *label, hb_ot_name_id_t name_id)
357   {
358     if (verbose)
359     {
360       printf ("%s: ", label);
361     }
362 
363     char name[16384];
364     unsigned name_len = sizeof name;
365     _hb_ot_name_get_utf8 (face, name_id,
366 			  language,
367 			  &name_len, name);
368 
369     printf ("%s\n", name);
370   }
_show_familyinfo_t371   void _show_family ()		{ _show_name ("Family", 1); }
_show_subfamilyinfo_t372   void _show_subfamily ()
373   {
374     hb_ot_name_id_t name_id = 2;
375 
376     unsigned named_instance = hb_font_get_var_named_instance (font);
377     if (named_instance != HB_FONT_NO_VAR_NAMED_INSTANCE)
378       name_id = hb_ot_var_named_instance_get_subfamily_name_id (face, named_instance);
379 
380     _show_name ("Subfamily", name_id);
381   }
_show_unique_nameinfo_t382   void _show_unique_name ()	{ _show_name ("Unique name", 3); }
_show_full_nameinfo_t383   void _show_full_name ()	{ _show_name ("Full name", 4); }
_show_postscript_nameinfo_t384   void _show_postscript_name ()
385   {
386     hb_ot_name_id_t name_id = 6;
387 
388     unsigned named_instance = hb_font_get_var_named_instance (font);
389     if (named_instance != HB_FONT_NO_VAR_NAMED_INSTANCE)
390       name_id = hb_ot_var_named_instance_get_postscript_name_id (face, named_instance);
391 
392 
393     _show_name ("Postscript name", name_id);
394   }
_show_versioninfo_t395   void _show_version ()		{ _show_name ("Version", 5); }
396 
_has_blobinfo_t397   bool _has_blob (hb_tag_t tag)
398   {
399     hb_blob_t *blob = hb_face_reference_table (face, tag);
400     bool ret = hb_blob_get_length (blob);
401     hb_blob_destroy (blob);
402     return ret;
403   }
404 
_show_technologyinfo_t405   void _show_technology ()
406   {
407     if (_has_blob (HB_TAG('g','l','y','f')))
408       printf ("Has TrueType outlines\n");
409     if (_has_blob (HB_TAG('C','F','F',' ')) || _has_blob (HB_TAG('C','F','F','2')))
410       printf ("Has Postscript outlines\n");
411 
412     if (_has_blob (HB_TAG('f','p','g','m')) || _has_blob (HB_TAG('p','r','e','p')) || _has_blob (HB_TAG('c','v','t',' ')))
413       printf ("Has TrueType hinting\n");
414 
415     if (_has_blob (HB_TAG('G','S','U','B')) || _has_blob (HB_TAG('G','P','O','S')))
416       printf ("Has OpenType layout\n");
417     if (_has_blob (HB_TAG('m','o','r','x')) || _has_blob (HB_TAG('k','e','r','x')))
418       printf ("Has AAT layout\n");
419     if (_has_blob (HB_TAG('S','i','l','f')))
420       printf ("Has Graphite layout\n");
421     if (_has_blob (HB_TAG('k','e','r','n')))
422       printf ("Has legacy kerning\n");
423 
424     if (_has_blob (HB_TAG('E','B','D','T')))
425       printf ("Has monochrome bitmaps\n");
426 
427     if (_has_blob (HB_TAG('C','B','D','T')) || _has_blob (HB_TAG('s','b','i','x')))
428       printf ("Has color bitmaps\n");
429     if (_has_blob (HB_TAG('S','V','G',' ')))
430       printf ("Has color SVGs\n");
431     if (_has_blob (HB_TAG('C','O','L','R')))
432       printf ("Has color paintings\n");
433 
434     if (_has_blob (HB_TAG('f','v','a','r')))  printf ("Has variations\n");
435   }
436 
_show_unicode_countinfo_t437   void _show_unicode_count ()
438   {
439     if (verbose)
440     {
441       printf ("Unicode count: ");
442     }
443 
444     hb_set_t *unicodes = hb_set_create ();
445     hb_face_collect_unicodes (face, unicodes);
446 
447     printf ("%u\n", hb_set_get_population (unicodes));
448 
449     hb_set_destroy (unicodes);
450   }
451 
_show_glyph_countinfo_t452   void _show_glyph_count ()
453   {
454     if (verbose)
455     {
456       printf ("Glyph count: ");
457     }
458 
459     printf ("%u\n", hb_face_get_glyph_count (face));
460   }
461 
_show_upeminfo_t462   void _show_upem ()
463   {
464     if (verbose)
465     {
466       printf ("Units-Per-EM: ");
467     }
468 
469     printf ("%u\n", hb_face_get_upem (face));
470   }
471 
_show_extentsinfo_t472   void _show_extents ()
473   {
474     hb_font_extents_t extents;
475     hb_font_get_extents_for_direction (font, direction, &extents);
476 
477     if (verbose) printf ("Ascender: ");
478     printf ("%d\n", extents.ascender);
479 
480     if (verbose) printf ("Descender: ");
481     printf ("%d\n", extents.descender);
482 
483     if (verbose) printf ("Line gap: ");
484     printf ("%d\n", extents.line_gap);
485   }
486 
_get_nameinfo_t487   void _get_name ()
488   {
489     for (char **p = get_name; *p; p++)
490     {
491       hb_ot_name_id_t name_id = (hb_ot_name_id_t) atoi (*p);
492       _show_name (*p, name_id);
493     }
494   }
495 
_get_styleinfo_t496   void _get_style ()
497   {
498     for (char **p = get_style; *p; p++)
499     {
500       hb_style_tag_t tag = (hb_style_tag_t) hb_tag_from_string (*p, -1);
501 
502       if (verbose)
503 	printf ("Style %c%c%c%c: ", HB_UNTAG (tag));
504 
505       float v = hb_style_get_value (font, tag);
506       printf ("%g\n", (double) v);
507     }
508   }
509 
_get_metricinfo_t510   void _get_metric ()
511   {
512     bool fallback = false;
513     for (char **p = get_metric; *p; p++)
514     {
515       hb_ot_metrics_tag_t tag = (hb_ot_metrics_tag_t) hb_tag_from_string (*p, -1);
516       hb_position_t position;
517 
518       if (verbose)
519 	printf ("Metric %c%c%c%c: ", HB_UNTAG (tag));
520 
521       if (hb_ot_metrics_get_position (font, tag, &position))
522 	printf ("%d	\n", position);
523       else
524       {
525 	hb_ot_metrics_get_position_with_fallback (font, tag, &position);
526 	printf ("%d	*\n", position);
527 	fallback = true;
528       }
529     }
530 
531     if (verbose && fallback)
532     {
533       printf ("\n[*] Fallback value\n");
534     }
535   }
536 
_get_baselineinfo_t537   void _get_baseline ()
538   {
539     hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
540     hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
541     unsigned script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
542     unsigned language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
543 
544     hb_ot_tags_from_script_and_language (script, language,
545 					 &script_count, script_tags,
546 					 &language_count, language_tags);
547 
548     hb_tag_t script_tag = script_count ? script_tags[script_count - 1] : HB_TAG_NONE;
549     hb_tag_t language_tag = language_count ? language_tags[0] : HB_TAG_NONE;
550 
551     if (ot_script_str)
552       script_tag = hb_tag_from_string (ot_script_str, -1);
553     if (ot_language_str)
554       language_tag = hb_tag_from_string (ot_language_str, -1);
555 
556 
557     bool fallback = false;
558     for (char **p = get_baseline; *p; p++)
559     {
560       hb_ot_layout_baseline_tag_t tag = (hb_ot_layout_baseline_tag_t) hb_tag_from_string (*p, -1);
561       hb_position_t position;
562 
563       if (verbose)
564 	printf ("Baseline %c%c%c%c: ", HB_UNTAG (tag));
565 
566       if (hb_ot_layout_get_baseline (font, tag, direction, script_tag, language_tag, &position))
567 	printf ("%d	\n", position);
568       else
569       {
570 	hb_ot_layout_get_baseline_with_fallback (font, tag, direction, script_tag, language_tag, &position);
571 	printf ("%d	*\n", position);
572 	fallback = true;
573       }
574     }
575 
576     if (verbose && fallback)
577     {
578       printf ("\n[*] Fallback value\n");
579     }
580   }
581 
_get_metainfo_t582   void _get_meta ()
583   {
584     for (char **p = get_meta; *p; p++)
585     {
586       hb_ot_meta_tag_t tag = (hb_ot_meta_tag_t) hb_tag_from_string (*p, -1);
587 
588       hb_blob_t *blob = hb_ot_meta_reference_entry (face, tag);
589 
590       if (verbose)
591 	printf ("Meta %c%c%c%c: ", HB_UNTAG (tag));
592 
593       printf ("%.*s\n",
594 	      (int) hb_blob_get_length (blob),
595 	      hb_blob_get_data (blob, nullptr));
596 
597       hb_blob_destroy (blob);
598     }
599   }
600 
601   void
_get_tableinfo_t602   _get_table ()
603   {
604     hb_blob_t *blob = hb_face_reference_table (face, hb_tag_from_string (get_table, -1));
605     unsigned count = 0;
606     const char *data = hb_blob_get_data (blob, &count);
607     fwrite (data, 1, count, stdout);
608     hb_blob_destroy (blob);
609   }
610 
_list_namesinfo_t611   void _list_names ()
612   {
613     if (verbose)
614     {
615       separator ();
616       printf ("Name information:\n\n");
617       printf ("Id: Name			Text\n------------------------------------\n");
618     }
619 
620 #ifdef HB_HAS_GOBJECT
621     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_NAME_ID_PREDEFINED);
622 #endif
623 
624     unsigned count;
625     const auto *entries = hb_ot_name_list_names (face, &count);
626     for (unsigned i = 0; i < count; i++)
627     {
628       char name[16384];
629       unsigned name_len = sizeof name;
630       _hb_ot_name_get_utf8 (face, entries[i].name_id,
631 			    language,
632 			    &name_len, name);
633 
634 #ifdef HB_HAS_GOBJECT
635       if (verbose)
636       {
637 	GEnumValue *enum_value = g_enum_get_value (enum_class, entries[i].name_id);
638 	printf ("%u: %-27s	%s\n", entries[i].name_id, enum_value ? enum_value->value_nick : "", name);
639       }
640       else
641 #endif
642 	printf ("%u	%s\n", entries[i].name_id, name);
643     }
644   }
645 
646 #ifdef HB_HAS_GOBJECT
_list_styleinfo_t647   void _list_style ()
648   {
649     if (verbose)
650     {
651       separator ();
652       printf ("Style information:\n\n");
653       printf ("Tag:  Name				Value\n---------------------------------------------\n");
654     }
655 
656     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_STYLE_TAG);
657 
658     unsigned count = enum_class->n_values;
659     const auto *entries = enum_class->values;
660     for (unsigned i = 0; i < count; i++)
661     {
662 	float v = hb_style_get_value (font, (hb_style_tag_t) entries[i].value);
663 	printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
664 	if (verbose)
665 	  printf (": %-33s", entries[i].value_nick);
666 	printf ("	%g\n", (double) v);
667     }
668   }
669 
_list_metricsinfo_t670   void _list_metrics ()
671   {
672     if (verbose)
673     {
674       separator ();
675       printf ("Metrics information:\n\n");
676       printf ("Tag:  Name				Value\n---------------------------------------------\n");
677     }
678 
679     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_METRICS_TAG);
680 
681     bool any_fallback = false;
682 
683     unsigned count = enum_class->n_values;
684     const auto *entries = enum_class->values;
685     for (unsigned i = 0; i < count; i++)
686     {
687 	bool fallback = false;
688 	hb_position_t v;
689 	if (!hb_ot_metrics_get_position (font,
690 					(hb_ot_metrics_tag_t) entries[i].value,
691 					&v))
692 	{
693 	  hb_ot_metrics_get_position_with_fallback (font,
694 						    (hb_ot_metrics_tag_t) entries[i].value,
695 						    &v);
696 	  any_fallback = fallback = true;
697 	}
698 	printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
699 	if (verbose)
700 	  printf (": %-33s", entries[i].value_nick);
701 	printf ("	%d	", v);
702 
703 	if (fallback)
704 	  printf ("*");
705 	printf ("\n");
706     }
707 
708     if (verbose && any_fallback)
709     {
710       printf ("\n[*] Fallback value\n");
711     }
712   }
713 
_list_baselinesinfo_t714   void _list_baselines ()
715   {
716     hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
717     hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
718     unsigned script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
719     unsigned language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
720 
721     hb_ot_tags_from_script_and_language (script, language,
722 					 &script_count, script_tags,
723 					 &language_count, language_tags);
724 
725     hb_tag_t script_tag = script_count ? script_tags[script_count - 1] : HB_TAG_NONE;
726     hb_tag_t language_tag = language_count ? language_tags[0] : HB_TAG_NONE;
727 
728     if (ot_script_str)
729       script_tag = hb_tag_from_string (ot_script_str, -1);
730     if (ot_language_str)
731       language_tag = hb_tag_from_string (ot_language_str, -1);
732 
733 
734     if (verbose)
735     {
736       separator ();
737       printf ("Baselines information:\n\n");
738       printf ("Tag:  Name				Value\n---------------------------------------------\n");
739     }
740 
741     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_LAYOUT_BASELINE_TAG);
742 
743     bool any_fallback = false;
744 
745     unsigned count = enum_class->n_values;
746     const auto *entries = enum_class->values;
747     for (unsigned i = 0; i < count; i++)
748     {
749 	bool fallback = false;
750 	hb_position_t v;
751 	if (!hb_ot_layout_get_baseline (font, (hb_ot_layout_baseline_tag_t) entries[i].value,
752 					direction, script_tag, language_tag,
753 					&v))
754 	{
755 	  hb_ot_layout_get_baseline_with_fallback (font, (hb_ot_layout_baseline_tag_t) entries[i].value,
756 						   direction, script_tag, language_tag,
757 						   &v);
758 	  any_fallback = fallback = true;
759 	}
760 	printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
761 	if (verbose)
762 	  printf (": %-33s", entries[i].value_nick);
763 	printf ("	%d	", v);
764 
765 	if (fallback)
766 	  printf ("*");
767 	printf ("\n");
768     }
769 
770     if (verbose && any_fallback)
771     {
772       printf ("\n[*] Fallback value\n");
773     }
774   }
775 #endif
776 
_list_tablesinfo_t777   void _list_tables ()
778   {
779     if (verbose)
780     {
781       separator ();
782       printf ("Table information:\n\n");
783       printf ("Tag	Size\n------------\n");
784     }
785 
786     unsigned count = hb_face_get_table_tags (face, 0, nullptr, nullptr);
787     hb_tag_t *tags = (hb_tag_t *) calloc (count, sizeof (hb_tag_t));
788     hb_face_get_table_tags (face, 0, &count, tags);
789 
790     for (unsigned i = 0; i < count; i++)
791     {
792       hb_tag_t tag = tags[i];
793 
794       hb_blob_t *blob = hb_face_reference_table (face, tag);
795 
796       printf ("%c%c%c%c %8u bytes\n", HB_UNTAG (tag), hb_blob_get_length (blob));
797 
798       hb_blob_destroy (blob);
799     }
800 
801     free (tags);
802   }
803 
804   void
_list_unicodesinfo_t805   _list_unicodes ()
806   {
807     if (verbose)
808     {
809       separator ();
810       printf ("Character-set information:\n\n");
811       printf ("Unicode	Glyph name\n------------------\n");
812     }
813 
814     hb_set_t *unicodes = hb_set_create ();
815     hb_map_t *cmap = hb_map_create ();
816 
817     hb_face_collect_nominal_glyph_mapping (face, cmap, unicodes);
818 
819     for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
820 	 hb_set_next (unicodes, &u);)
821     {
822       hb_codepoint_t gid = hb_map_get (cmap, u);
823 
824       char glyphname[64];
825       hb_font_glyph_to_string (font, gid,
826 			       glyphname, sizeof glyphname);
827 
828       printf ("U+%04X	%s\n", u, glyphname);
829     }
830 
831     hb_map_destroy (cmap);
832 
833 
834     /* List variation-selector sequences. */
835     hb_set_t *vars = hb_set_create ();
836 
837     hb_face_collect_variation_selectors (face, vars);
838 
839     for (hb_codepoint_t vs = HB_SET_VALUE_INVALID;
840 	 hb_set_next (vars, &vs);)
841     {
842       hb_set_clear (unicodes);
843       hb_face_collect_variation_unicodes (face, vs, unicodes);
844 
845       for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
846 	   hb_set_next (unicodes, &u);)
847       {
848 	hb_codepoint_t gid = 0;
849 	HB_UNUSED bool b = hb_font_get_variation_glyph (font, u, vs, &gid);
850 	assert (b);
851 
852 	char glyphname[64];
853 	hb_font_glyph_to_string (font, gid,
854 				 glyphname, sizeof glyphname);
855 
856 	printf ("U+%04X,U+%04X	%s\n", vs, u, glyphname);
857       }
858     }
859 
860     hb_set_destroy (vars);
861     hb_set_destroy (unicodes);
862   }
863 
864   void
_list_glyphsinfo_t865   _list_glyphs ()
866   {
867     if (verbose)
868     {
869       separator ();
870       printf ("Glyph-set information:\n\n");
871       printf ("GlyphID	Glyph name\n------------------\n");
872     }
873 
874     unsigned num_glyphs = hb_face_get_glyph_count (face);
875 
876     for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
877     {
878       char glyphname[64];
879       hb_font_glyph_to_string (font, gid,
880 			       glyphname, sizeof glyphname);
881 
882       printf ("%u	%s\n", gid, glyphname);
883     }
884   }
885 
886   void
_list_scriptsinfo_t887   _list_scripts ()
888   {
889     if (verbose)
890     {
891       separator ();
892       printf ("Layout script information:\n\n");
893     }
894 
895     hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
896 
897     for (unsigned int i = 0; table_tags[i]; i++)
898     {
899       if (verbose) printf ("Table: ");
900       printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
901 
902       hb_tag_t script_array[32];
903       unsigned script_count = sizeof script_array / sizeof script_array[0];
904       unsigned script_offset = 0;
905       do
906       {
907 	hb_ot_layout_table_get_script_tags (face, table_tags[i],
908 					    script_offset,
909 					    &script_count,
910 					    script_array);
911 
912 	for (unsigned script_index = 0; script_index < script_count; script_index++)
913 	{
914 	  printf ("	");
915 	  if (verbose) printf ("Script: ");
916 
917 	  hb_tag_t hb_sc = hb_script_to_iso15924_tag (hb_ot_tag_to_script (script_array[script_index]));
918 	  if (script_array[script_index] == HB_TAG ('D','F','L','T'))
919 	    hb_sc = HB_SCRIPT_COMMON;
920 
921 	  printf ("%c%c%c%c (%c%c%c%c)\n",
922 		  HB_UNTAG (hb_sc),
923 		  HB_UNTAG (script_array[script_index]));
924 
925 	  hb_tag_t language_array[32];
926 	  unsigned language_count = sizeof language_array / sizeof language_array[0];
927 	  unsigned language_offset = 0;
928 	  do
929 	  {
930 	    hb_ot_layout_script_get_language_tags (face, table_tags[i],
931 						   script_offset + script_index,
932 						   language_offset,
933 						   &language_count,
934 						   language_array);
935 
936 	    for (unsigned language_index = 0; language_index < language_count; language_index++)
937 	    {
938 	      printf ("		");
939 	      if (verbose) printf ("Language: ");
940 	      printf ("%s (%c%c%c%c)\n",
941 		      hb_language_to_string (hb_ot_tag_to_language (language_array[language_index])),
942 		      HB_UNTAG (language_array[language_index]));
943 	    }
944 
945 	    language_offset += language_count;
946 	  }
947 	  while (language_count == sizeof language_array / sizeof language_array[0]);
948 	}
949 
950 	script_offset += script_count;
951       }
952       while (script_count == sizeof script_array / sizeof script_array[0]);
953 
954     }
955 
956   }
957 
958   void
_list_features_no_scriptinfo_t959   _list_features_no_script ()
960   {
961     if (verbose)
962     {
963       printf ("Showing all font features with duplicates removed.\n\n");
964     }
965 
966     hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
967 
968     hb_set_t *features = hb_set_create ();
969 
970     for (unsigned int i = 0; table_tags[i]; i++)
971     {
972       if (verbose) printf ("Table: ");
973       printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
974 
975       hb_set_clear (features);
976       hb_tag_t feature_array[32];
977       unsigned feature_count = sizeof feature_array / sizeof feature_array[0];
978       unsigned feature_offset = 0;
979       do
980       {
981 	hb_ot_layout_table_get_feature_tags (face, table_tags[i],
982 					     feature_offset,
983 					     &feature_count,
984 					     feature_array);
985 
986 	for (unsigned feature_index = 0; feature_index < feature_count; feature_index++)
987 	{
988 	  if (hb_set_has (features, feature_array[feature_index]))
989 	    continue;
990 	  hb_set_add (features, feature_array[feature_index]);
991 
992 	  hb_ot_name_id_t label_id;
993 
994 	  hb_ot_layout_feature_get_name_ids (face,
995 					     table_tags[i],
996 					     feature_offset + feature_index,
997 					     &label_id,
998 					     nullptr,
999 					     nullptr,
1000 					     nullptr,
1001 					     nullptr);
1002 
1003 	  char name[64];
1004 	  unsigned name_len = sizeof name;
1005 
1006 	  _hb_ot_name_get_utf8 (face, label_id,
1007 				language,
1008 				&name_len, name);
1009 
1010 	  printf ("	");
1011 	  if (verbose) printf ("Feature: ");
1012 	  printf ("%c%c%c%c", HB_UNTAG (feature_array[feature_index]));
1013 
1014 	  if (*name)
1015 	    printf ("	%s", name);
1016 
1017 	  printf ("\n");
1018 	}
1019 
1020 	feature_offset += feature_count;
1021       }
1022       while (feature_count == sizeof feature_array / sizeof feature_array[0]);
1023     }
1024 
1025     hb_set_destroy (features);
1026   }
1027 
1028   void
_list_featuresinfo_t1029   _list_features ()
1030   {
1031     if (verbose)
1032     {
1033       separator ();
1034       printf ("Layout features information:\n\n");
1035     }
1036 
1037     hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
1038 
1039     if (script == HB_SCRIPT_INVALID && !ot_script_str)
1040     {
1041       _list_features_no_script ();
1042       return;
1043     }
1044 
1045     for (unsigned int i = 0; table_tags[i]; i++)
1046     {
1047       if (verbose) printf ("Table: ");
1048       printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
1049 
1050       auto table_tag = table_tags[i];
1051 
1052       hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
1053       hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
1054       unsigned script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
1055       unsigned language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
1056 
1057       hb_ot_tags_from_script_and_language (script, language,
1058 					   &script_count, script_tags,
1059 					   &language_count, language_tags);
1060 
1061       if (ot_script_str)
1062       {
1063 	script_tags[0] = hb_tag_from_string (ot_script_str, -1);
1064 	script_count = 1;
1065       }
1066       if (ot_language_str)
1067       {
1068 	language_tags[0] = hb_tag_from_string (ot_language_str, -1);
1069 	language_count = 1;
1070       }
1071 
1072       unsigned script_index;
1073       hb_tag_t chosen_script;
1074       hb_ot_layout_table_select_script (face, table_tag,
1075 					script_count, script_tags,
1076 					&script_index, &chosen_script);
1077 
1078       unsigned language_index;
1079       hb_tag_t chosen_language;
1080       hb_ot_layout_script_select_language2 (face, table_tag,
1081 					   script_index,
1082 					   language_count, language_tags,
1083 					   &language_index, &chosen_language);
1084 
1085       if (verbose)
1086       {
1087         if (chosen_script)
1088 	{
1089 	  printf ("	Script: %c%c%c%c\n", HB_UNTAG (chosen_script));
1090 	  if (chosen_language)
1091 	    printf ("	Language: %c%c%c%c\n", HB_UNTAG (chosen_language));
1092 	  else
1093 	    printf ("	Language: Default\n");
1094 	}
1095       }
1096 
1097       unsigned feature_array[32];
1098       unsigned feature_count = sizeof feature_array / sizeof feature_array[0];
1099       unsigned feature_offset = 0;
1100       do
1101       {
1102 	hb_ot_layout_language_get_feature_indexes (face, table_tag,
1103 						   script_index, language_index,
1104 						   feature_offset,
1105 						   &feature_count,
1106 						   feature_array);
1107 
1108 	for (unsigned feature_index = 0; feature_index < feature_count; feature_index++)
1109 	{
1110 	  hb_ot_name_id_t label_id;
1111 
1112 	  hb_ot_layout_feature_get_name_ids (face,
1113 					     table_tags[i],
1114 					     feature_array[feature_index],
1115 					     &label_id,
1116 					     nullptr,
1117 					     nullptr,
1118 					     nullptr,
1119 					     nullptr);
1120 
1121 	  char name[64];
1122 	  unsigned name_len = sizeof name;
1123 
1124 	  _hb_ot_name_get_utf8 (face, label_id,
1125 				language,
1126 				&name_len, name);
1127 
1128 	  printf ("	");
1129 	  if (verbose) printf ("Feature: ");
1130 	  hb_tag_t feature_tag;
1131 	  unsigned f_count = 1;
1132 	  hb_ot_layout_table_get_feature_tags (face, table_tag,
1133 					       feature_array[feature_index],
1134 					       &f_count, &feature_tag);
1135 	  printf ("%c%c%c%c", HB_UNTAG (feature_tag));
1136 
1137 	  if (*name)
1138 	    printf ("	%s", name);
1139 
1140 	  printf ("\n");
1141 	}
1142 
1143 	feature_offset += feature_count;
1144       }
1145       while (feature_count == sizeof feature_array / sizeof feature_array[0]);
1146     }
1147   }
1148 
1149 #ifndef HB_NO_VAR
1150   void
_list_variationsinfo_t1151   _list_variations ()
1152   {
1153     if (verbose)
1154     {
1155       separator ();
1156       printf ("Variations information:\n\n");
1157     }
1158 
1159     hb_ot_var_axis_info_t *axes;
1160 
1161     unsigned count = hb_ot_var_get_axis_infos (face, 0, nullptr, nullptr);
1162     axes = (hb_ot_var_axis_info_t *) calloc (count, sizeof (hb_ot_var_axis_info_t));
1163     hb_ot_var_get_axis_infos (face, 0, &count, axes);
1164 
1165     bool has_hidden = false;
1166 
1167     if (verbose && count)
1168     {
1169       printf ("Varitation axes:\n\n");
1170       printf ("Tag	Minimum	Default	Maximum	Name\n------------------------------------\n");
1171     }
1172     for (unsigned i = 0; i < count; i++)
1173     {
1174       const auto &axis = axes[i];
1175       if (axis.flags & HB_OT_VAR_AXIS_FLAG_HIDDEN)
1176 	has_hidden = true;
1177 
1178       char name[64];
1179       unsigned name_len = sizeof name;
1180 
1181       _hb_ot_name_get_utf8 (face, axis.name_id,
1182 			    language,
1183 			    &name_len, name);
1184 
1185       printf ("%c%c%c%c%s	%g	%g	%g	%s\n",
1186 	      HB_UNTAG (axis.tag),
1187 	      axis.flags & HB_OT_VAR_AXIS_FLAG_HIDDEN ? "*" : "",
1188 	      (double) axis.min_value,
1189 	      (double) axis.default_value,
1190 	      (double) axis.max_value,
1191 	      name);
1192     }
1193     if (verbose && has_hidden)
1194       printf ("\n[*] Hidden axis\n");
1195 
1196     free (axes);
1197     axes = nullptr;
1198 
1199     count = hb_ot_var_get_named_instance_count (face);
1200     if (count)
1201     {
1202       if (verbose)
1203       {
1204 	printf ("\n\nNamed instances:\n\n");
1205       printf ("Index	Name				Position\n------------------------------------------------\n");
1206       }
1207 
1208       for (unsigned i = 0; i < count; i++)
1209       {
1210 	char name[64];
1211 	unsigned name_len = sizeof name;
1212 
1213 	hb_ot_name_id_t name_id = hb_ot_var_named_instance_get_subfamily_name_id (face, i);
1214 	_hb_ot_name_get_utf8 (face, name_id,
1215 			      language,
1216 			      &name_len, name);
1217 
1218 	unsigned coords_count = hb_ot_var_named_instance_get_design_coords (face, i, nullptr, nullptr);
1219 	float* coords;
1220 	coords = (float *) calloc (coords_count, sizeof (float));
1221 	hb_ot_var_named_instance_get_design_coords (face, i, &coords_count, coords);
1222 
1223 	printf ("%u	%-32s", i, name);
1224 	for (unsigned j = 0; j < coords_count; j++)
1225 	  printf ("%g, ", (double) coords[j]);
1226 	printf ("\n");
1227 
1228 	free (coords);
1229       }
1230     }
1231   }
1232 #endif
1233 
1234 #ifdef HAVE_CHAFA
1235   GString *
_palette_chafa_strinfo_t1236   _palette_chafa_str (unsigned palette_index)
1237   {
1238     unsigned count = hb_ot_color_palette_get_colors (face, palette_index, 0,
1239 						     nullptr, nullptr);
1240 
1241     hb_color_t *palette = (hb_color_t *) malloc (count * sizeof (hb_color_t));
1242     hb_ot_color_palette_get_colors (face, palette_index, 0,
1243 				    &count, palette);
1244 
1245 #define REPEAT 16
1246     hb_color_t *data = (hb_color_t *) malloc (count * REPEAT * sizeof (hb_color_t));
1247     for (unsigned i = 0; i < count; i++)
1248       for (unsigned j = 0; j < REPEAT; j++)
1249 	data[i * REPEAT + j] = palette[i];
1250     free (palette);
1251     palette = nullptr;
1252 
1253     chafa_set_n_threads (1); // https://github.com/hpjansson/chafa/issues/125#issuecomment-1397475217
1254 			     //
1255     gchar **environ = g_get_environ ();
1256     ChafaTermInfo *term_info = chafa_term_db_detect (chafa_term_db_get_default (),
1257 						     environ);
1258 
1259     ChafaCanvasMode mode;
1260     ChafaPixelMode pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS;
1261     if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT))
1262       mode = CHAFA_CANVAS_MODE_TRUECOLOR;
1263     else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256))
1264       mode = CHAFA_CANVAS_MODE_INDEXED_240;
1265     else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16))
1266       mode = CHAFA_CANVAS_MODE_INDEXED_16;
1267     else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS))
1268       mode = CHAFA_CANVAS_MODE_FGBG_BGFG;
1269     else
1270       mode = CHAFA_CANVAS_MODE_FGBG;
1271 
1272     ChafaSymbolMap *symbol_map = chafa_symbol_map_new ();
1273     chafa_symbol_map_add_by_tags (symbol_map,
1274 				  (ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK));
1275 
1276     ChafaCanvasConfig *config = chafa_canvas_config_new ();
1277     chafa_canvas_config_set_canvas_mode (config, mode);
1278     chafa_canvas_config_set_pixel_mode (config, pixel_mode);
1279     chafa_canvas_config_set_cell_geometry (config, REPEAT, 1);
1280     chafa_canvas_config_set_geometry (config, 2 * count, 1);
1281     chafa_canvas_config_set_symbol_map (config, symbol_map);
1282     chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN);
1283     chafa_canvas_config_set_work_factor (config, 1.0f);
1284 
1285     ChafaCanvas *canvas = chafa_canvas_new (config);
1286     chafa_canvas_draw_all_pixels (canvas,
1287 				  G_BYTE_ORDER == G_BIG_ENDIAN
1288 				    ? CHAFA_PIXEL_BGRA8_UNASSOCIATED
1289 				    : CHAFA_PIXEL_ARGB8_UNASSOCIATED,
1290 				  (const guint8 *) data,
1291 				  count * REPEAT,
1292 				  1,
1293 				  sizeof (hb_color_t));
1294 
1295     free (data);
1296 
1297     auto gs = chafa_canvas_print (canvas, term_info);
1298 
1299     chafa_canvas_unref (canvas);
1300     chafa_canvas_config_unref (config);
1301     chafa_symbol_map_unref (symbol_map);
1302     chafa_term_info_unref (term_info);
1303     g_strfreev (environ);
1304 
1305     return gs;
1306   }
1307 #endif
1308 
1309   void
_list_palettesinfo_t1310   _list_palettes ()
1311   {
1312     if (verbose)
1313     {
1314       separator ();
1315       printf ("Color palettes information:\n");
1316     }
1317 
1318     {
1319       if (verbose)
1320       {
1321 	printf ("\nPalettes:\n\n");
1322 	printf ("Index	Flags	Name\n--------------------\n");
1323       }
1324       unsigned count = hb_ot_color_palette_get_count (face);
1325       for (unsigned i = 0; i < count; i++)
1326       {
1327 	hb_ot_name_id_t name_id = hb_ot_color_palette_get_name_id (face, i);
1328 	hb_ot_color_palette_flags_t flags = hb_ot_color_palette_get_flags (face, i);
1329 
1330 	char name[64];
1331 	unsigned name_len = sizeof name;
1332 
1333 	_hb_ot_name_get_utf8 (face, name_id,
1334 			      language,
1335 			      &name_len, name);
1336         const char *type = "";
1337 	if (flags)
1338 	{
1339 	  if (flags & HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND)
1340           {
1341 	    if (flags & HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND)
1342 	      type = "Both";
1343             else
1344 	      type = "Light";
1345           }
1346           else {
1347 	    type = "Dark";
1348           }
1349 	}
1350 
1351 #ifdef HAVE_CHAFA
1352 	char *chafa_env = getenv ("HB_CHAFA");
1353 	bool use_chafa = !chafa_env || atoi (chafa_env);
1354 	if (verbose && use_chafa && isatty (fileno (stdout)))
1355 	{
1356 	  GString *chafa_str = _palette_chafa_str (i);
1357 	  printf ("%u	%s	%-23s	%*s\n", i, type, name,
1358 		  (int) chafa_str->len, chafa_str->str);
1359 	  g_string_free (chafa_str, TRUE);
1360 	}
1361 	else
1362 #endif
1363 	  printf ("%u	%s	%s\n", i, type, name);
1364       }
1365     }
1366 
1367     {
1368       if (verbose)
1369       {
1370 	printf ("\nColors:\n\n");
1371 	printf ("Index	Name\n------------\n");
1372       }
1373       unsigned count = hb_ot_color_palette_get_colors (face, 0, 0, nullptr, nullptr);
1374       for (unsigned i = 0; i < count; i++)
1375       {
1376 	hb_ot_name_id_t name_id = hb_ot_color_palette_color_get_name_id (face, i);
1377 
1378 	char name[64];
1379 	unsigned name_len = sizeof name;
1380 	_hb_ot_name_get_utf8 (face, name_id,
1381 			      language,
1382 			      &name_len, name);
1383 
1384 	printf ("%u	%s\n", i, name);
1385       }
1386     }
1387   }
1388 
1389   void
_list_metainfo_t1390   _list_meta ()
1391   {
1392     if (verbose)
1393     {
1394       separator ();
1395       printf ("Meta information:\n");
1396     }
1397 
1398     {
1399       if (verbose)
1400       {
1401 	printf ("\nTag	Data\n------------\n");
1402       }
1403       unsigned count = hb_ot_meta_get_entry_tags (face, 0, nullptr, nullptr);
1404       for (unsigned i = 0; i < count; i++)
1405       {
1406 	hb_ot_meta_tag_t tag;
1407 	unsigned len = 1;
1408 	hb_ot_meta_get_entry_tags (face, i, &len, &tag);
1409 
1410 	hb_blob_t *blob = hb_ot_meta_reference_entry (face, tag);
1411 
1412 	printf ("%c%c%c%c	%.*s\n", HB_UNTAG (tag),
1413 		(int) hb_blob_get_length (blob),
1414 		hb_blob_get_data (blob, nullptr));
1415 
1416 	hb_blob_destroy (blob);
1417       }
1418     }
1419   }
1420 
1421 };
1422 
1423 
1424 template <typename consumer_t,
1425 	  typename font_options_type>
1426 struct main_font_t :
1427        option_parser_t,
1428        font_options_type,
1429        consumer_t
1430 {
operator ()main_font_t1431   int operator () (int argc, char **argv)
1432   {
1433     add_options ();
1434 
1435     if (argc == 2)
1436       consumer_t::show_all = true;
1437 
1438     parse (&argc, &argv);
1439 
1440     consumer_t::operator () (this);
1441 
1442     return 0;
1443   }
1444 };
1445 
1446 int
main(int argc,char ** argv)1447 main (int argc, char **argv)
1448 {
1449   return batch_main<info_t> (argc, argv);
1450 }
1451