43 * which are sequences of Unicode characters that use the same font and have |
43 * which are sequences of Unicode characters that use the same font and have |
44 * the same text direction, script and language. After shaping the buffer |
44 * the same text direction, script and language. After shaping the buffer |
45 * contains the output glyphs and their positions. |
45 * contains the output glyphs and their positions. |
46 **/ |
46 **/ |
47 |
47 |
48 static bool |
|
49 parse_space (const char **pp, const char *end) |
|
50 { |
|
51 while (*pp < end && ISSPACE (**pp)) |
|
52 (*pp)++; |
|
53 return true; |
|
54 } |
|
55 |
|
56 static bool |
|
57 parse_char (const char **pp, const char *end, char c) |
|
58 { |
|
59 parse_space (pp, end); |
|
60 |
|
61 if (*pp == end || **pp != c) |
|
62 return false; |
|
63 |
|
64 (*pp)++; |
|
65 return true; |
|
66 } |
|
67 |
|
68 static bool |
|
69 parse_uint (const char **pp, const char *end, unsigned int *pv) |
|
70 { |
|
71 char buf[32]; |
|
72 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); |
|
73 strncpy (buf, *pp, len); |
|
74 buf[len] = '\0'; |
|
75 |
|
76 char *p = buf; |
|
77 char *pend = p; |
|
78 unsigned int v; |
|
79 |
|
80 /* Intentionally use strtol instead of strtoul, such that |
|
81 * -1 turns into "big number"... */ |
|
82 errno = 0; |
|
83 v = strtol (p, &pend, 0); |
|
84 if (errno || p == pend) |
|
85 return false; |
|
86 |
|
87 *pv = v; |
|
88 *pp += pend - p; |
|
89 return true; |
|
90 } |
|
91 |
|
92 static bool |
|
93 parse_bool (const char **pp, const char *end, unsigned int *pv) |
|
94 { |
|
95 parse_space (pp, end); |
|
96 |
|
97 const char *p = *pp; |
|
98 while (*pp < end && ISALPHA(**pp)) |
|
99 (*pp)++; |
|
100 |
|
101 /* CSS allows on/off as aliases 1/0. */ |
|
102 if (*pp - p == 2 || 0 == strncmp (p, "on", 2)) |
|
103 *pv = 1; |
|
104 else if (*pp - p == 3 || 0 == strncmp (p, "off", 2)) |
|
105 *pv = 0; |
|
106 else |
|
107 return false; |
|
108 |
|
109 return true; |
|
110 } |
|
111 |
|
112 static bool |
|
113 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) |
|
114 { |
|
115 if (parse_char (pp, end, '-')) |
|
116 feature->value = 0; |
|
117 else { |
|
118 parse_char (pp, end, '+'); |
|
119 feature->value = 1; |
|
120 } |
|
121 |
|
122 return true; |
|
123 } |
|
124 |
|
125 static bool |
|
126 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) |
|
127 { |
|
128 parse_space (pp, end); |
|
129 |
|
130 char quote = 0; |
|
131 |
|
132 if (*pp < end && (**pp == '\'' || **pp == '"')) |
|
133 { |
|
134 quote = **pp; |
|
135 (*pp)++; |
|
136 } |
|
137 |
|
138 const char *p = *pp; |
|
139 while (*pp < end && ISALNUM(**pp)) |
|
140 (*pp)++; |
|
141 |
|
142 if (p == *pp || *pp - p > 4) |
|
143 return false; |
|
144 |
|
145 feature->tag = hb_tag_from_string (p, *pp - p); |
|
146 |
|
147 if (quote) |
|
148 { |
|
149 /* CSS expects exactly four bytes. And we only allow quotations for |
|
150 * CSS compatibility. So, enforce the length. */ |
|
151 if (*pp - p != 4) |
|
152 return false; |
|
153 if (*pp == end || **pp != quote) |
|
154 return false; |
|
155 (*pp)++; |
|
156 } |
|
157 |
|
158 return true; |
|
159 } |
|
160 |
|
161 static bool |
|
162 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) |
|
163 { |
|
164 parse_space (pp, end); |
|
165 |
|
166 bool has_start; |
|
167 |
|
168 feature->start = 0; |
|
169 feature->end = (unsigned int) -1; |
|
170 |
|
171 if (!parse_char (pp, end, '[')) |
|
172 return true; |
|
173 |
|
174 has_start = parse_uint (pp, end, &feature->start); |
|
175 |
|
176 if (parse_char (pp, end, ':')) { |
|
177 parse_uint (pp, end, &feature->end); |
|
178 } else { |
|
179 if (has_start) |
|
180 feature->end = feature->start + 1; |
|
181 } |
|
182 |
|
183 return parse_char (pp, end, ']'); |
|
184 } |
|
185 |
|
186 static bool |
|
187 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) |
|
188 { |
|
189 bool had_equal = parse_char (pp, end, '='); |
|
190 bool had_value = parse_uint (pp, end, &feature->value) || |
|
191 parse_bool (pp, end, &feature->value); |
|
192 /* CSS doesn't use equal-sign between tag and value. |
|
193 * If there was an equal-sign, then there *must* be a value. |
|
194 * A value without an eqaul-sign is ok, but not required. */ |
|
195 return !had_equal || had_value; |
|
196 } |
|
197 |
|
198 |
|
199 static bool |
|
200 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) |
|
201 { |
|
202 return parse_feature_value_prefix (pp, end, feature) && |
|
203 parse_feature_tag (pp, end, feature) && |
|
204 parse_feature_indices (pp, end, feature) && |
|
205 parse_feature_value_postfix (pp, end, feature) && |
|
206 parse_space (pp, end) && |
|
207 *pp == end; |
|
208 } |
|
209 |
|
210 /** |
|
211 * hb_feature_from_string: |
|
212 * @str: (array length=len) (element-type uint8_t): a string to parse |
|
213 * @len: length of @str, or -1 if string is %NULL terminated |
|
214 * @feature: (out): the #hb_feature_t to initialize with the parsed values |
|
215 * |
|
216 * Parses a string into a #hb_feature_t. |
|
217 * |
|
218 * TODO: document the syntax here. |
|
219 * |
|
220 * Return value: |
|
221 * %true if @str is successfully parsed, %false otherwise. |
|
222 * |
|
223 * Since: 0.9.5 |
|
224 **/ |
|
225 hb_bool_t |
|
226 hb_feature_from_string (const char *str, int len, |
|
227 hb_feature_t *feature) |
|
228 { |
|
229 hb_feature_t feat; |
|
230 |
|
231 if (len < 0) |
|
232 len = strlen (str); |
|
233 |
|
234 if (likely (parse_one_feature (&str, str + len, &feat))) |
|
235 { |
|
236 if (feature) |
|
237 *feature = feat; |
|
238 return true; |
|
239 } |
|
240 |
|
241 if (feature) |
|
242 memset (feature, 0, sizeof (*feature)); |
|
243 return false; |
|
244 } |
|
245 |
|
246 /** |
|
247 * hb_feature_to_string: |
|
248 * @feature: an #hb_feature_t to convert |
|
249 * @buf: (array length=size) (out): output string |
|
250 * @size: the allocated size of @buf |
|
251 * |
|
252 * Converts a #hb_feature_t into a %NULL-terminated string in the format |
|
253 * understood by hb_feature_from_string(). The client in responsible for |
|
254 * allocating big enough size for @buf, 128 bytes is more than enough. |
|
255 * |
|
256 * Since: 0.9.5 |
|
257 **/ |
|
258 void |
|
259 hb_feature_to_string (hb_feature_t *feature, |
|
260 char *buf, unsigned int size) |
|
261 { |
|
262 if (unlikely (!size)) return; |
|
263 |
|
264 char s[128]; |
|
265 unsigned int len = 0; |
|
266 if (feature->value == 0) |
|
267 s[len++] = '-'; |
|
268 hb_tag_to_string (feature->tag, s + len); |
|
269 len += 4; |
|
270 while (len && s[len - 1] == ' ') |
|
271 len--; |
|
272 if (feature->start != 0 || feature->end != (unsigned int) -1) |
|
273 { |
|
274 s[len++] = '['; |
|
275 if (feature->start) |
|
276 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start)); |
|
277 if (feature->end != feature->start + 1) { |
|
278 s[len++] = ':'; |
|
279 if (feature->end != (unsigned int) -1) |
|
280 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end)); |
|
281 } |
|
282 s[len++] = ']'; |
|
283 } |
|
284 if (feature->value > 1) |
|
285 { |
|
286 s[len++] = '='; |
|
287 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); |
|
288 } |
|
289 assert (len < ARRAY_LENGTH (s)); |
|
290 len = MIN (len, size - 1); |
|
291 memcpy (buf, s, len); |
|
292 buf[len] = '\0'; |
|
293 } |
|
294 |
|
295 |
|
296 static const char **static_shaper_list; |
48 static const char **static_shaper_list; |
297 |
49 |
298 #ifdef HB_USE_ATEXIT |
50 #ifdef HB_USE_ATEXIT |
299 static |
51 static |
300 void free_static_shaper_list (void) |
52 void free_static_shaper_list (void) |