|
1 /* |
|
2 * Copyright © 2009 Red Hat, Inc. |
|
3 * Copyright © 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 * Red Hat Author(s): Behdad Esfahbod |
|
26 * Google Author(s): Behdad Esfahbod |
|
27 */ |
|
28 |
|
29 #include "hb-private.hh" |
|
30 |
|
31 #include "hb-shaper-private.hh" |
|
32 #include "hb-shape-plan-private.hh" |
|
33 #include "hb-buffer-private.hh" |
|
34 #include "hb-font-private.hh" |
|
35 |
|
36 |
|
37 static void |
|
38 parse_space (const char **pp, const char *end) |
|
39 { |
|
40 char c; |
|
41 while (*pp < end && (c = **pp, ISSPACE (c))) |
|
42 (*pp)++; |
|
43 } |
|
44 |
|
45 static hb_bool_t |
|
46 parse_char (const char **pp, const char *end, char c) |
|
47 { |
|
48 parse_space (pp, end); |
|
49 |
|
50 if (*pp == end || **pp != c) |
|
51 return false; |
|
52 |
|
53 (*pp)++; |
|
54 return true; |
|
55 } |
|
56 |
|
57 static hb_bool_t |
|
58 parse_uint (const char **pp, const char *end, unsigned int *pv) |
|
59 { |
|
60 char buf[32]; |
|
61 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); |
|
62 strncpy (buf, *pp, len); |
|
63 buf[len] = '\0'; |
|
64 |
|
65 char *p = buf; |
|
66 char *pend = p; |
|
67 unsigned int v; |
|
68 |
|
69 /* Intentionally use strtol instead of strtoul, such that |
|
70 * -1 turns into "big number"... */ |
|
71 errno = 0; |
|
72 v = strtol (p, &pend, 0); |
|
73 if (errno || p == pend) |
|
74 return false; |
|
75 |
|
76 *pv = v; |
|
77 *pp += pend - p; |
|
78 return true; |
|
79 } |
|
80 |
|
81 static hb_bool_t |
|
82 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature) |
|
83 { |
|
84 if (parse_char (pp, end, '-')) |
|
85 feature->value = 0; |
|
86 else { |
|
87 parse_char (pp, end, '+'); |
|
88 feature->value = 1; |
|
89 } |
|
90 |
|
91 return true; |
|
92 } |
|
93 |
|
94 static hb_bool_t |
|
95 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) |
|
96 { |
|
97 const char *p = *pp; |
|
98 char c; |
|
99 |
|
100 parse_space (pp, end); |
|
101 |
|
102 #define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9')) |
|
103 while (*pp < end && (c = **pp, ISALNUM(c))) |
|
104 (*pp)++; |
|
105 #undef ISALNUM |
|
106 |
|
107 if (p == *pp) |
|
108 return false; |
|
109 |
|
110 feature->tag = hb_tag_from_string (p, *pp - p); |
|
111 return true; |
|
112 } |
|
113 |
|
114 static hb_bool_t |
|
115 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) |
|
116 { |
|
117 parse_space (pp, end); |
|
118 |
|
119 hb_bool_t has_start; |
|
120 |
|
121 feature->start = 0; |
|
122 feature->end = (unsigned int) -1; |
|
123 |
|
124 if (!parse_char (pp, end, '[')) |
|
125 return true; |
|
126 |
|
127 has_start = parse_uint (pp, end, &feature->start); |
|
128 |
|
129 if (parse_char (pp, end, ':')) { |
|
130 parse_uint (pp, end, &feature->end); |
|
131 } else { |
|
132 if (has_start) |
|
133 feature->end = feature->start + 1; |
|
134 } |
|
135 |
|
136 return parse_char (pp, end, ']'); |
|
137 } |
|
138 |
|
139 static hb_bool_t |
|
140 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature) |
|
141 { |
|
142 return !parse_char (pp, end, '=') || parse_uint (pp, end, &feature->value); |
|
143 } |
|
144 |
|
145 |
|
146 static hb_bool_t |
|
147 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) |
|
148 { |
|
149 return parse_feature_value_prefix (pp, end, feature) && |
|
150 parse_feature_tag (pp, end, feature) && |
|
151 parse_feature_indices (pp, end, feature) && |
|
152 parse_feature_value_postfix (pp, end, feature) && |
|
153 *pp == end; |
|
154 } |
|
155 |
|
156 /** |
|
157 * hb_feature_from_string: |
|
158 * @str: (array length=len): |
|
159 * @len: |
|
160 * @feature: (out): |
|
161 * |
|
162 * |
|
163 * |
|
164 * Return value: |
|
165 * |
|
166 * Since: 1.0 |
|
167 **/ |
|
168 hb_bool_t |
|
169 hb_feature_from_string (const char *str, int len, |
|
170 hb_feature_t *feature) |
|
171 { |
|
172 if (len < 0) |
|
173 len = strlen (str); |
|
174 |
|
175 return parse_one_feature (&str, str + len, feature); |
|
176 } |
|
177 |
|
178 /** |
|
179 * hb_feature_to_string: |
|
180 * @feature: |
|
181 * @buf: (array length=size): |
|
182 * @size: |
|
183 * |
|
184 * |
|
185 * |
|
186 * Since: 1.0 |
|
187 **/ |
|
188 void |
|
189 hb_feature_to_string (hb_feature_t *feature, |
|
190 char *buf, unsigned int size) |
|
191 { |
|
192 if (unlikely (!size)) return; |
|
193 |
|
194 char s[128]; |
|
195 unsigned int len = 0; |
|
196 if (feature->value == 0) |
|
197 s[len++] = '-'; |
|
198 hb_tag_to_string (feature->tag, s + len); |
|
199 len += 4; |
|
200 while (len && s[len - 1] == ' ') |
|
201 len--; |
|
202 if (feature->start != 0 || feature->end != (unsigned int) -1) |
|
203 { |
|
204 s[len++] = '['; |
|
205 if (feature->start) |
|
206 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->start)); |
|
207 if (feature->end != feature->start + 1) { |
|
208 s[len++] = ':'; |
|
209 if (feature->end != (unsigned int) -1) |
|
210 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->end)); |
|
211 } |
|
212 s[len++] = ']'; |
|
213 } |
|
214 if (feature->value > 1) |
|
215 { |
|
216 s[len++] = '='; |
|
217 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->value)); |
|
218 } |
|
219 assert (len < ARRAY_LENGTH (s)); |
|
220 len = MIN (len, size - 1); |
|
221 memcpy (buf, s, len); |
|
222 buf[len] = '\0'; |
|
223 } |
|
224 |
|
225 |
|
226 static const char **static_shaper_list; |
|
227 |
|
228 static inline |
|
229 void free_static_shaper_list (void) |
|
230 { |
|
231 free (static_shaper_list); |
|
232 } |
|
233 |
|
234 /** |
|
235 * hb_shape_list_shapers: |
|
236 * |
|
237 * |
|
238 * |
|
239 * Return value: (transfer none): |
|
240 * |
|
241 * Since: 1.0 |
|
242 **/ |
|
243 const char ** |
|
244 hb_shape_list_shapers (void) |
|
245 { |
|
246 retry: |
|
247 const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_list); |
|
248 |
|
249 if (unlikely (!shaper_list)) |
|
250 { |
|
251 /* Not found; allocate one. */ |
|
252 shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const char *)); |
|
253 if (unlikely (!shaper_list)) { |
|
254 static const char *nil_shaper_list[] = {NULL}; |
|
255 return nil_shaper_list; |
|
256 } |
|
257 |
|
258 const hb_shaper_pair_t *shapers = _hb_shapers_get (); |
|
259 unsigned int i; |
|
260 for (i = 0; i < HB_SHAPERS_COUNT; i++) |
|
261 shaper_list[i] = shapers[i].name; |
|
262 shaper_list[i] = NULL; |
|
263 |
|
264 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { |
|
265 free (shaper_list); |
|
266 goto retry; |
|
267 } |
|
268 |
|
269 #ifdef HAVE_ATEXIT |
|
270 atexit (free_static_shaper_list); /* First person registers atexit() callback. */ |
|
271 #endif |
|
272 } |
|
273 |
|
274 return shaper_list; |
|
275 } |
|
276 |
|
277 |
|
278 /** |
|
279 * hb_shape_full: |
|
280 * @font: a font. |
|
281 * @buffer: a buffer. |
|
282 * @features: (array length=num_features): |
|
283 * @num_features: |
|
284 * @shaper_list: (array zero-terminated=1): |
|
285 * |
|
286 * |
|
287 * |
|
288 * Return value: |
|
289 * |
|
290 * Since: 1.0 |
|
291 **/ |
|
292 hb_bool_t |
|
293 hb_shape_full (hb_font_t *font, |
|
294 hb_buffer_t *buffer, |
|
295 const hb_feature_t *features, |
|
296 unsigned int num_features, |
|
297 const char * const *shaper_list) |
|
298 { |
|
299 if (unlikely (!buffer->len)) |
|
300 return true; |
|
301 |
|
302 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); |
|
303 |
|
304 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list); |
|
305 hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); |
|
306 hb_shape_plan_destroy (shape_plan); |
|
307 |
|
308 if (res) |
|
309 buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; |
|
310 return res; |
|
311 } |
|
312 |
|
313 /** |
|
314 * hb_shape: |
|
315 * @font: a font. |
|
316 * @buffer: a buffer. |
|
317 * @features: (array length=num_features): |
|
318 * @num_features: |
|
319 * |
|
320 * |
|
321 * |
|
322 * Since: 1.0 |
|
323 **/ |
|
324 void |
|
325 hb_shape (hb_font_t *font, |
|
326 hb_buffer_t *buffer, |
|
327 const hb_feature_t *features, |
|
328 unsigned int num_features) |
|
329 { |
|
330 hb_shape_full (font, buffer, features, num_features, NULL); |
|
331 } |