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_gids(const char * name G_GNUC_UNUSED,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)32 parse_gids (const char *name G_GNUC_UNUSED,
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 *gids = subset_opts->input->glyphs;
39
40 char *s = (char *) arg;
41 char *p;
42
43 while (s && *s)
44 {
45 while (*s && strchr (", ", *s))
46 s++;
47 if (!*s)
48 break;
49
50 errno = 0;
51 hb_codepoint_t start_code = strtoul (s, &p, 10);
52 if (s[0] == '-' || errno || s == p)
53 {
54 hb_set_destroy (gids);
55 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
56 "Failed parsing gids values at: '%s'", s);
57 return false;
58 }
59
60 if (p && p[0] == '-') //gid ranges
61 {
62 s = ++p;
63 hb_codepoint_t end_code = strtoul (s, &p, 10);
64 if (s[0] == '-' || errno || s == p)
65 {
66 hb_set_destroy (gids);
67 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
68 "Failed parsing gids values at: '%s'", s);
69 return false;
70 }
71
72 if (end_code < start_code)
73 {
74 hb_set_destroy (gids);
75 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
76 "Invalid gids range value %u-%u", start_code, end_code);
77 return false;
78 }
79 hb_set_add_range (gids, start_code, end_code);
80 }
81 else
82 {
83 hb_set_add (gids, start_code);
84 }
85 s = p;
86 }
87
88 return true;
89 }
90
91 static gboolean
parse_nameids(const char * name,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)92 parse_nameids (const char *name,
93 const char *arg,
94 gpointer data,
95 GError **error G_GNUC_UNUSED)
96 {
97 subset_options_t *subset_opts = (subset_options_t *) data;
98 hb_set_t *name_ids = subset_opts->input->name_ids;
99
100 char last_name_char = name[strlen (name) - 1];
101
102 if (last_name_char != '+' && last_name_char != '-')
103 hb_set_clear (name_ids);
104
105 if (0 == strcmp (arg, "*"))
106 {
107 if (last_name_char == '-')
108 hb_set_del_range (name_ids, 0, 0x7FFF);
109 else
110 hb_set_add_range (name_ids, 0, 0x7FFF);
111 return true;
112 }
113
114 char *s = (char *) arg;
115 char *p;
116
117 while (s && *s)
118 {
119 while (*s && strchr (", ", *s))
120 s++;
121 if (!*s)
122 break;
123
124 errno = 0;
125 hb_codepoint_t u = strtoul (s, &p, 10);
126 if (errno || s == p)
127 {
128 hb_set_destroy (name_ids);
129 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
130 "Failed parsing nameID values at: '%s'", s);
131 return false;
132 }
133
134 if (last_name_char != '-')
135 {
136 hb_set_add (name_ids, u);
137 } else {
138 hb_set_del (name_ids, u);
139 }
140
141 s = p;
142 }
143
144 return true;
145 }
146
147 static gboolean
parse_name_languages(const char * name,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)148 parse_name_languages (const char *name,
149 const char *arg,
150 gpointer data,
151 GError **error G_GNUC_UNUSED)
152 {
153 subset_options_t *subset_opts = (subset_options_t *) data;
154 hb_set_t *name_languages = subset_opts->input->name_languages;
155
156 char last_name_char = name[strlen (name) - 1];
157
158 if (last_name_char != '+' && last_name_char != '-')
159 hb_set_clear (name_languages);
160
161 if (0 == strcmp (arg, "*"))
162 {
163 if (last_name_char == '-')
164 hb_set_del_range (name_languages, 0, 0x5FFF);
165 else
166 hb_set_add_range (name_languages, 0, 0x5FFF);
167 return true;
168 }
169
170 char *s = (char *) arg;
171 char *p;
172
173 while (s && *s)
174 {
175 while (*s && strchr (", ", *s))
176 s++;
177 if (!*s)
178 break;
179
180 errno = 0;
181 hb_codepoint_t u = strtoul (s, &p, 10);
182 if (errno || s == p)
183 {
184 hb_set_destroy (name_languages);
185 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
186 "Failed parsing name_languages values at: '%s'", s);
187 return false;
188 }
189
190 if (last_name_char != '-')
191 {
192 hb_set_add (name_languages, u);
193 } else {
194 hb_set_del (name_languages, u);
195 }
196
197 s = p;
198 }
199
200 return true;
201 }
202
203 static gboolean
parse_drop_tables(const char * name,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)204 parse_drop_tables (const char *name,
205 const char *arg,
206 gpointer data,
207 GError **error G_GNUC_UNUSED)
208 {
209 subset_options_t *subset_opts = (subset_options_t *) data;
210 hb_set_t *drop_tables = subset_opts->input->drop_tables;
211
212 char last_name_char = name[strlen (name) - 1];
213
214 if (last_name_char != '+' && last_name_char != '-')
215 hb_set_clear (drop_tables);
216
217 char *s = strtok((char *) arg, ", ");
218 while (s)
219 {
220 if (strlen (s) > 4) // Table tags are at most 4 bytes.
221 {
222 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
223 "Failed parsing table tag values at: '%s'", s);
224 return false;
225 }
226
227 hb_tag_t tag = hb_tag_from_string (s, strlen (s));
228
229 if (last_name_char != '-')
230 hb_set_add (drop_tables, tag);
231 else
232 hb_set_del (drop_tables, tag);
233
234 s = strtok(nullptr, ", ");
235 }
236
237 return true;
238 }
239
240 void
add_options(option_parser_t * parser)241 subset_options_t::add_options (option_parser_t *parser)
242 {
243 GOptionEntry entries[] =
244 {
245 {"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->input->drop_hints, "Whether to drop hints", nullptr},
246 {"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->input->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr},
247 {"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"},
248 {"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->input->desubroutinize, "Remove CFF/CFF2 use of subroutines", nullptr},
249 {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"},
250 {"name-legacy", 0, 0, G_OPTION_ARG_NONE, &this->input->name_legacy, "Keep legacy (non-Unicode) 'name' table entries", nullptr},
251 {"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers"},
252 {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."},
253 {"drop-tables+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."},
254 {"drop-tables-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."},
255
256 {nullptr}
257 };
258 parser->add_group (entries,
259 "subset",
260 "Subset options:",
261 "Options subsetting",
262 this);
263 }
264