Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/harfbuzz/util/hb-subset.cc @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /* | |
| 2 * Copyright © 2010 Behdad Esfahbod | |
| 3 * Copyright © 2011,2012 Google, Inc. | |
| 4 * | |
| 5 * This is part of HarfBuzz, a text shaping library. | |
| 6 * | |
| 7 * Permission is hereby granted, without written agreement and without | |
| 8 * license or royalty fees, to use, copy, modify, and distribute this | |
| 9 * software and its documentation for any purpose, provided that the | |
| 10 * above copyright notice and the following two paragraphs appear in | |
| 11 * all copies of this software. | |
| 12 * | |
| 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | |
| 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
| 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | |
| 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
| 17 * DAMAGE. | |
| 18 * | |
| 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
| 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
| 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | |
| 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
| 24 * | |
| 25 * Google Author(s): Garret Rieger, Rod Sheeter | |
| 26 */ | |
| 27 | |
| 28 #include "batch.hh" | |
| 29 #include "face-options.hh" | |
| 30 #include "main-font-text.hh" | |
| 31 #include "output-options.hh" | |
| 32 | |
| 33 #include <hb-subset.h> | |
| 34 | |
| 35 static hb_face_t* preprocess_face(hb_face_t* face) | |
| 36 { | |
| 37 return hb_subset_preprocess (face); | |
| 38 } | |
| 39 | |
| 40 /* | |
| 41 * Command line interface to the harfbuzz font subsetter. | |
| 42 */ | |
| 43 | |
| 44 struct subset_main_t : option_parser_t, face_options_t, output_options_t<false> | |
| 45 { | |
| 46 subset_main_t () | |
| 47 : input (hb_subset_input_create_or_fail ()) | |
| 48 {} | |
| 49 ~subset_main_t () | |
| 50 { | |
| 51 hb_subset_input_destroy (input); | |
| 52 } | |
| 53 | |
| 54 void parse_face (int argc, const char * const *argv) | |
| 55 { | |
| 56 option_parser_t parser; | |
| 57 face_options_t face_opts; | |
| 58 | |
| 59 face_opts.add_options (&parser); | |
| 60 | |
| 61 GOptionEntry entries[] = | |
| 62 { | |
| 63 {G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN, | |
| 64 G_OPTION_ARG_CALLBACK, (gpointer) &collect_face, nullptr, "[FONT-FILE] [TEXT]"}, | |
| 65 {nullptr} | |
| 66 }; | |
| 67 parser.add_main_group (entries, &face_opts); | |
| 68 parser.add_options (); | |
| 69 | |
| 70 g_option_context_set_ignore_unknown_options (parser.context, true); | |
| 71 g_option_context_set_help_enabled (parser.context, false); | |
| 72 | |
| 73 char **args = (char **) | |
| 74 #if GLIB_CHECK_VERSION (2, 68, 0) | |
| 75 g_memdup2 | |
| 76 #else | |
| 77 g_memdup | |
| 78 #endif | |
| 79 (argv, argc * sizeof (*argv)); | |
| 80 parser.parse (&argc, &args); | |
| 81 g_free (args); | |
| 82 | |
| 83 set_face (face_opts.face); | |
| 84 } | |
| 85 | |
| 86 void parse (int argc, char **argv) | |
| 87 { | |
| 88 bool help = false; | |
| 89 for (auto i = 1; i < argc; i++) | |
| 90 if (!strncmp ("--help", argv[i], 6)) | |
| 91 { | |
| 92 help = true; | |
| 93 break; | |
| 94 } | |
| 95 | |
| 96 if (likely (!help)) | |
| 97 { | |
| 98 /* Do a preliminary parse to load font-face, such that we can use it | |
| 99 * during main option parsing. */ | |
| 100 parse_face (argc, argv); | |
| 101 } | |
| 102 | |
| 103 add_options (); | |
| 104 option_parser_t::parse (&argc, &argv); | |
| 105 } | |
| 106 | |
| 107 int operator () (int argc, char **argv) | |
| 108 { | |
| 109 parse (argc, argv); | |
| 110 | |
| 111 hb_face_t* orig_face = face; | |
| 112 if (preprocess) | |
| 113 orig_face = preprocess_face (face); | |
| 114 | |
| 115 hb_face_t *new_face = nullptr; | |
| 116 for (unsigned i = 0; i < num_iterations; i++) | |
| 117 { | |
| 118 hb_face_destroy (new_face); | |
| 119 new_face = hb_subset_or_fail (orig_face, input); | |
| 120 } | |
| 121 | |
| 122 bool success = new_face; | |
| 123 if (success) | |
| 124 { | |
| 125 hb_blob_t *result = hb_face_reference_blob (new_face); | |
| 126 write_file (output_file, result); | |
| 127 hb_blob_destroy (result); | |
| 128 } | |
| 129 | |
| 130 hb_face_destroy (new_face); | |
| 131 if (preprocess) | |
| 132 hb_face_destroy (orig_face); | |
| 133 | |
| 134 return success ? 0 : 1; | |
| 135 } | |
| 136 | |
| 137 bool | |
| 138 write_file (const char *output_file, hb_blob_t *blob) | |
| 139 { | |
| 140 assert (out_fp); | |
| 141 | |
| 142 unsigned int size; | |
| 143 const char* data = hb_blob_get_data (blob, &size); | |
| 144 | |
| 145 while (size) | |
| 146 { | |
| 147 size_t ret = fwrite (data, 1, size, out_fp); | |
| 148 size -= ret; | |
| 149 data += ret; | |
| 150 if (size && ferror (out_fp)) | |
| 151 fail (false, "Failed to write output: %s", strerror (errno)); | |
| 152 } | |
| 153 | |
| 154 return true; | |
| 155 } | |
| 156 | |
| 157 void add_options (); | |
| 158 | |
| 159 protected: | |
| 160 static gboolean | |
| 161 collect_face (const char *name, | |
| 162 const char *arg, | |
| 163 gpointer data, | |
| 164 GError **error); | |
| 165 static gboolean | |
| 166 collect_rest (const char *name, | |
| 167 const char *arg, | |
| 168 gpointer data, | |
| 169 GError **error); | |
| 170 | |
| 171 public: | |
| 172 | |
| 173 unsigned num_iterations = 1; | |
| 174 gboolean preprocess; | |
| 175 hb_subset_input_t *input = nullptr; | |
| 176 }; | |
| 177 | |
| 178 static gboolean | |
| 179 parse_gids (const char *name G_GNUC_UNUSED, | |
| 180 const char *arg, | |
| 181 gpointer data, | |
| 182 GError **error) | |
| 183 { | |
| 184 subset_main_t *subset_main = (subset_main_t *) data; | |
| 185 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 186 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 187 hb_set_t *gids = hb_subset_input_glyph_set (subset_main->input); | |
| 188 | |
| 189 if (!is_remove && !is_add) hb_set_clear (gids); | |
| 190 | |
| 191 if (0 == strcmp (arg, "*")) | |
| 192 { | |
| 193 hb_set_clear (gids); | |
| 194 if (!is_remove) | |
| 195 hb_set_invert (gids); | |
| 196 return true; | |
| 197 } | |
| 198 | |
| 199 char *s = (char *) arg; | |
| 200 char *p; | |
| 201 | |
| 202 while (s && *s) | |
| 203 { | |
| 204 while (*s && strchr (", ", *s)) | |
| 205 s++; | |
| 206 if (!*s) | |
| 207 break; | |
| 208 | |
| 209 errno = 0; | |
| 210 hb_codepoint_t start_code = strtoul (s, &p, 10); | |
| 211 if (s[0] == '-' || errno || s == p) | |
| 212 { | |
| 213 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 214 "Failed parsing glyph-index at: '%s'", s); | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 if (p && p[0] == '-') // ranges | |
| 219 { | |
| 220 s = ++p; | |
| 221 hb_codepoint_t end_code = strtoul (s, &p, 10); | |
| 222 if (s[0] == '-' || errno || s == p) | |
| 223 { | |
| 224 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 225 "Failed parsing glyph-index at: '%s'", s); | |
| 226 return false; | |
| 227 } | |
| 228 | |
| 229 if (end_code < start_code) | |
| 230 { | |
| 231 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 232 "Invalid glyph-index range %u-%u", start_code, end_code); | |
| 233 return false; | |
| 234 } | |
| 235 if (!is_remove) | |
| 236 hb_set_add_range (gids, start_code, end_code); | |
| 237 else | |
| 238 hb_set_del_range (gids, start_code, end_code); | |
| 239 } | |
| 240 else | |
| 241 { | |
| 242 if (!is_remove) | |
| 243 hb_set_add (gids, start_code); | |
| 244 else | |
| 245 hb_set_del (gids, start_code); | |
| 246 } | |
| 247 | |
| 248 s = p; | |
| 249 } | |
| 250 | |
| 251 return true; | |
| 252 } | |
| 253 | |
| 254 static gboolean | |
| 255 parse_glyphs (const char *name G_GNUC_UNUSED, | |
| 256 const char *arg, | |
| 257 gpointer data, | |
| 258 GError **error G_GNUC_UNUSED) | |
| 259 { | |
| 260 subset_main_t *subset_main = (subset_main_t *) data; | |
| 261 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 262 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 263 hb_set_t *gids = hb_subset_input_glyph_set (subset_main->input); | |
| 264 | |
| 265 if (!is_remove && !is_add) hb_set_clear (gids); | |
| 266 | |
| 267 if (0 == strcmp (arg, "*")) | |
| 268 { | |
| 269 hb_set_clear (gids); | |
| 270 if (!is_remove) | |
| 271 hb_set_invert (gids); | |
| 272 return true; | |
| 273 } | |
| 274 | |
| 275 const char *p = arg; | |
| 276 const char *p_end = arg + strlen (arg); | |
| 277 | |
| 278 hb_font_t *font = hb_font_create (subset_main->face); | |
| 279 while (p < p_end) | |
| 280 { | |
| 281 while (p < p_end && (*p == ' ' || *p == ',')) | |
| 282 p++; | |
| 283 | |
| 284 const char *end = p; | |
| 285 while (end < p_end && *end != ' ' && *end != ',') | |
| 286 end++; | |
| 287 | |
| 288 if (p < end) | |
| 289 { | |
| 290 hb_codepoint_t gid; | |
| 291 if (!hb_font_get_glyph_from_name (font, p, end - p, &gid)) | |
| 292 { | |
| 293 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 294 "Failed parsing glyph name: '%s'", p); | |
| 295 return false; | |
| 296 } | |
| 297 | |
| 298 if (!is_remove) | |
| 299 hb_set_add (gids, gid); | |
| 300 else | |
| 301 hb_set_del (gids, gid); | |
| 302 } | |
| 303 | |
| 304 p = end + 1; | |
| 305 } | |
| 306 hb_font_destroy (font); | |
| 307 | |
| 308 return true; | |
| 309 } | |
| 310 | |
| 311 static gboolean | |
| 312 parse_text (const char *name G_GNUC_UNUSED, | |
| 313 const char *arg, | |
| 314 gpointer data, | |
| 315 GError **error G_GNUC_UNUSED) | |
| 316 { | |
| 317 subset_main_t *subset_main = (subset_main_t *) data; | |
| 318 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 319 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 320 hb_set_t *unicodes = hb_subset_input_unicode_set (subset_main->input); | |
| 321 | |
| 322 if (!is_remove && !is_add) hb_set_clear (unicodes); | |
| 323 | |
| 324 if (0 == strcmp (arg, "*")) | |
| 325 { | |
| 326 hb_set_clear (unicodes); | |
| 327 if (!is_remove) | |
| 328 hb_set_invert (unicodes); | |
| 329 return true; | |
| 330 } | |
| 331 | |
| 332 for (gchar *c = (gchar *) arg; | |
| 333 *c; | |
| 334 c = g_utf8_find_next_char(c, nullptr)) | |
| 335 { | |
| 336 gunichar cp = g_utf8_get_char(c); | |
| 337 if (!is_remove) | |
| 338 hb_set_add (unicodes, cp); | |
| 339 else | |
| 340 hb_set_del (unicodes, cp); | |
| 341 } | |
| 342 return true; | |
| 343 } | |
| 344 | |
| 345 static gboolean | |
| 346 parse_unicodes (const char *name G_GNUC_UNUSED, | |
| 347 const char *arg, | |
| 348 gpointer data, | |
| 349 GError **error) | |
| 350 { | |
| 351 subset_main_t *subset_main = (subset_main_t *) data; | |
| 352 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 353 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 354 hb_set_t *unicodes = hb_subset_input_unicode_set (subset_main->input); | |
| 355 | |
| 356 if (!is_remove && !is_add) hb_set_clear (unicodes); | |
| 357 | |
| 358 if (0 == strcmp (arg, "*")) | |
| 359 { | |
| 360 hb_set_clear (unicodes); | |
| 361 if (!is_remove) | |
| 362 hb_set_invert (unicodes); | |
| 363 return true; | |
| 364 } | |
| 365 | |
| 366 // XXX TODO Ranges | |
| 367 #define DELIMITERS "<+->{},;&#\\xXuUnNiI\n\t\v\f\r " | |
| 368 | |
| 369 char *s = (char *) arg; | |
| 370 char *p; | |
| 371 | |
| 372 while (s && *s) | |
| 373 { | |
| 374 while (*s && strchr (DELIMITERS, *s)) | |
| 375 s++; | |
| 376 if (!*s) | |
| 377 break; | |
| 378 | |
| 379 errno = 0; | |
| 380 hb_codepoint_t start_code = strtoul (s, &p, 16); | |
| 381 if (errno || s == p) | |
| 382 { | |
| 383 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 384 "Failed parsing Unicode at: '%s'", s); | |
| 385 return false; | |
| 386 } | |
| 387 | |
| 388 if (p && p[0] == '-') // ranges | |
| 389 { | |
| 390 s = ++p; | |
| 391 hb_codepoint_t end_code = strtoul (s, &p, 16); | |
| 392 if (s[0] == '-' || errno || s == p) | |
| 393 { | |
| 394 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 395 "Failed parsing Unicode at: '%s'", s); | |
| 396 return false; | |
| 397 } | |
| 398 | |
| 399 if (end_code < start_code) | |
| 400 { | |
| 401 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 402 "Invalid Unicode range %u-%u", start_code, end_code); | |
| 403 return false; | |
| 404 } | |
| 405 if (!is_remove) | |
| 406 hb_set_add_range (unicodes, start_code, end_code); | |
| 407 else | |
| 408 hb_set_del_range (unicodes, start_code, end_code); | |
| 409 } | |
| 410 else | |
| 411 { | |
| 412 if (!is_remove) | |
| 413 hb_set_add (unicodes, start_code); | |
| 414 else | |
| 415 hb_set_del (unicodes, start_code); | |
| 416 } | |
| 417 | |
| 418 s = p; | |
| 419 } | |
| 420 | |
| 421 return true; | |
| 422 } | |
| 423 | |
| 424 static gboolean | |
| 425 parse_nameids (const char *name, | |
| 426 const char *arg, | |
| 427 gpointer data, | |
| 428 GError **error) | |
| 429 { | |
| 430 subset_main_t *subset_main = (subset_main_t *) data; | |
| 431 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 432 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 433 hb_set_t *name_ids = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_NAME_ID); | |
| 434 | |
| 435 | |
| 436 if (!is_remove && !is_add) hb_set_clear (name_ids); | |
| 437 | |
| 438 if (0 == strcmp (arg, "*")) | |
| 439 { | |
| 440 hb_set_clear (name_ids); | |
| 441 if (!is_remove) | |
| 442 hb_set_invert (name_ids); | |
| 443 return true; | |
| 444 } | |
| 445 | |
| 446 char *s = (char *) arg; | |
| 447 char *p; | |
| 448 | |
| 449 while (s && *s) | |
| 450 { | |
| 451 while (*s && strchr (", ", *s)) | |
| 452 s++; | |
| 453 if (!*s) | |
| 454 break; | |
| 455 | |
| 456 errno = 0; | |
| 457 hb_codepoint_t u = strtoul (s, &p, 10); | |
| 458 if (errno || s == p) | |
| 459 { | |
| 460 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 461 "Failed parsing nameID at: '%s'", s); | |
| 462 return false; | |
| 463 } | |
| 464 | |
| 465 if (!is_remove) | |
| 466 { | |
| 467 hb_set_add (name_ids, u); | |
| 468 } else { | |
| 469 hb_set_del (name_ids, u); | |
| 470 } | |
| 471 | |
| 472 s = p; | |
| 473 } | |
| 474 | |
| 475 return true; | |
| 476 } | |
| 477 | |
| 478 static gboolean | |
| 479 parse_name_languages (const char *name, | |
| 480 const char *arg, | |
| 481 gpointer data, | |
| 482 GError **error) | |
| 483 { | |
| 484 subset_main_t *subset_main = (subset_main_t *) data; | |
| 485 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 486 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 487 hb_set_t *name_languages = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_NAME_LANG_ID); | |
| 488 | |
| 489 if (!is_remove && !is_add) hb_set_clear (name_languages); | |
| 490 | |
| 491 if (0 == strcmp (arg, "*")) | |
| 492 { | |
| 493 hb_set_clear (name_languages); | |
| 494 if (!is_remove) | |
| 495 hb_set_invert (name_languages); | |
| 496 return true; | |
| 497 } | |
| 498 | |
| 499 char *s = (char *) arg; | |
| 500 char *p; | |
| 501 | |
| 502 while (s && *s) | |
| 503 { | |
| 504 while (*s && strchr (", ", *s)) | |
| 505 s++; | |
| 506 if (!*s) | |
| 507 break; | |
| 508 | |
| 509 errno = 0; | |
| 510 hb_codepoint_t u = strtoul (s, &p, 10); | |
| 511 if (errno || s == p) | |
| 512 { | |
| 513 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 514 "Failed parsing name-language code at: '%s'", s); | |
| 515 return false; | |
| 516 } | |
| 517 | |
| 518 if (!is_remove) | |
| 519 { | |
| 520 hb_set_add (name_languages, u); | |
| 521 } else { | |
| 522 hb_set_del (name_languages, u); | |
| 523 } | |
| 524 | |
| 525 s = p; | |
| 526 } | |
| 527 | |
| 528 return true; | |
| 529 } | |
| 530 | |
| 531 template <hb_subset_flags_t flag> | |
| 532 static gboolean | |
| 533 set_flag (const char *name, | |
| 534 const char *arg, | |
| 535 gpointer data, | |
| 536 GError **error G_GNUC_UNUSED) | |
| 537 { | |
| 538 subset_main_t *subset_main = (subset_main_t *) data; | |
| 539 | |
| 540 hb_subset_input_set_flags (subset_main->input, | |
| 541 hb_subset_input_get_flags (subset_main->input) | flag); | |
| 542 | |
| 543 return true; | |
| 544 } | |
| 545 | |
| 546 static gboolean | |
| 547 parse_layout_tag_list (hb_subset_sets_t set_type, | |
| 548 const char *name, | |
| 549 const char *arg, | |
| 550 gpointer data, | |
| 551 GError **error G_GNUC_UNUSED) | |
| 552 { | |
| 553 subset_main_t *subset_main = (subset_main_t *) data; | |
| 554 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 555 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 556 hb_set_t *layout_tags = hb_subset_input_set (subset_main->input, set_type); | |
| 557 | |
| 558 if (!is_remove && !is_add) hb_set_clear (layout_tags); | |
| 559 | |
| 560 if (0 == strcmp (arg, "*")) | |
| 561 { | |
| 562 hb_set_clear (layout_tags); | |
| 563 if (!is_remove) | |
| 564 hb_set_invert (layout_tags); | |
| 565 return true; | |
| 566 } | |
| 567 | |
| 568 char *s = strtok((char *) arg, ", "); | |
| 569 while (s) | |
| 570 { | |
| 571 if (strlen (s) > 4) // tags are at most 4 bytes | |
| 572 { | |
| 573 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 574 "Failed parsing table tag at: '%s'", s); | |
| 575 return false; | |
| 576 } | |
| 577 | |
| 578 hb_tag_t tag = hb_tag_from_string (s, strlen (s)); | |
| 579 | |
| 580 if (!is_remove) | |
| 581 hb_set_add (layout_tags, tag); | |
| 582 else | |
| 583 hb_set_del (layout_tags, tag); | |
| 584 | |
| 585 s = strtok(nullptr, ", "); | |
| 586 } | |
| 587 | |
| 588 return true; | |
| 589 } | |
| 590 | |
| 591 static gboolean | |
| 592 parse_layout_features (const char *name, | |
| 593 const char *arg, | |
| 594 gpointer data, | |
| 595 GError **error) | |
| 596 | |
| 597 { | |
| 598 return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_FEATURE_TAG, | |
| 599 name, | |
| 600 arg, | |
| 601 data, | |
| 602 error); | |
| 603 } | |
| 604 | |
| 605 static gboolean | |
| 606 parse_layout_scripts (const char *name, | |
| 607 const char *arg, | |
| 608 gpointer data, | |
| 609 GError **error) | |
| 610 | |
| 611 { | |
| 612 return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG, | |
| 613 name, | |
| 614 arg, | |
| 615 data, | |
| 616 error); | |
| 617 } | |
| 618 | |
| 619 static gboolean | |
| 620 parse_drop_tables (const char *name, | |
| 621 const char *arg, | |
| 622 gpointer data, | |
| 623 GError **error) | |
| 624 { | |
| 625 subset_main_t *subset_main = (subset_main_t *) data; | |
| 626 hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); | |
| 627 hb_bool_t is_add = (name[strlen (name) - 1] == '+'); | |
| 628 hb_set_t *drop_tables = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_DROP_TABLE_TAG); | |
| 629 | |
| 630 if (!is_remove && !is_add) hb_set_clear (drop_tables); | |
| 631 | |
| 632 if (0 == strcmp (arg, "*")) | |
| 633 { | |
| 634 hb_set_clear (drop_tables); | |
| 635 if (!is_remove) | |
| 636 hb_set_invert (drop_tables); | |
| 637 return true; | |
| 638 } | |
| 639 | |
| 640 char *s = strtok((char *) arg, ", "); | |
| 641 while (s) | |
| 642 { | |
| 643 if (strlen (s) > 4) // Table tags are at most 4 bytes. | |
| 644 { | |
| 645 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 646 "Failed parsing table tag at: '%s'", s); | |
| 647 return false; | |
| 648 } | |
| 649 | |
| 650 hb_tag_t tag = hb_tag_from_string (s, strlen (s)); | |
| 651 | |
| 652 if (!is_remove) | |
| 653 hb_set_add (drop_tables, tag); | |
| 654 else | |
| 655 hb_set_del (drop_tables, tag); | |
| 656 | |
| 657 s = strtok(nullptr, ", "); | |
| 658 } | |
| 659 | |
| 660 return true; | |
| 661 } | |
| 662 | |
| 663 #ifndef HB_NO_VAR | |
| 664 static gboolean | |
| 665 parse_instance (const char *name, | |
| 666 const char *arg, | |
| 667 gpointer data, | |
| 668 GError **error) | |
| 669 { | |
| 670 subset_main_t *subset_main = (subset_main_t *) data; | |
| 671 | |
| 672 char *s = strtok((char *) arg, "="); | |
| 673 while (s) | |
| 674 { | |
| 675 unsigned len = strlen (s); | |
| 676 if (len > 4) //Axis tags are 4 bytes. | |
| 677 { | |
| 678 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 679 "Failed parsing axis tag at: '%s'", s); | |
| 680 return false; | |
| 681 } | |
| 682 | |
| 683 hb_tag_t axis_tag = hb_tag_from_string (s, len); | |
| 684 | |
| 685 s = strtok(nullptr, ", "); | |
| 686 if (!s) | |
| 687 { | |
| 688 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 689 "Value not specified for axis: %c%c%c%c", HB_UNTAG (axis_tag)); | |
| 690 return false; | |
| 691 } | |
| 692 | |
| 693 if (strcmp (s, "drop") == 0) | |
| 694 { | |
| 695 if (!hb_subset_input_pin_axis_to_default (subset_main->input, subset_main->face, axis_tag)) | |
| 696 { | |
| 697 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 698 "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag)); | |
| 699 return false; | |
| 700 } | |
| 701 } | |
| 702 else | |
| 703 { | |
| 704 errno = 0; | |
| 705 char *p; | |
| 706 float axis_value = strtof (s, &p); | |
| 707 if (errno || s == p) | |
| 708 { | |
| 709 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 710 "Failed parsing axis value at: '%s'", s); | |
| 711 return false; | |
| 712 } | |
| 713 | |
| 714 if (!hb_subset_input_pin_axis_location (subset_main->input, subset_main->face, axis_tag, axis_value)) | |
| 715 { | |
| 716 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, | |
| 717 "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag)); | |
| 718 return false; | |
| 719 } | |
| 720 } | |
| 721 s = strtok(nullptr, "="); | |
| 722 } | |
| 723 | |
| 724 return true; | |
| 725 } | |
| 726 #endif | |
| 727 | |
| 728 template <GOptionArgFunc line_parser, bool allow_comments=true> | |
| 729 static gboolean | |
| 730 parse_file_for (const char *name, | |
| 731 const char *arg, | |
| 732 gpointer data, | |
| 733 GError **error) | |
| 734 { | |
| 735 FILE *fp = nullptr; | |
| 736 if (0 != strcmp (arg, "-")) | |
| 737 fp = fopen (arg, "r"); | |
| 738 else | |
| 739 fp = stdin; | |
| 740 | |
| 741 if (!fp) | |
| 742 { | |
| 743 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, | |
| 744 "Failed opening file `%s': %s", | |
| 745 arg, strerror (errno)); | |
| 746 return false; | |
| 747 } | |
| 748 | |
| 749 GString *gs = g_string_new (nullptr); | |
| 750 do | |
| 751 { | |
| 752 g_string_set_size (gs, 0); | |
| 753 char buf[BUFSIZ]; | |
| 754 while (fgets (buf, sizeof (buf), fp)) | |
| 755 { | |
| 756 unsigned bytes = strlen (buf); | |
| 757 if (bytes && buf[bytes - 1] == '\n') | |
| 758 { | |
| 759 bytes--; | |
| 760 g_string_append_len (gs, buf, bytes); | |
| 761 break; | |
| 762 } | |
| 763 g_string_append_len (gs, buf, bytes); | |
| 764 } | |
| 765 if (ferror (fp)) | |
| 766 { | |
| 767 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, | |
| 768 "Failed reading file `%s': %s", | |
| 769 arg, strerror (errno)); | |
| 770 return false; | |
| 771 } | |
| 772 g_string_append_c (gs, '\0'); | |
| 773 | |
| 774 if (allow_comments) | |
| 775 { | |
| 776 char *comment = strchr (gs->str, '#'); | |
| 777 if (comment) | |
| 778 *comment = '\0'; | |
| 779 } | |
| 780 | |
| 781 line_parser ("+", gs->str, data, error); | |
| 782 | |
| 783 if (*error) | |
| 784 break; | |
| 785 } | |
| 786 while (!feof (fp)); | |
| 787 | |
| 788 g_string_free (gs, false); | |
| 789 | |
| 790 return true; | |
| 791 } | |
| 792 | |
| 793 gboolean | |
| 794 subset_main_t::collect_face (const char *name, | |
| 795 const char *arg, | |
| 796 gpointer data, | |
| 797 GError **error) | |
| 798 { | |
| 799 face_options_t *thiz = (face_options_t *) data; | |
| 800 | |
| 801 if (!thiz->font_file) | |
| 802 { | |
| 803 thiz->font_file = g_strdup (arg); | |
| 804 return true; | |
| 805 } | |
| 806 | |
| 807 return true; | |
| 808 } | |
| 809 | |
| 810 gboolean | |
| 811 subset_main_t::collect_rest (const char *name, | |
| 812 const char *arg, | |
| 813 gpointer data, | |
| 814 GError **error) | |
| 815 { | |
| 816 subset_main_t *thiz = (subset_main_t *) data; | |
| 817 | |
| 818 if (!thiz->font_file) | |
| 819 { | |
| 820 thiz->font_file = g_strdup (arg); | |
| 821 return true; | |
| 822 } | |
| 823 | |
| 824 parse_text (name, arg, data, error); | |
| 825 return true; | |
| 826 } | |
| 827 | |
| 828 void | |
| 829 subset_main_t::add_options () | |
| 830 { | |
| 831 set_summary ("Subset fonts to specification."); | |
| 832 | |
| 833 face_options_t::add_options (this); | |
| 834 | |
| 835 GOptionEntry glyphset_entries[] = | |
| 836 { | |
| 837 {"gids", 'g', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, | |
| 838 "Specify glyph IDs or ranges to include in the subset.\n" | |
| 839 " " | |
| 840 "Use --gids-=... to subtract codepoints from the current set.", "list of glyph indices/ranges or *"}, | |
| 841 {"gids-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to remove from the subset", "list of glyph indices/ranges or *"}, | |
| 842 {"gids+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_gids, "Specify glyph IDs or ranges to include in the subset", "list of glyph indices/ranges or *"}, | |
| 843 {"gids-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_gids>, "Specify file to read glyph IDs or ranges from", "filename"}, | |
| 844 {"glyphs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset. Use --glyphs-=... to subtract glyphs from the current set.", "list of glyph names or *"}, | |
| 845 {"glyphs+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to include in the subset", "list of glyph names"}, | |
| 846 {"glyphs-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_glyphs, "Specify glyph names to remove from the subset", "list of glyph names"}, | |
| 847 | |
| 848 | |
| 849 {"glyphs-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_glyphs>, "Specify file to read glyph names from", "filename"}, | |
| 850 | |
| 851 {"text", 't', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset. Use --text-=... to subtract codepoints from the current set.", "string"}, | |
| 852 {"text-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to remove from the subset", "string"}, | |
| 853 {"text+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_text, "Specify text to include in the subset", "string"}, | |
| 854 | |
| 855 | |
| 856 {"text-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_text, false>,"Specify file to read text from", "filename"}, | |
| 857 {"unicodes", 'u', 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, | |
| 858 "Specify Unicode codepoints or ranges to include in the subset. Use * to include all codepoints.\n" | |
| 859 " " | |
| 860 "--unicodes-=... can be used to subtract codepoints from the current set.\n" | |
| 861 " " | |
| 862 "For example: --unicodes=* --unicodes-=41,42,43 would create a subset with all codepoints\n" | |
| 863 " " | |
| 864 "except for 41, 42, 43.", | |
| 865 "list of hex numbers/ranges or *"}, | |
| 866 {"unicodes-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to remove from the subset", "list of hex numbers/ranges or *"}, | |
| 867 {"unicodes+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_unicodes, "Specify Unicode codepoints or ranges to include in the subset", "list of hex numbers/ranges or *"}, | |
| 868 | |
| 869 {"unicodes-file", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_file_for<parse_unicodes>,"Specify file to read Unicode codepoints or ranges from", "filename"}, | |
| 870 {nullptr} | |
| 871 }; | |
| 872 add_group (glyphset_entries, | |
| 873 "subset-glyphset", | |
| 874 "Subset glyph-set option:", | |
| 875 "Subsetting glyph-set options", | |
| 876 this); | |
| 877 | |
| 878 GOptionEntry other_entries[] = | |
| 879 { | |
| 880 {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids. Use --name-IDs-=... to subtract from the current set.", "list of int numbers or *"}, | |
| 881 {"name-IDs-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers or *"}, | |
| 882 {"name-IDs+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers or *"}, | |
| 883 {"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs. Use --name-languages-=... to subtract from the current set.", "list of int numbers or *"}, | |
| 884 {"name-languages-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"}, | |
| 885 {"name-languages+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"}, | |
| 886 | |
| 887 {"layout-features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved. Use --layout-features-=... to subtract from the current set.", "list of string table tags or *"}, | |
| 888 {"layout-features+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"}, | |
| 889 {"layout-features-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"}, | |
| 890 | |
| 891 {"layout-scripts", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved. Use --layout-scripts-=... to subtract from the current set.", "list of string table tags or *"}, | |
| 892 {"layout-scripts+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"}, | |
| 893 {"layout-scripts-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"}, | |
| 894 | |
| 895 {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables. Use --drop-tables-=... to subtract from the current set.", "list of string table tags or *"}, | |
| 896 {"drop-tables+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"}, | |
| 897 {"drop-tables-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"}, | |
| 898 #ifndef HB_NO_VAR | |
| 899 {"instance", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_instance, | |
| 900 "(Partially|Fully) Instantiate a variable font. A location consists of the tag of a variation axis, followed by '=', followed by a\n" | |
| 901 "number or the literal string 'drop'\n" | |
| 902 " " | |
| 903 "For example: --instance=\"wdth=100 wght=200\" or --instance=\"wdth=drop\"\n" | |
| 904 "Note: currently only fully instancing to the default location is supported\n", | |
| 905 "list of comma separated axis-locations"}, | |
| 906 #endif | |
| 907 {nullptr} | |
| 908 }; | |
| 909 add_group (other_entries, | |
| 910 "subset-other", | |
| 911 "Subset other option:", | |
| 912 "Subsetting other options", | |
| 913 this); | |
| 914 | |
| 915 GOptionEntry flag_entries[] = | |
| 916 { | |
| 917 {"no-hinting", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_HINTING>, "Whether to drop hints", nullptr}, | |
| 918 {"retain-gids", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_RETAIN_GIDS>, "If set don't renumber glyph ids in the subset.", nullptr}, | |
| 919 {"desubroutinize", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_DESUBROUTINIZE>, "Remove CFF/CFF2 use of subroutines", nullptr}, | |
| 920 {"name-legacy", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NAME_LEGACY>, "Keep legacy (non-Unicode) 'name' table entries", nullptr}, | |
| 921 {"set-overlaps-flag", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_SET_OVERLAPS_FLAG>, "Set the overlaps flag on each glyph.", nullptr}, | |
| 922 {"notdef-outline", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NOTDEF_OUTLINE>, "Keep the outline of \'.notdef\' glyph", nullptr}, | |
| 923 {"no-prune-unicode-ranges", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES>, "Don't change the 'OS/2 ulUnicodeRange*' bits.", nullptr}, | |
| 924 {"glyph-names", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_GLYPH_NAMES>, "Keep PS glyph names in TT-flavored fonts. ", nullptr}, | |
| 925 {"passthrough-tables", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (gpointer) &set_flag<HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED>, "Do not drop tables that the tool does not know how to subset.", nullptr}, | |
| 926 {"preprocess-face", 0, 0, G_OPTION_ARG_NONE, &this->preprocess, | |
| 927 "If set preprocesses the face with the add accelerator option before actually subsetting.", nullptr}, | |
| 928 {nullptr} | |
| 929 }; | |
| 930 add_group (flag_entries, | |
| 931 "subset-flags", | |
| 932 "Subset boolean option:", | |
| 933 "Subsetting boolean options", | |
| 934 this); | |
| 935 | |
| 936 GOptionEntry app_entries[] = | |
| 937 { | |
| 938 {"num-iterations", 'n', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, | |
| 939 &this->num_iterations, | |
| 940 "Run subsetter N times (default: 1)", "N"}, | |
| 941 {nullptr} | |
| 942 }; | |
| 943 add_group (app_entries, | |
| 944 "subset-app", | |
| 945 "Subset app option:", | |
| 946 "Subsetting application options", | |
| 947 this); | |
| 948 | |
| 949 output_options_t::add_options (this); | |
| 950 | |
| 951 GOptionEntry entries[] = | |
| 952 { | |
| 953 {G_OPTION_REMAINING, 0, G_OPTION_FLAG_IN_MAIN, | |
| 954 G_OPTION_ARG_CALLBACK, (gpointer) &collect_rest, nullptr, "[FONT-FILE] [TEXT]"}, | |
| 955 {nullptr} | |
| 956 }; | |
| 957 add_main_group (entries, this); | |
| 958 option_parser_t::add_options (); | |
| 959 } | |
| 960 | |
| 961 int | |
| 962 main (int argc, char **argv) | |
| 963 { | |
| 964 return batch_main<subset_main_t, true> (argc, argv); | |
| 965 } |
