diff --git a/util/face-options.hh b/util/face-options.hh index 386b9339c..f680e05cd 100644 --- a/util/face-options.hh +++ b/util/face-options.hh @@ -36,6 +36,9 @@ struct face_options_t g_free (font_file); } + void set_face (hb_face_t *face_) + { face = face_; } + void add_options (option_parser_t *parser); void post_parse (GError **error); diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 3817dd5b2..b30614179 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -46,8 +46,37 @@ struct subset_main_t : option_parser_t, face_options_t, output_options_t hb_subset_input_destroy (input); } + void parse_face (int argc, const char * const *argv) + { + option_parser_t parser; + face_options_t face_opts; + + face_opts.add_options (&parser); + + GOptionEntry entries[] = + { + {G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN, + G_OPTION_ARG_CALLBACK, (gpointer) &collect_face, nullptr, "[FONT-FILE] [TEXT]"}, + {nullptr} + }; + parser.add_main_group (entries, &face_opts); + + g_option_context_set_ignore_unknown_options (parser.context, true); + g_option_context_set_help_enabled (parser.context, false); + + char **args = (char **) g_memdup (argv, argc * sizeof (*argv)); + parser.parse (&argc, &args, true); + g_free (args); + + set_face (face_opts.face); + } + void parse (int argc, char **argv) { + /* Do a preliminary parse to load font-face, such that we can use it + * during main option parsing. */ + parse_face (argc, argv); + add_options (); option_parser_t::parse (&argc, &argv); } @@ -96,13 +125,22 @@ struct subset_main_t : option_parser_t, face_options_t, output_options_t return true; } + void + add_all_unicodes () + { + hb_set_t *codepoints = hb_subset_input_unicode_set (input); + hb_face_collect_unicodes (face, codepoints); + } + void add_options (); - public: - void post_parse (GError **error); - protected: static gboolean + collect_face (const char *name, + const char *arg, + gpointer data, + GError **error); + static gboolean collect_rest (const char *name, const char *arg, gpointer data, @@ -112,10 +150,6 @@ struct subset_main_t : option_parser_t, face_options_t, output_options_t unsigned num_iterations = 1; hb_subset_input_t *input = nullptr; - - /* Internal, ouch. */ - bool all_unicodes = false; - GString *glyph_names = nullptr; }; static gboolean @@ -183,13 +217,37 @@ parse_glyphs (const char *name G_GNUC_UNUSED, GError **error G_GNUC_UNUSED) { subset_main_t *subset_main = (subset_main_t *) data; + hb_set_t *gids = hb_subset_input_glyph_set (subset_main->input); - if (!subset_main->glyph_names) - subset_main->glyph_names = g_string_new (nullptr); - else - g_string_append_c (subset_main->glyph_names, ' '); + const char *p = arg; + const char *p_end = arg + strlen (arg); - g_string_append (subset_main->glyph_names, arg); + hb_font_t *font = hb_font_create (subset_main->face); + while (p < p_end) + { + while (p < p_end && (*p == ' ' || *p == ',')) + p++; + + const char *end = p; + while (end < p_end && *end != ' ' && *end != ',') + end++; + + if (p < end) + { + hb_codepoint_t gid; + if (!hb_font_get_glyph_from_name (font, p, end - p, &gid)) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing glyph name: '%s'", p); + return false; + } + + hb_set_add (gids, gid); + } + + p = end + 1; + } + hb_font_destroy (font); return true; } @@ -204,7 +262,7 @@ parse_text (const char *name G_GNUC_UNUSED, if (0 == strcmp (arg, "*")) { - subset_main->all_unicodes = true; + subset_main->add_all_unicodes (); return true; } @@ -229,7 +287,7 @@ parse_unicodes (const char *name G_GNUC_UNUSED, if (0 == strcmp (arg, "*")) { - subset_main->all_unicodes = true; + subset_main->add_all_unicodes (); return true; } @@ -565,6 +623,23 @@ parse_file_for (const char *name, return true; } +gboolean +subset_main_t::collect_face (const char *name, + const char *arg, + gpointer data, + GError **error) +{ + face_options_t *thiz = (face_options_t *) data; + + if (!thiz->font_file) + { + thiz->font_file = g_strdup (arg); + return true; + } + + return true; +} + gboolean subset_main_t::collect_rest (const char *name, const char *arg, @@ -592,14 +667,14 @@ subset_main_t::add_options () GOptionEntry glyphset_entries[] = { - {"gids", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to include in the subset", "list of glyph indices/ranges"}, - {"gids-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read glyph IDs or ranges from", "filename"}, - {"glyphs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset", "list of glyph names"}, - {"glyphs-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read glyph names fromt", "filename"}, - {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset", "string"}, - {"text-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read text from", "filename"}, - {"unicodes", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to include in the subset", "list of hex numbers/ranges"}, - {"unicodes-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read Unicode codepoints or ranges from", "filename"}, + {"gids", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to include in the subset", "list of glyph indices/ranges"}, + {"gids-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read glyph IDs or ranges from", "filename"}, + {"glyphs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset", "list of glyph names"}, + {"glyphs-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for, "Specify file to read glyph names fromt", "filename"}, + {"text", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset", "string"}, + {"text-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for,"Specify file to read text from", "filename"}, + {"unicodes", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to include in the subset", "list of hex numbers/ranges"}, + {"unicodes-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for,"Specify file to read Unicode codepoints or ranges from", "filename"}, {nullptr} }; add_group (glyphset_entries, @@ -610,18 +685,18 @@ subset_main_t::add_options () GOptionEntry other_entries[] = { - {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, - {"name-IDs-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, - {"name-IDs+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, - {"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"}, - {"name-languages-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"}, - {"name-languages+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"}, - {"layout-features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags."}, - {"layout-features+",0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags."}, - {"layout-features-",0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags."}, - {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, - {"drop-tables+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, - {"drop-tables-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, + {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, + {"name-IDs-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, + {"name-IDs+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, + {"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"}, + {"name-languages-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"}, + {"name-languages+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"}, + {"layout-features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags."}, + {"layout-features+",0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags."}, + {"layout-features-",0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags."}, + {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, + {"drop-tables+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, + {"drop-tables-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, {nullptr} }; add_group (other_entries, @@ -632,15 +707,14 @@ subset_main_t::add_options () GOptionEntry flag_entries[] = { - {"no-hinting", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Whether to drop hints", nullptr}, - {"retain-gids", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "If set don't renumber glyph ids in the subset.", nullptr}, - {"desubroutinize", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Remove CFF/CFF2 use of subroutines", nullptr}, - {"name-legacy", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep legacy (non-Unicode) 'name' table entries", nullptr}, - {"set-overlaps-flag", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, - "Set the overlaps flag on each glyph.", nullptr}, - {"notdef-outline", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep the outline of \'.notdef\' glyph", nullptr}, - {"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr}, - {"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep PS glyph names in TT-flavored fonts. ", nullptr}, + {"no-hinting", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Whether to drop hints", nullptr}, + {"retain-gids", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "If set don't renumber glyph ids in the subset.", nullptr}, + {"desubroutinize", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Remove CFF/CFF2 use of subroutines", nullptr}, + {"name-legacy", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep legacy (non-Unicode) 'name' table entries", nullptr}, + {"set-overlaps-flag", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Set the overlaps flag on each glyph.", nullptr}, + {"notdef-outline", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep the outline of \'.notdef\' glyph", nullptr}, + {"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr}, + {"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag, "Keep PS glyph names in TT-flavored fonts. ", nullptr}, {nullptr} }; add_group (flag_entries, @@ -674,58 +748,6 @@ subset_main_t::add_options () option_parser_t::add_options (); } -void -subset_main_t::post_parse (GError **error) -{ - /* This WILL get called multiple times. Oh well... */ - - if (all_unicodes) - { - hb_set_t *codepoints = hb_subset_input_unicode_set (input); - hb_face_collect_unicodes (face, codepoints); - all_unicodes = false; - } - - if (glyph_names) - { - char *p = glyph_names->str; - char *p_end = p + glyph_names->len; - - hb_set_t *gids = hb_subset_input_glyph_set (input); - - hb_font_t *font = hb_font_create (face); - while (p < p_end) - { - while (p < p_end && (*p == ' ' || *p == ',')) - p++; - - char *end = p; - while (end < p_end && *end != ' ' && *end != ',') - end++; - *end = '\0'; - - if (p < end) - { - hb_codepoint_t gid; - if (!hb_font_get_glyph_from_name (font, p, -1, &gid)) - { - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "Failed parsing glyph name: '%s'", p); - return; - } - - hb_set_add (gids, gid); - } - - p = end + 1; - } - hb_font_destroy (font); - - g_string_free (glyph_names, false); - glyph_names = nullptr; - } -} - int main (int argc, char **argv) { diff --git a/util/options.hh b/util/options.hh index b3be16d66..acb341fcb 100644 --- a/util/options.hh +++ b/util/options.hh @@ -126,7 +126,8 @@ struct option_parser_t GOptionGroup *group = g_option_group_new (nullptr, nullptr, nullptr, static_cast(closure), nullptr); g_option_group_add_entries (group, entries); - g_option_group_set_parse_hooks (group, nullptr, post_parse); + /* https://gitlab.gnome.org/GNOME/glib/-/issues/2460 */ + //g_option_group_set_parse_hooks (group, nullptr, post_parse); g_option_context_set_main_group (context, group); } @@ -143,10 +144,10 @@ struct option_parser_t g_ptr_array_add (to_free, p); } - void parse (int *argc, char ***argv); + bool parse (int *argc, char ***argv, bool ignore_error = false); - protected: GOptionContext *context; + protected: GPtrArray *to_free; }; @@ -195,8 +196,8 @@ option_parser_t::add_options () g_option_context_add_main_entries (context, entries, nullptr); } -inline void -option_parser_t::parse (int *argc, char ***argv) +inline bool +option_parser_t::parse (int *argc, char ***argv, bool ignore_error) { setlocale (LC_ALL, ""); @@ -205,12 +206,18 @@ option_parser_t::parse (int *argc, char ***argv) { if (parse_error) { - fail (true, "%s", parse_error->message); - //g_error_free (parse_error); + if (!ignore_error) + fail (true, "%s", parse_error->message); + g_error_free (parse_error); } else - fail (true, "Option parse error"); + { + if (!ignore_error) + fail (true, "Option parse error"); + } + return false; } + return true; }