2 * Copyright © 2023 Behdad Esfahbod
4 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
24 * Google Author(s): Behdad Esfahbod
28 #include "font-options.hh"
31 #include <hb-gobject.h>
38 const unsigned DEFAULT_FONT_SIZE = FONT_SIZE_UPEM;
39 const unsigned SUBPIXEL_BITS = 0;
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 */,
48 static hb_language_t en = hb_language_from_string ("en", -1);
50 unsigned len = *text_size;
51 if (!hb_ot_name_get_utf8 (face, name_id,
56 hb_ot_name_get_utf8 (face, name_id,
69 font_options_t::add_options (this);
71 GOptionEntry misc_entries[] =
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'"},
81 add_group (misc_entries,
83 "Miscellaneous options:",
84 "Miscellaneous options affecting queries",
86 false /* We add below. */);
88 GOptionEntry query_entries[] =
90 {"all", 'a', 0, G_OPTION_ARG_NONE, &this->all, "Show everything", nullptr},
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},
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'"},
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},
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},
126 {"list-variations",0, 0, G_OPTION_ARG_NONE, &this->list_variations, "List variations", nullptr},
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},
133 add_group (query_entries,
136 "Options to query the font instance",
140 GOptionEntry entries[] =
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]"},
147 add_main_group (entries, this);
149 option_parser_t::add_options ();
153 collect_rest (const char *name G_GNUC_UNUSED,
158 info_t *thiz = (info_t *) data;
160 if (!thiz->font_file)
162 thiz->font_file = g_strdup (arg);
166 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
167 "Too many arguments on the command line");
174 hb_bool_t verbose = true;
175 hb_bool_t first_item = true;
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;
186 hb_bool_t all = false;
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;
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;
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;
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;
222 hb_bool_t list_variations = false;
224 hb_bool_t list_palettes = false;
225 hb_bool_t list_meta = false;
230 post_parse (GError **error)
233 direction = hb_direction_from_string (direction_str, -1);
235 script = hb_script_from_string (script_str, -1);
236 language = hb_language_get_default ();
238 language = hb_language_from_string (language_str, -1);
242 operator () (int argc, char **argv)
249 parse (&argc, &argv);
265 show_postscript_name =
279 #ifdef HB_HAS_GOBJECT
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 ();
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 ();
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 ();
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 ();
329 if (list_variations) _list_variations ();
331 if (list_palettes) _list_palettes ();
332 if (list_meta) _list_meta ();
346 printf ("\n===\n\n");
352 printf ("Face count: %u\n", hb_face_count (blob));
356 _show_name (const char *label, hb_ot_name_id_t name_id)
360 printf ("%s: ", label);
364 unsigned name_len = sizeof name;
365 _hb_ot_name_get_utf8 (face, name_id,
369 printf ("%s\n", name);
371 void _show_family () { _show_name ("Family", 1); }
372 void _show_subfamily ()
374 hb_ot_name_id_t name_id = 2;
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);
380 _show_name ("Subfamily", name_id);
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 ()
386 hb_ot_name_id_t name_id = 6;
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);
393 _show_name ("Postscript name", name_id);
395 void _show_version () { _show_name ("Version", 5); }
397 bool _has_blob (hb_tag_t tag)
399 hb_blob_t *blob = hb_face_reference_table (face, tag);
400 bool ret = hb_blob_get_length (blob);
401 hb_blob_destroy (blob);
405 void _show_technology ()
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");
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");
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");
424 if (_has_blob (HB_TAG('E','B','D','T')))
425 printf ("Has monochrome bitmaps\n");
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");
434 if (_has_blob (HB_TAG('f','v','a','r'))) printf ("Has variations\n");
437 void _show_unicode_count ()
441 printf ("Unicode count: ");
444 hb_set_t *unicodes = hb_set_create ();
445 hb_face_collect_unicodes (face, unicodes);
447 printf ("%u\n", hb_set_get_population (unicodes));
449 hb_set_destroy (unicodes);
452 void _show_glyph_count ()
456 printf ("Glyph count: ");
459 printf ("%u\n", hb_face_get_glyph_count (face));
466 printf ("Units-Per-EM: ");
469 printf ("%u\n", hb_face_get_upem (face));
472 void _show_extents ()
474 hb_font_extents_t extents;
475 hb_font_get_extents_for_direction (font, direction, &extents);
477 if (verbose) printf ("Ascender: ");
478 printf ("%d\n", extents.ascender);
480 if (verbose) printf ("Descender: ");
481 printf ("%d\n", extents.descender);
483 if (verbose) printf ("Line gap: ");
484 printf ("%d\n", extents.line_gap);
489 for (char **p = get_name; *p; p++)
491 hb_ot_name_id_t name_id = (hb_ot_name_id_t) atoi (*p);
492 _show_name (*p, name_id);
498 for (char **p = get_style; *p; p++)
500 hb_style_tag_t tag = (hb_style_tag_t) hb_tag_from_string (*p, -1);
503 printf ("Style %c%c%c%c: ", HB_UNTAG (tag));
505 float v = hb_style_get_value (font, tag);
506 printf ("%g\n", (double) v);
512 bool fallback = false;
513 for (char **p = get_metric; *p; p++)
515 hb_ot_metrics_tag_t tag = (hb_ot_metrics_tag_t) hb_tag_from_string (*p, -1);
516 hb_position_t position;
519 printf ("Metric %c%c%c%c: ", HB_UNTAG (tag));
521 if (hb_ot_metrics_get_position (font, tag, &position))
522 printf ("%d \n", position);
525 hb_ot_metrics_get_position_with_fallback (font, tag, &position);
526 printf ("%d *\n", position);
531 if (verbose && fallback)
533 printf ("\n[*] Fallback value\n");
537 void _get_baseline ()
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;
544 hb_ot_tags_from_script_and_language (script, language,
545 &script_count, script_tags,
546 &language_count, language_tags);
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;
552 script_tag = hb_tag_from_string (ot_script_str, -1);
554 language_tag = hb_tag_from_string (ot_language_str, -1);
557 bool fallback = false;
558 for (char **p = get_baseline; *p; p++)
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;
564 printf ("Baseline %c%c%c%c: ", HB_UNTAG (tag));
566 if (hb_ot_layout_get_baseline (font, tag, direction, script_tag, language_tag, &position))
567 printf ("%d \n", position);
570 hb_ot_layout_get_baseline_with_fallback (font, tag, direction, script_tag, language_tag, &position);
571 printf ("%d *\n", position);
576 if (verbose && fallback)
578 printf ("\n[*] Fallback value\n");
584 for (char **p = get_meta; *p; p++)
586 hb_ot_meta_tag_t tag = (hb_ot_meta_tag_t) hb_tag_from_string (*p, -1);
588 hb_blob_t *blob = hb_ot_meta_reference_entry (face, tag);
591 printf ("Meta %c%c%c%c: ", HB_UNTAG (tag));
594 (int) hb_blob_get_length (blob),
595 hb_blob_get_data (blob, nullptr));
597 hb_blob_destroy (blob);
604 hb_blob_t *blob = hb_face_reference_table (face, hb_tag_from_string (get_table, -1));
606 const char *data = hb_blob_get_data (blob, &count);
607 fwrite (data, 1, count, stdout);
608 hb_blob_destroy (blob);
616 printf ("Name information:\n\n");
617 printf ("Id: Name Text\n------------------------------------\n");
620 #ifdef HB_HAS_GOBJECT
621 GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_NAME_ID_PREDEFINED);
625 const auto *entries = hb_ot_name_list_names (face, &count);
626 for (unsigned i = 0; i < count; i++)
629 unsigned name_len = sizeof name;
630 _hb_ot_name_get_utf8 (face, entries[i].name_id,
634 #ifdef HB_HAS_GOBJECT
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);
642 printf ("%u %s\n", entries[i].name_id, name);
646 #ifdef HB_HAS_GOBJECT
652 printf ("Style information:\n\n");
653 printf ("Tag: Name Value\n---------------------------------------------\n");
656 GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_STYLE_TAG);
658 unsigned count = enum_class->n_values;
659 const auto *entries = enum_class->values;
660 for (unsigned i = 0; i < count; i++)
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));
665 printf (": %-33s", entries[i].value_nick);
666 printf (" %g\n", (double) v);
670 void _list_metrics ()
675 printf ("Metrics information:\n\n");
676 printf ("Tag: Name Value\n---------------------------------------------\n");
679 GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_METRICS_TAG);
681 bool any_fallback = false;
683 unsigned count = enum_class->n_values;
684 const auto *entries = enum_class->values;
685 for (unsigned i = 0; i < count; i++)
687 bool fallback = false;
689 if (!hb_ot_metrics_get_position (font,
690 (hb_ot_metrics_tag_t) entries[i].value,
693 hb_ot_metrics_get_position_with_fallback (font,
694 (hb_ot_metrics_tag_t) entries[i].value,
696 any_fallback = fallback = true;
698 printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
700 printf (": %-33s", entries[i].value_nick);
708 if (verbose && any_fallback)
710 printf ("\n[*] Fallback value\n");
714 void _list_baselines ()
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;
721 hb_ot_tags_from_script_and_language (script, language,
722 &script_count, script_tags,
723 &language_count, language_tags);
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;
729 script_tag = hb_tag_from_string (ot_script_str, -1);
731 language_tag = hb_tag_from_string (ot_language_str, -1);
737 printf ("Baselines information:\n\n");
738 printf ("Tag: Name Value\n---------------------------------------------\n");
741 GEnumClass *enum_class = (GEnumClass *) g_type_class_ref ((GType) HB_GOBJECT_TYPE_OT_LAYOUT_BASELINE_TAG);
743 bool any_fallback = false;
745 unsigned count = enum_class->n_values;
746 const auto *entries = enum_class->values;
747 for (unsigned i = 0; i < count; i++)
749 bool fallback = false;
751 if (!hb_ot_layout_get_baseline (font, (hb_ot_layout_baseline_tag_t) entries[i].value,
752 direction, script_tag, language_tag,
755 hb_ot_layout_get_baseline_with_fallback (font, (hb_ot_layout_baseline_tag_t) entries[i].value,
756 direction, script_tag, language_tag,
758 any_fallback = fallback = true;
760 printf ("%c%c%c%c", HB_UNTAG(entries[i].value));
762 printf (": %-33s", entries[i].value_nick);
770 if (verbose && any_fallback)
772 printf ("\n[*] Fallback value\n");
782 printf ("Table information:\n\n");
783 printf ("Tag Size\n------------\n");
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);
790 for (unsigned i = 0; i < count; i++)
792 hb_tag_t tag = tags[i];
794 hb_blob_t *blob = hb_face_reference_table (face, tag);
796 printf ("%c%c%c%c %8u bytes\n", HB_UNTAG (tag), hb_blob_get_length (blob));
798 hb_blob_destroy (blob);
810 printf ("Character-set information:\n\n");
811 printf ("Unicode Glyph name\n------------------\n");
814 hb_set_t *unicodes = hb_set_create ();
815 hb_map_t *cmap = hb_map_create ();
817 hb_face_collect_nominal_glyph_mapping (face, cmap, unicodes);
819 for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
820 hb_set_next (unicodes, &u);)
822 hb_codepoint_t gid = hb_map_get (cmap, u);
825 hb_font_glyph_to_string (font, gid,
826 glyphname, sizeof glyphname);
828 printf ("U+%04X %s\n", u, glyphname);
831 hb_map_destroy (cmap);
834 /* List variation-selector sequences. */
835 hb_set_t *vars = hb_set_create ();
837 hb_face_collect_variation_selectors (face, vars);
839 for (hb_codepoint_t vs = HB_SET_VALUE_INVALID;
840 hb_set_next (vars, &vs);)
842 hb_set_clear (unicodes);
843 hb_face_collect_variation_unicodes (face, vs, unicodes);
845 for (hb_codepoint_t u = HB_SET_VALUE_INVALID;
846 hb_set_next (unicodes, &u);)
848 hb_codepoint_t gid = 0;
849 HB_UNUSED bool b = hb_font_get_variation_glyph (font, u, vs, &gid);
853 hb_font_glyph_to_string (font, gid,
854 glyphname, sizeof glyphname);
856 printf ("U+%04X,U+%04X %s\n", vs, u, glyphname);
860 hb_set_destroy (vars);
861 hb_set_destroy (unicodes);
870 printf ("Glyph-set information:\n\n");
871 printf ("GlyphID Glyph name\n------------------\n");
874 unsigned num_glyphs = hb_face_get_glyph_count (face);
876 for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
879 hb_font_glyph_to_string (font, gid,
880 glyphname, sizeof glyphname);
882 printf ("%u %s\n", gid, glyphname);
892 printf ("Layout script information:\n\n");
895 hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
897 for (unsigned int i = 0; table_tags[i]; i++)
899 if (verbose) printf ("Table: ");
900 printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
902 hb_tag_t script_array[32];
903 unsigned script_count = sizeof script_array / sizeof script_array[0];
904 unsigned script_offset = 0;
907 hb_ot_layout_table_get_script_tags (face, table_tags[i],
912 for (unsigned script_index = 0; script_index < script_count; script_index++)
915 if (verbose) printf ("Script: ");
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;
921 printf ("%c%c%c%c (%c%c%c%c)\n",
923 HB_UNTAG (script_array[script_index]));
925 hb_tag_t language_array[32];
926 unsigned language_count = sizeof language_array / sizeof language_array[0];
927 unsigned language_offset = 0;
930 hb_ot_layout_script_get_language_tags (face, table_tags[i],
931 script_offset + script_index,
936 for (unsigned language_index = 0; language_index < language_count; language_index++)
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]));
945 language_offset += language_count;
947 while (language_count == sizeof language_array / sizeof language_array[0]);
950 script_offset += script_count;
952 while (script_count == sizeof script_array / sizeof script_array[0]);
959 _list_features_no_script ()
963 printf ("Showing all font features with duplicates removed.\n\n");
966 hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
968 hb_set_t *features = hb_set_create ();
970 for (unsigned int i = 0; table_tags[i]; i++)
972 if (verbose) printf ("Table: ");
973 printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
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;
981 hb_ot_layout_table_get_feature_tags (face, table_tags[i],
986 for (unsigned feature_index = 0; feature_index < feature_count; feature_index++)
988 if (hb_set_has (features, feature_array[feature_index]))
990 hb_set_add (features, feature_array[feature_index]);
992 hb_ot_name_id_t label_id;
994 hb_ot_layout_feature_get_name_ids (face,
996 feature_offset + feature_index,
1004 unsigned name_len = sizeof name;
1006 _hb_ot_name_get_utf8 (face, label_id,
1011 if (verbose) printf ("Feature: ");
1012 printf ("%c%c%c%c", HB_UNTAG (feature_array[feature_index]));
1015 printf (" %s", name);
1020 feature_offset += feature_count;
1022 while (feature_count == sizeof feature_array / sizeof feature_array[0]);
1025 hb_set_destroy (features);
1034 printf ("Layout features information:\n\n");
1037 hb_tag_t table_tags[] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS, HB_TAG_NONE};
1039 if (script == HB_SCRIPT_INVALID && !ot_script_str)
1041 _list_features_no_script ();
1045 for (unsigned int i = 0; table_tags[i]; i++)
1047 if (verbose) printf ("Table: ");
1048 printf ("%c%c%c%c\n", HB_UNTAG (table_tags[i]));
1050 auto table_tag = table_tags[i];
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;
1057 hb_ot_tags_from_script_and_language (script, language,
1058 &script_count, script_tags,
1059 &language_count, language_tags);
1063 script_tags[0] = hb_tag_from_string (ot_script_str, -1);
1066 if (ot_language_str)
1068 language_tags[0] = hb_tag_from_string (ot_language_str, -1);
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);
1078 unsigned language_index;
1079 hb_tag_t chosen_language;
1080 hb_ot_layout_script_select_language2 (face, table_tag,
1082 language_count, language_tags,
1083 &language_index, &chosen_language);
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));
1093 printf (" Language: Default\n");
1097 unsigned feature_array[32];
1098 unsigned feature_count = sizeof feature_array / sizeof feature_array[0];
1099 unsigned feature_offset = 0;
1102 hb_ot_layout_language_get_feature_indexes (face, table_tag,
1103 script_index, language_index,
1108 for (unsigned feature_index = 0; feature_index < feature_count; feature_index++)
1110 hb_ot_name_id_t label_id;
1112 hb_ot_layout_feature_get_name_ids (face,
1114 feature_array[feature_index],
1122 unsigned name_len = sizeof name;
1124 _hb_ot_name_get_utf8 (face, label_id,
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));
1138 printf (" %s", name);
1143 feature_offset += feature_count;
1145 while (feature_count == sizeof feature_array / sizeof feature_array[0]);
1156 printf ("Variations information:\n\n");
1159 hb_ot_var_axis_info_t *axes;
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);
1165 bool has_hidden = false;
1167 if (verbose && count)
1169 printf ("Varitation axes:\n\n");
1170 printf ("Tag Minimum Default Maximum Name\n------------------------------------\n");
1172 for (unsigned i = 0; i < count; i++)
1174 const auto &axis = axes[i];
1175 if (axis.flags & HB_OT_VAR_AXIS_FLAG_HIDDEN)
1179 unsigned name_len = sizeof name;
1181 _hb_ot_name_get_utf8 (face, axis.name_id,
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,
1193 if (verbose && has_hidden)
1194 printf ("\n[*] Hidden axis\n");
1199 count = hb_ot_var_get_named_instance_count (face);
1204 printf ("\n\nNamed instances:\n\n");
1205 printf ("Index Name Position\n------------------------------------------------\n");
1208 for (unsigned i = 0; i < count; i++)
1211 unsigned name_len = sizeof name;
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,
1218 unsigned coords_count = hb_ot_var_named_instance_get_design_coords (face, i, nullptr, nullptr);
1220 coords = (float *) calloc (coords_count, sizeof (float));
1221 hb_ot_var_named_instance_get_design_coords (face, i, &coords_count, coords);
1223 printf ("%u %-32s", i, name);
1224 for (unsigned j = 0; j < coords_count; j++)
1225 printf ("%g, ", (double) coords[j]);
1236 _palette_chafa_str (unsigned palette_index)
1238 unsigned count = hb_ot_color_palette_get_colors (face, palette_index, 0,
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,
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];
1253 chafa_set_n_threads (1); // https://github.com/hpjansson/chafa/issues/125#issuecomment-1397475217
1255 gchar **environ = g_get_environ ();
1256 ChafaTermInfo *term_info = chafa_term_db_detect (chafa_term_db_get_default (),
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;
1270 mode = CHAFA_CANVAS_MODE_FGBG;
1272 ChafaSymbolMap *symbol_map = chafa_symbol_map_new ();
1273 chafa_symbol_map_add_by_tags (symbol_map,
1274 (ChafaSymbolTags) (CHAFA_SYMBOL_TAG_BLOCK));
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);
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,
1293 sizeof (hb_color_t));
1297 auto gs = chafa_canvas_print (canvas, term_info);
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);
1315 printf ("Color palettes information:\n");
1321 printf ("\nPalettes:\n\n");
1322 printf ("Index Flags Name\n--------------------\n");
1324 unsigned count = hb_ot_color_palette_get_count (face);
1325 for (unsigned i = 0; i < count; i++)
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);
1331 unsigned name_len = sizeof name;
1333 _hb_ot_name_get_utf8 (face, name_id,
1336 const char *type = "";
1339 if (flags & HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_LIGHT_BACKGROUND)
1341 if (flags & HB_OT_COLOR_PALETTE_FLAG_USABLE_WITH_DARK_BACKGROUND)
1352 char *chafa_env = getenv ("HB_CHAFA");
1353 bool use_chafa = !chafa_env || atoi (chafa_env);
1354 if (verbose && use_chafa && isatty (fileno (stdout)))
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);
1363 printf ("%u %s %s\n", i, type, name);
1370 printf ("\nColors:\n\n");
1371 printf ("Index Name\n------------\n");
1373 unsigned count = hb_ot_color_palette_get_colors (face, 0, 0, nullptr, nullptr);
1374 for (unsigned i = 0; i < count; i++)
1376 hb_ot_name_id_t name_id = hb_ot_color_palette_color_get_name_id (face, i);
1379 unsigned name_len = sizeof name;
1380 _hb_ot_name_get_utf8 (face, name_id,
1384 printf ("%u %s\n", i, name);
1395 printf ("Meta information:\n");
1401 printf ("\nTag Data\n------------\n");
1403 unsigned count = hb_ot_meta_get_entry_tags (face, 0, nullptr, nullptr);
1404 for (unsigned i = 0; i < count; i++)
1406 hb_ot_meta_tag_t tag;
1408 hb_ot_meta_get_entry_tags (face, i, &len, &tag);
1410 hb_blob_t *blob = hb_ot_meta_reference_entry (face, tag);
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));
1416 hb_blob_destroy (blob);
1424 template <typename consumer_t,
1425 typename font_options_type>
1426 struct main_font_t :
1431 int operator () (int argc, char **argv)
1436 consumer_t::show_all = true;
1438 parse (&argc, &argv);
1440 consumer_t::operator () (this);
1447 main (int argc, char **argv)
1449 return batch_main<info_t> (argc, argv);