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_drop_tables(const char * name,const char * arg,gpointer data,GError ** error G_GNUC_UNUSED)88 parse_drop_tables (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 *drop_tables = subset_opts->input->drop_tables;
95 
96   char last_name_char = name[strlen (name) - 1];
97 
98   if (last_name_char != '+' && last_name_char != '-')
99     hb_set_clear (drop_tables);
100 
101   char *s = strtok((char *) arg, ", ");
102   while (s)
103   {
104     if (strlen (s) > 4) // Table tags are at most 4 bytes.
105     {
106       g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
107 		   "Failed parsing table tag values at: '%s'", s);
108       return false;
109     }
110 
111     hb_tag_t tag = hb_tag_from_string (s, strlen (s));
112 
113     if (last_name_char != '-')
114       hb_set_add (drop_tables, tag);
115     else
116       hb_set_del (drop_tables, tag);
117 
118     s = strtok(nullptr, ", ");
119   }
120 
121   return true;
122 }
123 
124 void
add_options(option_parser_t * parser)125 subset_options_t::add_options (option_parser_t *parser)
126 {
127   GOptionEntry entries[] =
128   {
129     {"no-hinting", 0, 0, G_OPTION_ARG_NONE,  &this->input->drop_hints,   "Whether to drop hints",   nullptr},
130     {"retain-gids", 0, 0, G_OPTION_ARG_NONE,  &this->input->retain_gids,   "If set don't renumber glyph ids in the subset.",   nullptr},
131     {"desubroutinize", 0, 0, G_OPTION_ARG_NONE,  &this->input->desubroutinize,   "Remove CFF/CFF2 use of subroutines",   nullptr},
132     {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK,  (gpointer) &parse_nameids,  "Subset specified nameids", "list of int numbers"},
133     {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK,  (gpointer) &parse_drop_tables,  "Drop the specified tables.", "list of string table tags."},
134     {"drop-tables+", 0, 0, G_OPTION_ARG_CALLBACK,  (gpointer) &parse_drop_tables,  "Drop the specified tables.", "list of string table tags."},
135     {"drop-tables-", 0, 0, G_OPTION_ARG_CALLBACK,  (gpointer) &parse_drop_tables,  "Drop the specified tables.", "list of string table tags."},
136 
137     {nullptr}
138   };
139   parser->add_group (entries,
140 	 "subset",
141 	 "Subset options:",
142 	 "Options subsetting",
143 	 this);
144 }
145