xref: /aosp_15_r20/external/harfbuzz_ng/util/hb-info.cc (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
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     hb_blob_t *blob = hb_blob_create_from_file (font_file);
353     printf ("Face count: %u\n", hb_face_count (blob));
354     hb_blob_destroy (blob);
355   }
356 
357   void
_show_nameinfo_t358   _show_name (const char *label, hb_ot_name_id_t name_id)
359   {
360     if (verbose)
361     {
362       printf ("%s: ", label);
363     }
364 
365     char name[16384];
366     unsigned name_len = sizeof name;
367     _hb_ot_name_get_utf8 (face, name_id,
368 			  language,
369 			  &name_len, name);
370 
371     printf ("%s\n", name);
372   }
_show_familyinfo_t373   void _show_family ()		{ _show_name ("Family", 1); }
_show_subfamilyinfo_t374   void _show_subfamily ()
375   {
376     hb_ot_name_id_t name_id = 2;
377 
378     unsigned named_instance = hb_font_get_var_named_instance (font);
379     if (named_instance != HB_FONT_NO_VAR_NAMED_INSTANCE)
380       name_id = hb_ot_var_named_instance_get_subfamily_name_id (face, named_instance);
381 
382     _show_name ("Subfamily", name_id);
383   }
_show_unique_nameinfo_t384   void _show_unique_name ()	{ _show_name ("Unique name", 3); }
_show_full_nameinfo_t385   void _show_full_name ()	{ _show_name ("Full name", 4); }
_show_postscript_nameinfo_t386   void _show_postscript_name ()
387   {
388     hb_ot_name_id_t name_id = 6;
389 
390     unsigned named_instance = hb_font_get_var_named_instance (font);
391     if (named_instance != HB_FONT_NO_VAR_NAMED_INSTANCE)
392       name_id = hb_ot_var_named_instance_get_postscript_name_id (face, named_instance);
393 
394 
395     _show_name ("Postscript name", name_id);
396   }
_show_versioninfo_t397   void _show_version ()		{ _show_name ("Version", 5); }
398 
_has_blobinfo_t399   bool _has_blob (hb_tag_t tag)
400   {
401     hb_blob_t *blob = hb_face_reference_table (face, tag);
402     bool ret = hb_blob_get_length (blob);
403     hb_blob_destroy (blob);
404     return ret;
405   }
406 
_show_technologyinfo_t407   void _show_technology ()
408   {
409     if (_has_blob (HB_TAG('g','l','y','f')))
410       printf ("Has TrueType outlines\n");
411     if (_has_blob (HB_TAG('C','F','F',' ')) || _has_blob (HB_TAG('C','F','F','2')))
412       printf ("Has Postscript outlines\n");
413 
414     if (_has_blob (HB_TAG('f','p','g','m')) || _has_blob (HB_TAG('p','r','e','p')) || _has_blob (HB_TAG('c','v','t',' ')))
415       printf ("Has TrueType hinting\n");
416 
417     if (_has_blob (HB_TAG('G','S','U','B')) || _has_blob (HB_TAG('G','P','O','S')))
418       printf ("Has OpenType layout\n");
419     if (_has_blob (HB_TAG('m','o','r','x')) || _has_blob (HB_TAG('k','e','r','x')))
420       printf ("Has AAT layout\n");
421     if (_has_blob (HB_TAG('S','i','l','f')))
422       printf ("Has Graphite layout\n");
423     if (_has_blob (HB_TAG('k','e','r','n')))
424       printf ("Has legacy kerning\n");
425 
426     if (_has_blob (HB_TAG('E','B','D','T')))
427       printf ("Has monochrome bitmaps\n");
428 
429     if (_has_blob (HB_TAG('C','B','D','T')) || _has_blob (HB_TAG('s','b','i','x')))
430       printf ("Has color bitmaps\n");
431     if (_has_blob (HB_TAG('S','V','G',' ')))
432       printf ("Has color SVGs\n");
433     if (_has_blob (HB_TAG('C','O','L','R')))
434       printf ("Has color paintings\n");
435 
436     if (_has_blob (HB_TAG('f','v','a','r')))  printf ("Has variations\n");
437   }
438 
_show_unicode_countinfo_t439   void _show_unicode_count ()
440   {
441     if (verbose)
442     {
443       printf ("Unicode count: ");
444     }
445 
446     hb_set_t *unicodes = hb_set_create ();
447     hb_face_collect_unicodes (face, unicodes);
448 
449     printf ("%u\n", hb_set_get_population (unicodes));
450 
451     hb_set_destroy (unicodes);
452   }
453 
_show_glyph_countinfo_t454   void _show_glyph_count ()
455   {
456     if (verbose)
457     {
458       printf ("Glyph count: ");
459     }
460 
461     printf ("%u\n", hb_face_get_glyph_count (face));
462   }
463 
_show_upeminfo_t464   void _show_upem ()
465   {
466     if (verbose)
467     {
468       printf ("Units-Per-EM: ");
469     }
470 
471     printf ("%u\n", hb_face_get_upem (face));
472   }
473 
_show_extentsinfo_t474   void _show_extents ()
475   {
476     hb_font_extents_t extents;
477     hb_font_get_extents_for_direction (font, direction, &extents);
478 
479     if (verbose) printf ("Ascender: ");
480     printf ("%d\n", extents.ascender);
481 
482     if (verbose) printf ("Descender: ");
483     printf ("%d\n", extents.descender);
484 
485     if (verbose) printf ("Line gap: ");
486     printf ("%d\n", extents.line_gap);
487   }
488 
_get_nameinfo_t489   void _get_name ()
490   {
491     for (char **p = get_name; *p; p++)
492     {
493       hb_ot_name_id_t name_id = (hb_ot_name_id_t) atoi (*p);
494       _show_name (*p, name_id);
495     }
496   }
497 
_get_styleinfo_t498   void _get_style ()
499   {
500     for (char **p = get_style; *p; p++)
501     {
502       hb_style_tag_t tag = (hb_style_tag_t) hb_tag_from_string (*p, -1);
503 
504       if (verbose)
505 	printf ("Style %c%c%c%c: ", HB_UNTAG (tag));
506 
507       float v = hb_style_get_value (font, tag);
508       printf ("%g\n", (double) v);
509     }
510   }
511 
_get_metricinfo_t512   void _get_metric ()
513   {
514     bool fallback = false;
515     for (char **p = get_metric; *p; p++)
516     {
517       hb_ot_metrics_tag_t tag = (hb_ot_metrics_tag_t) hb_tag_from_string (*p, -1);
518       hb_position_t position;
519 
520       if (verbose)
521 	printf ("Metric %c%c%c%c: ", HB_UNTAG (tag));
522 
523       if (hb_ot_metrics_get_position (font, tag, &position))
524 	printf ("%d	\n", position);
525       else
526       {
527 	hb_ot_metrics_get_position_with_fallback (font, tag, &position);
528 	printf ("%d	*\n", position);
529 	fallback = true;
530       }
531     }
532 
533     if (verbose && fallback)
534     {
535       printf ("\n[*] Fallback value\n");
536     }
537   }
538 
_get_baselineinfo_t539   void _get_baseline ()
540   {
541     hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
542     hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
543     unsigned script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
544     unsigned language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
545 
546     hb_ot_tags_from_script_and_language (script, language,
547 					 &script_count, script_tags,
548 					 &language_count, language_tags);
549 
550     hb_tag_t script_tag = script_count ? script_tags[script_count - 1] : HB_TAG_NONE;
551     hb_tag_t language_tag = language_count ? language_tags[0] : HB_TAG_NONE;
552 
553     if (ot_script_str)
554       script_tag = hb_tag_from_string (ot_script_str, -1);
555     if (ot_language_str)
556       language_tag = hb_tag_from_string (ot_language_str, -1);
557 
558 
559     bool fallback = false;
560     for (char **p = get_baseline; *p; p++)
561     {
562       hb_ot_layout_baseline_tag_t tag = (hb_ot_layout_baseline_tag_t) hb_tag_from_string (*p, -1);
563       hb_position_t position;
564 
565       if (verbose)
566 	printf ("Baseline %c%c%c%c: ", HB_UNTAG (tag));
567 
568       if (hb_ot_layout_get_baseline (font, tag, direction, script_tag, language_tag, &position))
569 	printf ("%d	\n", position);
570       else
571       {
572 	hb_ot_layout_get_baseline_with_fallback (font, tag, direction, script_tag, language_tag, &position);
573 	printf ("%d	*\n", position);
574 	fallback = true;
575       }
576     }
577 
578     if (verbose && fallback)
579     {
580       printf ("\n[*] Fallback value\n");
581     }
582   }
583 
_get_metainfo_t584   void _get_meta ()
585   {
586     for (char **p = get_meta; *p; p++)
587     {
588       hb_ot_meta_tag_t tag = (hb_ot_meta_tag_t) hb_tag_from_string (*p, -1);
589 
590       hb_blob_t *blob = hb_ot_meta_reference_entry (face, tag);
591 
592       if (verbose)
593 	printf ("Meta %c%c%c%c: ", HB_UNTAG (tag));
594 
595       printf ("%.*s\n",
596 	      (int) hb_blob_get_length (blob),
597 	      hb_blob_get_data (blob, nullptr));
598 
599       hb_blob_destroy (blob);
600     }
601   }
602 
603   void
_get_tableinfo_t604   _get_table ()
605   {
606     hb_blob_t *blob = hb_face_reference_table (face, hb_tag_from_string (get_table, -1));
607     unsigned count = 0;
608     const char *data = hb_blob_get_data (blob, &count);
609     fwrite (data, 1, count, stdout);
610     hb_blob_destroy (blob);
611   }
612 
_list_namesinfo_t613   void _list_names ()
614   {
615     if (verbose)
616     {
617       separator ();
618       printf ("Name information:\n\n");
619       printf ("Id: Name			Text\n------------------------------------\n");
620     }
621 
622 #ifdef HB_HAS_GOBJECT
623     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_NAME_ID_PREDEFINED);
624 #endif
625 
626     unsigned count;
627     const auto *entries = hb_ot_name_list_names (face, &count);
628     for (unsigned i = 0; i < count; i++)
629     {
630       char name[16384];
631       unsigned name_len = sizeof name;
632       _hb_ot_name_get_utf8 (face, entries[i].name_id,
633 			    language,
634 			    &name_len, name);
635 
636 #ifdef HB_HAS_GOBJECT
637       if (verbose)
638       {
639 	GEnumValue *enum_value = g_enum_get_value (enum_class, entries[i].name_id);
640 	printf ("%u: %-27s	%s\n", entries[i].name_id, enum_value ? enum_value->value_nick : "", name);
641       }
642       else
643 #endif
644 	printf ("%u	%s\n", entries[i].name_id, name);
645     }
646   }
647 
648 #ifdef HB_HAS_GOBJECT
_list_styleinfo_t649   void _list_style ()
650   {
651     if (verbose)
652     {
653       separator ();
654       printf ("Style information:\n\n");
655       printf ("Tag:  Name				Value\n---------------------------------------------\n");
656     }
657 
658     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_STYLE_TAG);
659 
660     unsigned count = enum_class->n_values;
661     const auto *entries = enum_class->values;
662     for (unsigned i = 0; i < count; i++)
663     {
664 	float v = hb_style_get_value (font, (hb_style_tag_t) entries[i].value);
665 	printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
666 	if (verbose)
667 	  printf (": %-33s", entries[i].value_nick);
668 	printf ("	%g\n", (double) v);
669     }
670   }
671 
_list_metricsinfo_t672   void _list_metrics ()
673   {
674     if (verbose)
675     {
676       separator ();
677       printf ("Metrics information:\n\n");
678       printf ("Tag:  Name				Value\n---------------------------------------------\n");
679     }
680 
681     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_METRICS_TAG);
682 
683     bool any_fallback = false;
684 
685     unsigned count = enum_class->n_values;
686     const auto *entries = enum_class->values;
687     for (unsigned i = 0; i < count; i++)
688     {
689 	bool fallback = false;
690 	hb_position_t v;
691 	if (!hb_ot_metrics_get_position (font,
692 					(hb_ot_metrics_tag_t) entries[i].value,
693 					&v))
694 	{
695 	  hb_ot_metrics_get_position_with_fallback (font,
696 						    (hb_ot_metrics_tag_t) entries[i].value,
697 						    &v);
698 	  any_fallback = fallback = true;
699 	}
700 	printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
701 	if (verbose)
702 	  printf (": %-33s", entries[i].value_nick);
703 	printf ("	%d	", v);
704 
705 	if (fallback)
706 	  printf ("*");
707 	printf ("\n");
708     }
709 
710     if (verbose && any_fallback)
711     {
712       printf ("\n[*] Fallback value\n");
713     }
714   }
715 
_list_baselinesinfo_t716   void _list_baselines ()
717   {
718     hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
719     hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
720     unsigned script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
721     unsigned language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
722 
723     hb_ot_tags_from_script_and_language (script, language,
724 					 &script_count, script_tags,
725 					 &language_count, language_tags);
726 
727     hb_tag_t script_tag = script_count ? script_tags[script_count - 1] : HB_TAG_NONE;
728     hb_tag_t language_tag = language_count ? language_tags[0] : HB_TAG_NONE;
729 
730     if (ot_script_str)
731       script_tag = hb_tag_from_string (ot_script_str, -1);
732     if (ot_language_str)
733       language_tag = hb_tag_from_string (ot_language_str, -1);
734 
735 
736     if (verbose)
737     {
738       separator ();
739       printf ("Baselines information:\n\n");
740       printf ("Tag:  Name				Value\n---------------------------------------------\n");
741     }
742 
743     GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_LAYOUT_BASELINE_TAG);
744 
745     bool any_fallback = false;
746 
747     unsigned count = enum_class->n_values;
748     const auto *entries = enum_class->values;
749     for (unsigned i = 0; i < count; i++)
750     {
751 	bool fallback = false;
752 	hb_position_t v;
753 	if (!hb_ot_layout_get_baseline (font, (hb_ot_layout_baseline_tag_t) entries[i].value,
754 					direction, script_tag, language_tag,
755 					&v))
756 	{
757 	  hb_ot_layout_get_baseline_with_fallback (font, (hb_ot_layout_baseline_tag_t) entries[i].value,
758 						   direction, script_tag, language_tag,
759 						   &v);
760 	  any_fallback = fallback = true;
761 	}
762 	printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
763 	if (verbose)
764 	  printf (": %-33s", entries[i].value_nick);
765 	printf ("	%d	", v);
766 
767 	if (fallback)
768 	  printf ("*");
769 	printf ("\n");
770     }
771 
772     if (verbose && any_fallback)
773     {
774       printf ("\n[*] Fallback value\n");
775     }
776   }
777 #endif
778 
_list_tablesinfo_t779   void _list_tables ()
780   {
781     if (verbose)
782     {
783       separator ();
784       printf ("Table information:\n\n");
785       printf ("Tag	Size\n------------\n");
786     }
787 
788     unsigned count = hb_face_get_table_tags (face, 0, nullptr, nullptr);
789     hb_tag_t *tags = (hb_tag_t *) calloc (count, sizeof (hb_tag_t));
790     hb_face_get_table_tags (face, 0, &count, tags);
791 
792     for (unsigned i = 0; i < count; i++)
793     {
794       hb_tag_t tag = tags[i];
795 
796       hb_blob_t *blob = hb_face_reference_table (face, tag);
797 
798       printf ("%c%c%c%c %8u bytes\n", HB_UNTAG (tag), hb_blob_get_length (blob));
799 
800       hb_blob_destroy (blob);
801     }
802 
803     free (tags);
804   }
805 
806   void
_list_unicodesinfo_t807   _list_unicodes ()
808   {
809     if (verbose)
810     {
811       separator ();
812       printf ("Character-set information:\n\n");
813       printf ("Unicode	Glyph name\n------------------\n");
814     }
815 
816     hb_set_t *unicodes = hb_set_create ();
817     hb_map_t *cmap = hb_map_create ();
818 
819     hb_face_collect_nominal_glyph_mapping (face, cmap, unicodes);
820 
821     for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
822 	 hb_set_next (unicodes, &u);)
823     {
824       hb_codepoint_t gid = hb_map_get (cmap, u);
825 
826       char glyphname[128];
827       hb_font_glyph_to_string (font, gid,
828 			       glyphname, sizeof glyphname);
829 
830       printf ("U+%04X	%s\n", u, glyphname);
831     }
832 
833     hb_map_destroy (cmap);
834 
835 
836     /* List variation-selector sequences. */
837     hb_set_t *vars = hb_set_create ();
838 
839     hb_face_collect_variation_selectors (face, vars);
840 
841     for (hb_codepoint_t vs = HB_SET_VALUE_INVALID;
842 	 hb_set_next (vars, &vs);)
843     {
844       hb_set_clear (unicodes);
845       hb_face_collect_variation_unicodes (face, vs, unicodes);
846 
847       for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
848 	   hb_set_next (unicodes, &u);)
849       {
850 	hb_codepoint_t gid = 0;
851 	HB_UNUSED bool b = hb_font_get_variation_glyph (font, u, vs, &gid);
852 	assert (b);
853 
854 	char glyphname[128];
855 	hb_font_glyph_to_string (font, gid,
856 				 glyphname, sizeof glyphname);
857 
858 	printf ("U+%04X,U+%04X	%s\n", vs, u, glyphname);
859       }
860     }
861 
862     hb_set_destroy (vars);
863     hb_set_destroy (unicodes);
864   }
865 
866   void
_list_glyphsinfo_t867   _list_glyphs ()
868   {
869     if (verbose)
870     {
871       separator ();
872       printf ("Glyph-set information:\n\n");
873       printf ("GlyphID	Glyph name\n------------------\n");
874     }
875 
876     unsigned num_glyphs = hb_face_get_glyph_count (face);
877 
878     for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
879     {
880       char glyphname[128];
881       hb_font_glyph_to_string (font, gid,
882 			       glyphname, sizeof glyphname);
883 
884       printf ("%u	%s\n", gid, glyphname);
885     }
886   }
887 
888   void
_list_scriptsinfo_t889   _list_scripts ()
890   {
891     if (verbose)
892     {
893       separator ();
894       printf ("Layout script information:\n\n");
895     }
896 
897     hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
898 
899     for (unsigned int i = 0; table_tags[i]; i++)
900     {
901       if (verbose) printf ("Table: ");
902       printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
903 
904       hb_tag_t script_array[32];
905       unsigned script_count = sizeof script_array / sizeof script_array[0];
906       unsigned script_offset = 0;
907       do
908       {
909 	hb_ot_layout_table_get_script_tags (face, table_tags[i],
910 					    script_offset,
911 					    &script_count,
912 					    script_array);
913 
914 	for (unsigned script_index = 0; script_index < script_count; script_index++)
915 	{
916 	  printf ("	");
917 	  if (verbose) printf ("Script: ");
918 
919 	  hb_tag_t hb_sc = hb_script_to_iso15924_tag (hb_ot_tag_to_script (script_array[script_index]));
920 	  if (script_array[script_index] == HB_TAG ('D','F','L','T'))
921 	    hb_sc = HB_SCRIPT_COMMON;
922 
923 	  printf ("%c%c%c%c (%c%c%c%c)\n",
924 		  HB_UNTAG (hb_sc),
925 		  HB_UNTAG (script_array[script_index]));
926 
927 	  hb_tag_t language_array[32];
928 	  unsigned language_count = sizeof language_array / sizeof language_array[0];
929 	  unsigned language_offset = 0;
930 	  do
931 	  {
932 	    hb_ot_layout_script_get_language_tags (face, table_tags[i],
933 						   script_offset + script_index,
934 						   language_offset,
935 						   &language_count,
936 						   language_array);
937 
938 	    for (unsigned language_index = 0; language_index < language_count; language_index++)
939 	    {
940 	      printf ("		");
941 	      if (verbose) printf ("Language: ");
942 	      printf ("%s (%c%c%c%c)\n",
943 		      hb_language_to_string (hb_ot_tag_to_language (language_array[language_index])),
944 		      HB_UNTAG (language_array[language_index]));
945 	    }
946 
947 	    language_offset += language_count;
948 	  }
949 	  while (language_count == sizeof language_array / sizeof language_array[0]);
950 	}
951 
952 	script_offset += script_count;
953       }
954       while (script_count == sizeof script_array / sizeof script_array[0]);
955 
956     }
957 
958   }
959 
960   void
_list_features_no_scriptinfo_t961   _list_features_no_script ()
962   {
963     if (verbose)
964     {
965       printf ("Showing all font features with duplicates removed.\n\n");
966     }
967 
968     hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
969 
970     hb_set_t *features = hb_set_create ();
971 
972     for (unsigned int i = 0; table_tags[i]; i++)
973     {
974       if (verbose) printf ("Table: ");
975       printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
976 
977       hb_set_clear (features);
978       hb_tag_t feature_array[32];
979       unsigned feature_count = sizeof feature_array / sizeof feature_array[0];
980       unsigned feature_offset = 0;
981       do
982       {
983 	hb_ot_layout_table_get_feature_tags (face, table_tags[i],
984 					     feature_offset,
985 					     &feature_count,
986 					     feature_array);
987 
988 	for (unsigned feature_index = 0; feature_index < feature_count; feature_index++)
989 	{
990 	  if (hb_set_has (features, feature_array[feature_index]))
991 	    continue;
992 	  hb_set_add (features, feature_array[feature_index]);
993 
994 	  hb_ot_name_id_t label_id;
995 
996 	  hb_ot_layout_feature_get_name_ids (face,
997 					     table_tags[i],
998 					     feature_offset + feature_index,
999 					     &label_id,
1000 					     nullptr,
1001 					     nullptr,
1002 					     nullptr,
1003 					     nullptr);
1004 
1005 	  char name[128];
1006 	  unsigned name_len = sizeof name;
1007 
1008 	  _hb_ot_name_get_utf8 (face, label_id,
1009 				language,
1010 				&name_len, name);
1011 
1012 	  printf ("	");
1013 	  if (verbose) printf ("Feature: ");
1014 	  printf ("%c%c%c%c", HB_UNTAG (feature_array[feature_index]));
1015 
1016 	  if (*name)
1017 	    printf ("	%s", name);
1018 
1019 	  printf ("\n");
1020 	}
1021 
1022 	feature_offset += feature_count;
1023       }
1024       while (feature_count == sizeof feature_array / sizeof feature_array[0]);
1025     }
1026 
1027     hb_set_destroy (features);
1028   }
1029 
1030   void
_list_featuresinfo_t1031   _list_features ()
1032   {
1033     if (verbose)
1034     {
1035       separator ();
1036       printf ("Layout features information:\n\n");
1037     }
1038 
1039     hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
1040 
1041     if (script == HB_SCRIPT_INVALID && !ot_script_str)
1042     {
1043       _list_features_no_script ();
1044       return;
1045     }
1046 
1047     for (unsigned int i = 0; table_tags[i]; i++)
1048     {
1049       if (verbose) printf ("Table: ");
1050       printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
1051 
1052       auto table_tag = table_tags[i];
1053 
1054       hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
1055       hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
1056       unsigned script_count = HB_OT_MAX_TAGS_PER_SCRIPT;
1057       unsigned language_count = HB_OT_MAX_TAGS_PER_LANGUAGE;
1058 
1059       hb_ot_tags_from_script_and_language (script, language,
1060 					   &script_count, script_tags,
1061 					   &language_count, language_tags);
1062 
1063       if (ot_script_str)
1064       {
1065 	script_tags[0] = hb_tag_from_string (ot_script_str, -1);
1066 	script_count = 1;
1067       }
1068       if (ot_language_str)
1069       {
1070 	language_tags[0] = hb_tag_from_string (ot_language_str, -1);
1071 	language_count = 1;
1072       }
1073 
1074       unsigned script_index;
1075       hb_tag_t chosen_script;
1076       hb_ot_layout_table_select_script (face, table_tag,
1077 					script_count, script_tags,
1078 					&script_index, &chosen_script);
1079 
1080       unsigned language_index;
1081       hb_tag_t chosen_language;
1082       hb_ot_layout_script_select_language2 (face, table_tag,
1083 					   script_index,
1084 					   language_count, language_tags,
1085 					   &language_index, &chosen_language);
1086 
1087       if (verbose)
1088       {
1089         if (chosen_script)
1090 	{
1091 	  printf ("	Script: %c%c%c%c\n", HB_UNTAG (chosen_script));
1092 	  if (chosen_language)
1093 	    printf ("	Language: %c%c%c%c\n", HB_UNTAG (chosen_language));
1094 	  else
1095 	    printf ("	Language: Default\n");
1096 	}
1097       }
1098 
1099       unsigned feature_array[32];
1100       unsigned feature_count = sizeof feature_array / sizeof feature_array[0];
1101       unsigned feature_offset = 0;
1102       do
1103       {
1104 	hb_ot_layout_language_get_feature_indexes (face, table_tag,
1105 						   script_index, language_index,
1106 						   feature_offset,
1107 						   &feature_count,
1108 						   feature_array);
1109 
1110 	for (unsigned feature_index = 0; feature_index < feature_count; feature_index++)
1111 	{
1112 	  hb_ot_name_id_t label_id;
1113 
1114 	  hb_ot_layout_feature_get_name_ids (face,
1115 					     table_tags[i],
1116 					     feature_array[feature_index],
1117 					     &label_id,
1118 					     nullptr,
1119 					     nullptr,
1120 					     nullptr,
1121 					     nullptr);
1122 
1123 	  char name[128];
1124 	  unsigned name_len = sizeof name;
1125 
1126 	  _hb_ot_name_get_utf8 (face, label_id,
1127 				language,
1128 				&name_len, name);
1129 
1130 	  printf ("	");
1131 	  if (verbose) printf ("Feature: ");
1132 	  hb_tag_t feature_tag;
1133 	  unsigned f_count = 1;
1134 	  hb_ot_layout_table_get_feature_tags (face, table_tag,
1135 					       feature_array[feature_index],
1136 					       &f_count, &feature_tag);
1137 	  printf ("%c%c%c%c", HB_UNTAG (feature_tag));
1138 
1139 	  if (*name)
1140 	    printf ("	%s", name);
1141 
1142 	  printf ("\n");
1143 	}
1144 
1145 	feature_offset += feature_count;
1146       }
1147       while (feature_count == sizeof feature_array / sizeof feature_array[0]);
1148     }
1149   }
1150 
1151 #ifndef HB_NO_VAR
1152   void
_list_variationsinfo_t1153   _list_variations ()
1154   {
1155     if (verbose)
1156     {
1157       separator ();
1158       printf ("Variations information:\n\n");
1159     }
1160 
1161     hb_ot_var_axis_info_t *axes;
1162 
1163     unsigned count = hb_ot_var_get_axis_infos (face, 0, nullptr, nullptr);
1164     axes = (hb_ot_var_axis_info_t *) calloc (count, sizeof (hb_ot_var_axis_info_t));
1165     hb_ot_var_get_axis_infos (face, 0, &count, axes);
1166 
1167     bool has_hidden = false;
1168 
1169     if (verbose && count)
1170     {
1171       printf ("Varitation axes:\n\n");
1172       printf ("Tag	Minimum	Default	Maximum	Name\n------------------------------------\n");
1173     }
1174     for (unsigned i = 0; i < count; i++)
1175     {
1176       const auto &axis = axes[i];
1177       if (axis.flags & HB_OT_VAR_AXIS_FLAG_HIDDEN)
1178 	has_hidden = true;
1179 
1180       char name[128];
1181       unsigned name_len = sizeof name;
1182 
1183       _hb_ot_name_get_utf8 (face, axis.name_id,
1184 			    language,
1185 			    &name_len, name);
1186 
1187       printf ("%c%c%c%c%s	%g	%g	%g	%s\n",
1188 	      HB_UNTAG (axis.tag),
1189 	      axis.flags & HB_OT_VAR_AXIS_FLAG_HIDDEN ? "*" : "",
1190 	      (double) axis.min_value,
1191 	      (double) axis.default_value,
1192 	      (double) axis.max_value,
1193 	      name);
1194     }
1195     if (verbose && has_hidden)
1196       printf ("\n[*] Hidden axis\n");
1197 
1198     free (axes);
1199     axes = nullptr;
1200 
1201     count = hb_ot_var_get_named_instance_count (face);
1202     if (count)
1203     {
1204       if (verbose)
1205       {
1206 	printf ("\n\nNamed instances:\n\n");
1207       printf ("Index	Name				Position\n------------------------------------------------\n");
1208       }
1209 
1210       for (unsigned i = 0; i < count; i++)
1211       {
1212 	char name[128];
1213 	unsigned name_len = sizeof name;
1214 
1215 	hb_ot_name_id_t name_id = hb_ot_var_named_instance_get_subfamily_name_id (face, i);
1216 	_hb_ot_name_get_utf8 (face, name_id,
1217 			      language,
1218 			      &name_len, name);
1219 
1220 	unsigned coords_count = hb_ot_var_named_instance_get_design_coords (face, i, nullptr, nullptr);
1221 	float* coords;
1222 	coords = (float *) calloc (coords_count, sizeof (float));
1223 	hb_ot_var_named_instance_get_design_coords (face, i, &coords_count, coords);
1224 
1225 	printf ("%u	%-32s", i, name);
1226 	for (unsigned j = 0; j < coords_count; j++)
1227 	  printf ("%g, ", (double) coords[j]);
1228 	printf ("\n");
1229 
1230 	free (coords);
1231       }
1232     }
1233   }
1234 #endif
1235 
1236 #ifdef HAVE_CHAFA
1237   GString *
_palette_chafa_strinfo_t1238   _palette_chafa_str (unsigned palette_index)
1239   {
1240     unsigned count = hb_ot_color_palette_get_colors (face, palette_index, 0,
1241 						     nullptr, nullptr);
1242 
1243     hb_color_t *palette = (hb_color_t *) malloc (count * sizeof (hb_color_t));
1244     hb_ot_color_palette_get_colors (face, palette_index, 0,
1245 				    &count, palette);
1246 
1247 #define REPEAT 16
1248     hb_color_t *data = (hb_color_t *) malloc (count * REPEAT * sizeof (hb_color_t));
1249     for (unsigned i = 0; i < count; i++)
1250       for (unsigned j = 0; j < REPEAT; j++)
1251 	data[i * REPEAT + j] = palette[i];
1252     free (palette);
1253     palette = nullptr;
1254 
1255     chafa_set_n_threads (1); // https://github.com/hpjansson/chafa/issues/125#issuecomment-1397475217
1256 			     //
1257     gchar **environ = g_get_environ ();
1258     ChafaTermInfo *term_info = chafa_term_db_detect (chafa_term_db_get_default (),
1259 						     environ);
1260 
1261     ChafaCanvasMode mode;
1262     ChafaPixelMode pixel_mode = CHAFA_PIXEL_MODE_SYMBOLS;
1263     if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_DIRECT))
1264       mode = CHAFA_CANVAS_MODE_TRUECOLOR;
1265     else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_256))
1266       mode = CHAFA_CANVAS_MODE_INDEXED_240;
1267     else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_SET_COLOR_FGBG_16))
1268       mode = CHAFA_CANVAS_MODE_INDEXED_16;
1269     else if (chafa_term_info_have_seq (term_info, CHAFA_TERM_SEQ_INVERT_COLORS))
1270       mode = CHAFA_CANVAS_MODE_FGBG_BGFG;
1271     else
1272       mode = CHAFA_CANVAS_MODE_FGBG;
1273 
1274     ChafaSymbolMap *symbol_map = chafa_symbol_map_new ();
1275     chafa_symbol_map_add_by_tags (symbol_map,
1276 				  (ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK));
1277 
1278     ChafaCanvasConfig *config = chafa_canvas_config_new ();
1279     chafa_canvas_config_set_canvas_mode (config, mode);
1280     chafa_canvas_config_set_pixel_mode (config, pixel_mode);
1281     chafa_canvas_config_set_cell_geometry (config, REPEAT, 1);
1282     chafa_canvas_config_set_geometry (config, 2 * count, 1);
1283     chafa_canvas_config_set_symbol_map (config, symbol_map);
1284     chafa_canvas_config_set_color_extractor (config, CHAFA_COLOR_EXTRACTOR_MEDIAN);
1285     chafa_canvas_config_set_work_factor (config, 1.0f);
1286 
1287     ChafaCanvas *canvas = chafa_canvas_new (config);
1288     chafa_canvas_draw_all_pixels (canvas,
1289 				  G_BYTE_ORDER == G_BIG_ENDIAN
1290 				    ? CHAFA_PIXEL_BGRA8_UNASSOCIATED
1291 				    : CHAFA_PIXEL_ARGB8_UNASSOCIATED,
1292 				  (const guint8 *) data,
1293 				  count * REPEAT,
1294 				  1,
1295 				  sizeof (hb_color_t));
1296 
1297     free (data);
1298 
1299     auto gs = chafa_canvas_print (canvas, term_info);
1300 
1301     chafa_canvas_unref (canvas);
1302     chafa_canvas_config_unref (config);
1303     chafa_symbol_map_unref (symbol_map);
1304     chafa_term_info_unref (term_info);
1305     g_strfreev (environ);
1306 
1307     return gs;
1308   }
1309 #endif
1310 
1311   void
_list_palettesinfo_t1312   _list_palettes ()
1313   {
1314     if (verbose)
1315     {
1316       separator ();
1317       printf ("Color palettes information:\n");
1318     }
1319 
1320     {
1321       if (verbose)
1322       {
1323 	printf ("\nPalettes:\n\n");
1324 	printf ("Index	Flags	Name\n--------------------\n");
1325       }
1326       unsigned count = hb_ot_color_palette_get_count (face);
1327       for (unsigned i = 0; i < count; i++)
1328       {
1329 	hb_ot_name_id_t name_id = hb_ot_color_palette_get_name_id (face, i);
1330 	hb_ot_color_palette_flags_t flags = hb_ot_color_palette_get_flags (face, i);
1331 
1332 	char name[128];
1333 	unsigned name_len = sizeof name;
1334 
1335 	_hb_ot_name_get_utf8 (face, name_id,
1336 			      language,
1337 			      &name_len, name);
1338         const char *type = "";
1339 	if (flags)
1340 	{
1341 	  if (flags & HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND)
1342           {
1343 	    if (flags & HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND)
1344 	      type = "Both";
1345             else
1346 	      type = "Light";
1347           }
1348           else {
1349 	    type = "Dark";
1350           }
1351 	}
1352 
1353 #ifdef HAVE_CHAFA
1354 	char *chafa_env = getenv ("HB_CHAFA");
1355 	bool use_chafa = !chafa_env || atoi (chafa_env);
1356 	if (verbose && use_chafa && isatty (fileno (stdout)))
1357 	{
1358 	  GString *chafa_str = _palette_chafa_str (i);
1359 	  printf ("%u	%s	%-23s	%*s\n", i, type, name,
1360 		  (int) chafa_str->len, chafa_str->str);
1361 	  g_string_free (chafa_str, TRUE);
1362 	}
1363 	else
1364 #endif
1365 	  printf ("%u	%s	%s\n", i, type, name);
1366       }
1367     }
1368 
1369     {
1370       if (verbose)
1371       {
1372 	printf ("\nColors:\n\n");
1373 	printf ("Index	Name\n------------\n");
1374       }
1375       unsigned count = hb_ot_color_palette_get_colors (face, 0, 0, nullptr, nullptr);
1376       for (unsigned i = 0; i < count; i++)
1377       {
1378 	hb_ot_name_id_t name_id = hb_ot_color_palette_color_get_name_id (face, i);
1379 
1380 	char name[128];
1381 	unsigned name_len = sizeof name;
1382 	_hb_ot_name_get_utf8 (face, name_id,
1383 			      language,
1384 			      &name_len, name);
1385 
1386 	printf ("%u	%s\n", i, name);
1387       }
1388     }
1389   }
1390 
1391   void
_list_metainfo_t1392   _list_meta ()
1393   {
1394     if (verbose)
1395     {
1396       separator ();
1397       printf ("Meta information:\n");
1398     }
1399 
1400     {
1401       if (verbose)
1402       {
1403 	printf ("\nTag	Data\n------------\n");
1404       }
1405       unsigned count = hb_ot_meta_get_entry_tags (face, 0, nullptr, nullptr);
1406       for (unsigned i = 0; i < count; i++)
1407       {
1408 	hb_ot_meta_tag_t tag;
1409 	unsigned len = 1;
1410 	hb_ot_meta_get_entry_tags (face, i, &len, &tag);
1411 
1412 	hb_blob_t *blob = hb_ot_meta_reference_entry (face, tag);
1413 
1414 	printf ("%c%c%c%c	%.*s\n", HB_UNTAG (tag),
1415 		(int) hb_blob_get_length (blob),
1416 		hb_blob_get_data (blob, nullptr));
1417 
1418 	hb_blob_destroy (blob);
1419       }
1420     }
1421   }
1422 
1423 };
1424 
1425 
1426 template <typename consumer_t,
1427 	  typename font_options_type>
1428 struct main_font_t :
1429        option_parser_t,
1430        font_options_type,
1431        consumer_t
1432 {
operator ()main_font_t1433   int operator () (int argc, char **argv)
1434   {
1435     add_options ();
1436 
1437     if (argc == 2)
1438       consumer_t::show_all = true;
1439 
1440     parse (&argc, &argv);
1441 
1442     consumer_t::operator () (this);
1443 
1444     return 0;
1445   }
1446 };
1447 
1448 int
main(int argc,char ** argv)1449 main (int argc, char **argv)
1450 {
1451   return batch_main<info_t> (argc, argv);
1452 }
1453