1 /*
2 * Copyright © 2019 Google, Inc.
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): Garret Rieger
25 */
26
27 #include "options.hh"
28
29 #include "hb-subset-input.hh"
30
31 static gboolean
parse_nameids(const char * name,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)32 parse_nameids (const char *name,
33 const char *arg,
34 gpointer data,
35 GError **error G_GNUC_UNUSED)
36 {
37 subset_options_t *subset_opts = (subset_options_t *) data;
38 hb_set_t *name_ids = subset_opts->input->name_ids;
39
40 char last_name_char = name[strlen (name) - 1];
41
42 if (last_name_char != '+' && last_name_char != '-')
43 hb_set_clear (name_ids);
44
45 if (0 == strcmp (arg, "*"))
46 {
47 if (last_name_char == '-')
48 hb_set_del_range (name_ids, 0, 0x7FFF);
49 else
50 hb_set_add_range (name_ids, 0, 0x7FFF);
51 return true;
52 }
53
54 char *s = (char *) arg;
55 char *p;
56
57 while (s && *s)
58 {
59 while (*s && strchr (", ", *s))
60 s++;
61 if (!*s)
62 break;
63
64 errno = 0;
65 hb_codepoint_t u = strtoul (s, &p, 10);
66 if (errno || s == p)
67 {
68 hb_set_destroy (name_ids);
69 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
70 "Failed parsing nameID values at: '%s'", s);
71 return false;
72 }
73
74 if (last_name_char != '-')
75 {
76 hb_set_add (name_ids, u);
77 } else {
78 hb_set_del (name_ids, u);
79 }
80
81 s = p;
82 }
83
84 return true;
85 }
86
87 static gboolean
parse_name_languages(const char * name,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)88 parse_name_languages (const char *name,
89 const char *arg,
90 gpointer data,
91 GError **error G_GNUC_UNUSED)
92 {
93 subset_options_t *subset_opts = (subset_options_t *) data;
94 hb_set_t *name_languages = subset_opts->input->name_languages;
95
96 char last_name_char = name[strlen (name) - 1];
97
98 if (last_name_char != '+' && last_name_char != '-')
99 hb_set_clear (name_languages);
100
101 if (0 == strcmp (arg, "*"))
102 {
103 if (last_name_char == '-')
104 hb_set_del_range (name_languages, 0, 0x5FFF);
105 else
106 hb_set_add_range (name_languages, 0, 0x5FFF);
107 return true;
108 }
109
110 char *s = (char *) arg;
111 char *p;
112
113 while (s && *s)
114 {
115 while (*s && strchr (", ", *s))
116 s++;
117 if (!*s)
118 break;
119
120 errno = 0;
121 hb_codepoint_t u = strtoul (s, &p, 10);
122 if (errno || s == p)
123 {
124 hb_set_destroy (name_languages);
125 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
126 "Failed parsing name_languages values at: '%s'", s);
127 return false;
128 }
129
130 if (last_name_char != '-')
131 {
132 hb_set_add (name_languages, u);
133 } else {
134 hb_set_del (name_languages, u);
135 }
136
137 s = p;
138 }
139
140 return true;
141 }
142
143 static gboolean
parse_drop_tables(const char * name,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)144 parse_drop_tables (const char *name,
145 const char *arg,
146 gpointer data,
147 GError **error G_GNUC_UNUSED)
148 {
149 subset_options_t *subset_opts = (subset_options_t *) data;
150 hb_set_t *drop_tables = subset_opts->input->drop_tables;
151
152 char last_name_char = name[strlen (name) - 1];
153
154 if (last_name_char != '+' && last_name_char != '-')
155 hb_set_clear (drop_tables);
156
157 char *s = strtok((char *) arg, ", ");
158 while (s)
159 {
160 if (strlen (s) > 4) // Table tags are at most 4 bytes.
161 {
162 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
163 "Failed parsing table tag values at: '%s'", s);
164 return false;
165 }
166
167 hb_tag_t tag = hb_tag_from_string (s, strlen (s));
168
169 if (last_name_char != '-')
170 hb_set_add (drop_tables, tag);
171 else
172 hb_set_del (drop_tables, tag);
173
174 s = strtok(nullptr, ", ");
175 }
176
177 return true;
178 }
179
180 void
add_options(option_parser_t * parser)181 subset_options_t::add_options (option_parser_t *parser)
182 {
183 GOptionEntry entries[] =
184 {
185 {"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->input->drop_hints, "Whether to drop hints", nullptr},
186 {"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->input->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr},
187 {"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->input->desubroutinize, "Remove CFF/CFF2 use of subroutines", nullptr},
188 {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"},
189 {"name-legacy", 0, 0, G_OPTION_ARG_NONE, &this->input->name_legacy, "Keep legacy (non-Unicode) 'name' table entries", nullptr},
190 {"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"},
191 {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."},
192 {"drop-tables+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."},
193 {"drop-tables-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."},
194
195 {nullptr}
196 };
197 parser->add_group (entries,
198 "subset",
199 "Subset options:",
200 "Options subsetting",
201 this);
202 }
203