Imported Upstream version 8.2.2
[platform/upstream/harfbuzz.git] / util / hb-info.cc
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
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 {
67   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
153   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
230   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
242   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
339   void separator ()
340   {
341     if (first_item)
342     {
343       first_item = false;
344       return;
345     }
346     printf ("\n===\n\n");
347   }
348
349   void
350   _show_face_count ()
351   {
352     printf ("Face count: %u\n", hb_face_count (blob));
353   }
354
355   void
356   _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   }
371   void _show_family ()          { _show_name ("Family", 1); }
372   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   }
382   void _show_unique_name ()     { _show_name ("Unique name", 3); }
383   void _show_full_name ()       { _show_name ("Full name", 4); }
384   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   }
395   void _show_version ()         { _show_name ("Version", 5); }
396
397   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
405   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
437   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
452   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
462   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
472   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
487   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
496   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
510   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
537   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
582   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
602   _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
611   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
647   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
670   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
714   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
777   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
805   _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
865   _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
887   _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
959   _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
1029   _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
1151   _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 *
1236   _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
1310   _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
1390   _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 {
1431   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
1447 main (int argc, char **argv)
1448 {
1449   return batch_main<info_t> (argc, argv);
1450 }