1 /*
2 * Copyright © 2023 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 #ifndef HELPER_SUBSET_HH
27 #define HELPER_SUBSET_HH
28
29 #include "glib.h"
30 #include <errno.h>
31 #include <math.h>
32 #include <stdbool.h>
33 #include "hb-subset.h"
34
35 #ifndef HB_NO_VAR
36
37 // Parses an axis position string and sets min, default, and max to
38 // the requested values. If a value should be set to it's default value
39 // then it will be set to NaN.
40 static gboolean
parse_axis_position(const char * s,float * min,float * def,float * max,gboolean * drop,GError ** error)41 parse_axis_position(const char* s,
42 float* min,
43 float* def,
44 float* max,
45 gboolean* drop,
46 GError **error)
47 {
48 const char* part = strpbrk(s, ":");
49 *drop = false;
50 if (!part) {
51 // Single value.
52 if (strcmp (s, "drop") == 0)
53 {
54 *min = NAN;
55 *def = NAN;
56 *max = NAN;
57 *drop = true;
58 return true;
59 }
60
61 errno = 0;
62 char *p;
63 float axis_value = strtof (s, &p);
64 if (errno || s == p)
65 {
66 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
67 "Failed parsing axis value at: '%s'", s);
68 return false;
69 }
70
71 *min = axis_value;
72 *def = axis_value;
73 *max = axis_value;
74 return true;
75 }
76
77
78 float values[3];
79 int count = 0;
80 for (int i = 0; i < 3; i++) {
81 errno = 0;
82 count++;
83 if (!*s || part == s) {
84 values[i] = NAN;
85
86 if (part == NULL) break;
87 s = part + 1;
88 part = strpbrk(s, ":");
89 continue;
90 }
91
92 char *pend;
93 values[i] = strtof (s, &pend);
94 if (errno || s == pend || (part && pend != part))
95 {
96 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
97 "Failed parsing axis value at: '%s'", s);
98 return false;
99 }
100
101 if (part == NULL) break;
102 s = pend + 1;
103 part = strpbrk(s, ":");
104 }
105
106 if (count == 2) {
107 *min = values[0];
108 *def = NAN;
109 *max = values[1];
110 return true;
111 } else if (count == 3) {
112 *min = values[0];
113 *def = values[1];
114 *max = values[2];
115 return true;
116 }
117
118 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
119 "Failed parsing axis value at: '%s'", s);
120 return false;
121 }
122
123 static gboolean
parse_instancing_spec(const char * arg,hb_face_t * face,hb_subset_input_t * input,GError ** error)124 parse_instancing_spec (const char *arg,
125 hb_face_t* face,
126 hb_subset_input_t* input,
127 GError **error)
128 {
129 char* s;
130 while ((s = strtok((char *) arg, "=")))
131 {
132 arg = NULL;
133 unsigned len = strlen (s);
134 if (len > 4) //Axis tags are 4 bytes.
135 {
136 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
137 "Failed parsing axis tag at: '%s'", s);
138 return false;
139 }
140
141 /* support *=drop */
142 if (0 == strcmp (s, "*"))
143 {
144 s = strtok(NULL, ", ");
145 if (0 != strcmp (s, "drop"))
146 {
147 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
148 "Failed parsing axis position at: '%s'", s);
149 return false;
150 }
151
152 if (!hb_subset_input_pin_all_axes_to_default (input, face))
153 {
154 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
155 "Failed pinning all axes to default.");
156 return false;
157 }
158 continue;
159 }
160
161 hb_tag_t axis_tag = hb_tag_from_string (s, len);
162
163 s = strtok(NULL, ", ");
164 if (!s)
165 {
166 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
167 "Value not specified for axis: %c%c%c%c", HB_UNTAG (axis_tag));
168 return false;
169 }
170
171 gboolean drop;
172 float min, def, max;
173 if (!parse_axis_position(s, &min, &def, &max, &drop, error))
174 return false;
175
176 if (drop)
177 {
178 if (!hb_subset_input_pin_axis_to_default (input,
179 face,
180 axis_tag))
181 {
182 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
183 "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
184 return false;
185 }
186 continue;
187 }
188
189 if (min == def && def == max) {
190 if (!hb_subset_input_pin_axis_location (input,
191 face, axis_tag,
192 def))
193 {
194 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
195 "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
196 return false;
197 }
198 continue;
199 }
200
201 if (!hb_subset_input_set_axis_range (input,
202 face, axis_tag,
203 min, max, def))
204 {
205 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
206 "Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
207 return false;
208 }
209 continue;
210
211 g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
212 "Partial instancing is not supported.");
213 return false;
214 }
215
216 return true;
217 }
218
219 #endif
220
221 #endif
222