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 }