xref: /aosp_15_r20/external/harfbuzz_ng/util/helper-subset.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
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