/* * Copyright © 2019 Google, Inc. * * This is part of HarfBuzz, a text shaping library. * * Permission is hereby granted, without written agreement and without * license or royalty fees, to use, copy, modify, and distribute this * software and its documentation for any purpose, provided that the * above copyright notice and the following two paragraphs appear in * all copies of this software. * * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Google Author(s): Garret Rieger */ #include "options.hh" #include "hb-subset-input.hh" static gboolean parse_gids (const char *name G_GNUC_UNUSED, const char *arg, gpointer data, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; hb_set_t *gids = subset_opts->input->glyphs; char *s = (char *) arg; char *p; while (s && *s) { while (*s && strchr (", ", *s)) s++; if (!*s) break; errno = 0; hb_codepoint_t start_code = strtoul (s, &p, 10); if (s[0] == '-' || errno || s == p) { hb_set_destroy (gids); g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed parsing gids values at: '%s'", s); return false; } if (p && p[0] == '-') //gid ranges { s = ++p; hb_codepoint_t end_code = strtoul (s, &p, 10); if (s[0] == '-' || errno || s == p) { hb_set_destroy (gids); g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed parsing gids values at: '%s'", s); return false; } if (end_code < start_code) { hb_set_destroy (gids); g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Invalid gids range value %u-%u", start_code, end_code); return false; } hb_set_add_range (gids, start_code, end_code); } else { hb_set_add (gids, start_code); } s = p; } return true; } static gboolean parse_nameids (const char *name, const char *arg, gpointer data, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; hb_set_t *name_ids = subset_opts->input->name_ids; char last_name_char = name[strlen (name) - 1]; if (last_name_char != '+' && last_name_char != '-') hb_set_clear (name_ids); if (0 == strcmp (arg, "*")) { if (last_name_char == '-') hb_set_del_range (name_ids, 0, 0x7FFF); else hb_set_add_range (name_ids, 0, 0x7FFF); return true; } char *s = (char *) arg; char *p; while (s && *s) { while (*s && strchr (", ", *s)) s++; if (!*s) break; errno = 0; hb_codepoint_t u = strtoul (s, &p, 10); if (errno || s == p) { hb_set_destroy (name_ids); g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed parsing nameID values at: '%s'", s); return false; } if (last_name_char != '-') { hb_set_add (name_ids, u); } else { hb_set_del (name_ids, u); } s = p; } return true; } static gboolean parse_name_languages (const char *name, const char *arg, gpointer data, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; hb_set_t *name_languages = subset_opts->input->name_languages; char last_name_char = name[strlen (name) - 1]; if (last_name_char != '+' && last_name_char != '-') hb_set_clear (name_languages); if (0 == strcmp (arg, "*")) { if (last_name_char == '-') hb_set_del_range (name_languages, 0, 0x5FFF); else hb_set_add_range (name_languages, 0, 0x5FFF); return true; } char *s = (char *) arg; char *p; while (s && *s) { while (*s && strchr (", ", *s)) s++; if (!*s) break; errno = 0; hb_codepoint_t u = strtoul (s, &p, 10); if (errno || s == p) { hb_set_destroy (name_languages); g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed parsing name_languages values at: '%s'", s); return false; } if (last_name_char != '-') { hb_set_add (name_languages, u); } else { hb_set_del (name_languages, u); } s = p; } return true; } static gboolean parse_layout_features (const char *name, const char *arg, gpointer data, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; hb_set_t *layout_features = subset_opts->input->layout_features; char last_name_char = name[strlen (name) - 1]; if (last_name_char != '+' && last_name_char != '-') hb_set_clear (layout_features); if (0 == strcmp (arg, "*")) { if (last_name_char == '-') { hb_set_clear (layout_features); *subset_opts->bool_for (HB_SUBSET_FLAGS_RETAIN_ALL_FEATURES) = false; } else { *subset_opts->bool_for (HB_SUBSET_FLAGS_RETAIN_ALL_FEATURES) = true; } return true; } char *s = strtok((char *) arg, ", "); while (s) { if (strlen (s) > 4) // table tags are at most 4 bytes { g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed parsing table tag values at: '%s'", s); return false; } hb_tag_t tag = hb_tag_from_string (s, strlen (s)); if (last_name_char != '-') hb_set_add (layout_features, tag); else hb_set_del (layout_features, tag); s = strtok(nullptr, ", "); } return true; } static gboolean parse_drop_tables (const char *name, const char *arg, gpointer data, GError **error G_GNUC_UNUSED) { subset_options_t *subset_opts = (subset_options_t *) data; hb_set_t *drop_tables = subset_opts->input->drop_tables; char last_name_char = name[strlen (name) - 1]; if (last_name_char != '+' && last_name_char != '-') hb_set_clear (drop_tables); char *s = strtok((char *) arg, ", "); while (s) { if (strlen (s) > 4) // Table tags are at most 4 bytes. { g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, "Failed parsing table tag values at: '%s'", s); return false; } hb_tag_t tag = hb_tag_from_string (s, strlen (s)); if (last_name_char != '-') hb_set_add (drop_tables, tag); else hb_set_del (drop_tables, tag); s = strtok(nullptr, ", "); } return true; } void subset_options_t::add_options (option_parser_t *parser) { GOptionEntry entries[] = { {"no-hinting", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_NO_HINTING), "Whether to drop hints", nullptr}, {"retain-gids", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_RETAIN_GIDS), "If set don't renumber glyph ids in the subset.", nullptr}, {"gids", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to include in the subset", "list of comma/whitespace-separated int numbers or ranges"}, {"desubroutinize", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_DESUBROUTINIZE), "Remove CFF/CFF2 use of subroutines", nullptr}, {"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-legacy", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_NAME_LEGACY), "Keep legacy (non-Unicode) 'name' table entries", nullptr}, {"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."}, {"num-iterations", 'n', 0, G_OPTION_ARG_INT, &this->num_iterations, "Run subsetter N times (default: 1)", "N"}, {"set-overlaps-flag", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG), "Set the overlaps flag on each glyph.", nullptr}, {"notdef-outline", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_NOTDEF_OUTLINE), "Keep the outline of \'.notdef\' glyph", nullptr}, {"no-prune-unicode-ranges", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES), "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr}, {"glyph-names", 0, 0, G_OPTION_ARG_NONE, this->bool_for (HB_SUBSET_FLAGS_GLYPH_NAMES), "Keep PS glyph names in TT-flavored fonts. ", nullptr}, {nullptr} }; parser->add_group (entries, "subset", "Subset options:", "Options subsetting", this); }