OLD | NEW |
1 /* | 1 /* |
2 * Copyright © 2009 Red Hat, Inc. | 2 * Copyright © 2009 Red Hat, Inc. |
3 * Copyright © 2012 Google, Inc. | 3 * Copyright © 2012 Google, Inc. |
4 * | 4 * |
5 * This is part of HarfBuzz, a text shaping library. | 5 * This is part of HarfBuzz, a text shaping library. |
6 * | 6 * |
7 * Permission is hereby granted, without written agreement and without | 7 * Permission is hereby granted, without written agreement and without |
8 * license or royalty fees, to use, copy, modify, and distribute this | 8 * license or royalty fees, to use, copy, modify, and distribute this |
9 * software and its documentation for any purpose, provided that the | 9 * software and its documentation for any purpose, provided that the |
10 * above copyright notice and the following two paragraphs appear in | 10 * above copyright notice and the following two paragraphs appear in |
(...skipping 16 matching lines...) Expand all Loading... |
27 */ | 27 */ |
28 | 28 |
29 #include "hb-private.hh" | 29 #include "hb-private.hh" |
30 | 30 |
31 #include "hb-shaper-private.hh" | 31 #include "hb-shaper-private.hh" |
32 #include "hb-shape-plan-private.hh" | 32 #include "hb-shape-plan-private.hh" |
33 #include "hb-buffer-private.hh" | 33 #include "hb-buffer-private.hh" |
34 #include "hb-font-private.hh" | 34 #include "hb-font-private.hh" |
35 | 35 |
36 | 36 |
37 static void | 37 static bool |
38 parse_space (const char **pp, const char *end) | 38 parse_space (const char **pp, const char *end) |
39 { | 39 { |
40 char c; | 40 while (*pp < end && ISSPACE (**pp)) |
41 while (*pp < end && (c = **pp, ISSPACE (c))) | |
42 (*pp)++; | 41 (*pp)++; |
| 42 return true; |
43 } | 43 } |
44 | 44 |
45 static hb_bool_t | 45 static bool |
46 parse_char (const char **pp, const char *end, char c) | 46 parse_char (const char **pp, const char *end, char c) |
47 { | 47 { |
48 parse_space (pp, end); | 48 parse_space (pp, end); |
49 | 49 |
50 if (*pp == end || **pp != c) | 50 if (*pp == end || **pp != c) |
51 return false; | 51 return false; |
52 | 52 |
53 (*pp)++; | 53 (*pp)++; |
54 return true; | 54 return true; |
55 } | 55 } |
56 | 56 |
57 static hb_bool_t | 57 static bool |
58 parse_uint (const char **pp, const char *end, unsigned int *pv) | 58 parse_uint (const char **pp, const char *end, unsigned int *pv) |
59 { | 59 { |
60 char buf[32]; | 60 char buf[32]; |
61 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); | 61 unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp)); |
62 strncpy (buf, *pp, len); | 62 strncpy (buf, *pp, len); |
63 buf[len] = '\0'; | 63 buf[len] = '\0'; |
64 | 64 |
65 char *p = buf; | 65 char *p = buf; |
66 char *pend = p; | 66 char *pend = p; |
67 unsigned int v; | 67 unsigned int v; |
68 | 68 |
69 /* Intentionally use strtol instead of strtoul, such that | 69 /* Intentionally use strtol instead of strtoul, such that |
70 * -1 turns into "big number"... */ | 70 * -1 turns into "big number"... */ |
71 errno = 0; | 71 errno = 0; |
72 v = strtol (p, &pend, 0); | 72 v = strtol (p, &pend, 0); |
73 if (errno || p == pend) | 73 if (errno || p == pend) |
74 return false; | 74 return false; |
75 | 75 |
76 *pv = v; | 76 *pv = v; |
77 *pp += pend - p; | 77 *pp += pend - p; |
78 return true; | 78 return true; |
79 } | 79 } |
80 | 80 |
81 static hb_bool_t | 81 static bool |
| 82 parse_bool (const char **pp, const char *end, unsigned int *pv) |
| 83 { |
| 84 parse_space (pp, end); |
| 85 |
| 86 const char *p = *pp; |
| 87 while (*pp < end && ISALPHA(**pp)) |
| 88 (*pp)++; |
| 89 |
| 90 /* CSS allows on/off as aliases 1/0. */ |
| 91 if (*pp - p == 2 || 0 == strncmp (p, "on", 2)) |
| 92 *pv = 1; |
| 93 else if (*pp - p == 3 || 0 == strncmp (p, "off", 2)) |
| 94 *pv = 0; |
| 95 else |
| 96 return false; |
| 97 |
| 98 return true; |
| 99 } |
| 100 |
| 101 static bool |
82 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feat
ure) | 102 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feat
ure) |
83 { | 103 { |
84 if (parse_char (pp, end, '-')) | 104 if (parse_char (pp, end, '-')) |
85 feature->value = 0; | 105 feature->value = 0; |
86 else { | 106 else { |
87 parse_char (pp, end, '+'); | 107 parse_char (pp, end, '+'); |
88 feature->value = 1; | 108 feature->value = 1; |
89 } | 109 } |
90 | 110 |
91 return true; | 111 return true; |
92 } | 112 } |
93 | 113 |
94 static hb_bool_t | 114 static bool |
95 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) | 115 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) |
96 { | 116 { |
97 const char *p = *pp; | |
98 char c; | |
99 | |
100 parse_space (pp, end); | 117 parse_space (pp, end); |
101 | 118 |
102 #define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') ||
('0' <= (c) && (c) <= '9')) | 119 char quote = 0; |
103 while (*pp < end && (c = **pp, ISALNUM(c))) | 120 |
| 121 if (*pp < end && (**pp == '\'' || **pp == '"')) |
| 122 { |
| 123 quote = **pp; |
104 (*pp)++; | 124 (*pp)++; |
105 #undef ISALNUM | 125 } |
106 | 126 |
107 if (p == *pp) | 127 const char *p = *pp; |
| 128 while (*pp < end && ISALNUM(**pp)) |
| 129 (*pp)++; |
| 130 |
| 131 if (p == *pp || *pp - p > 4) |
108 return false; | 132 return false; |
109 | 133 |
110 feature->tag = hb_tag_from_string (p, *pp - p); | 134 feature->tag = hb_tag_from_string (p, *pp - p); |
| 135 |
| 136 if (quote) |
| 137 { |
| 138 /* CSS expects exactly four bytes. And we only allow quotations for |
| 139 * CSS compatibility. So, enforce the length. */ |
| 140 if (*pp - p != 4) |
| 141 return false; |
| 142 if (*pp == end || **pp != quote) |
| 143 return false; |
| 144 (*pp)++; |
| 145 } |
| 146 |
111 return true; | 147 return true; |
112 } | 148 } |
113 | 149 |
114 static hb_bool_t | 150 static bool |
115 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) | 151 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) |
116 { | 152 { |
117 parse_space (pp, end); | 153 parse_space (pp, end); |
118 | 154 |
119 hb_bool_t has_start; | 155 bool has_start; |
120 | 156 |
121 feature->start = 0; | 157 feature->start = 0; |
122 feature->end = (unsigned int) -1; | 158 feature->end = (unsigned int) -1; |
123 | 159 |
124 if (!parse_char (pp, end, '[')) | 160 if (!parse_char (pp, end, '[')) |
125 return true; | 161 return true; |
126 | 162 |
127 has_start = parse_uint (pp, end, &feature->start); | 163 has_start = parse_uint (pp, end, &feature->start); |
128 | 164 |
129 if (parse_char (pp, end, ':')) { | 165 if (parse_char (pp, end, ':')) { |
130 parse_uint (pp, end, &feature->end); | 166 parse_uint (pp, end, &feature->end); |
131 } else { | 167 } else { |
132 if (has_start) | 168 if (has_start) |
133 feature->end = feature->start + 1; | 169 feature->end = feature->start + 1; |
134 } | 170 } |
135 | 171 |
136 return parse_char (pp, end, ']'); | 172 return parse_char (pp, end, ']'); |
137 } | 173 } |
138 | 174 |
139 static hb_bool_t | 175 static bool |
140 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *fea
ture) | 176 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *fea
ture) |
141 { | 177 { |
142 return !parse_char (pp, end, '=') || parse_uint (pp, end, &feature->value); | 178 bool had_equal = parse_char (pp, end, '='); |
| 179 bool had_value = parse_uint (pp, end, &feature->value) || |
| 180 parse_bool (pp, end, &feature->value); |
| 181 /* CSS doesn't use equal-sign between tag and value. |
| 182 * If there was an equal-sign, then there *must* be a value. |
| 183 * A value without an eqaul-sign is ok, but not required. */ |
| 184 return !had_equal || had_value; |
143 } | 185 } |
144 | 186 |
145 | 187 |
146 static hb_bool_t | 188 static bool |
147 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) | 189 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) |
148 { | 190 { |
149 return parse_feature_value_prefix (pp, end, feature) && | 191 return parse_feature_value_prefix (pp, end, feature) && |
150 parse_feature_tag (pp, end, feature) && | 192 parse_feature_tag (pp, end, feature) && |
151 parse_feature_indices (pp, end, feature) && | 193 parse_feature_indices (pp, end, feature) && |
152 parse_feature_value_postfix (pp, end, feature) && | 194 parse_feature_value_postfix (pp, end, feature) && |
| 195 parse_space (pp, end) && |
153 *pp == end; | 196 *pp == end; |
154 } | 197 } |
155 | 198 |
156 /** | 199 /** |
157 * hb_feature_from_string: | 200 * hb_feature_from_string: |
158 * @str: (array length=len): | 201 * @str: (array length=len): |
159 * @len: | 202 * @len: |
160 * @feature: (out): | 203 * @feature: (out) (allow-none): |
161 * | 204 * |
162 * | 205 * |
163 * | 206 * |
164 * Return value: | 207 * Return value: |
165 * | 208 * |
166 * Since: 1.0 | 209 * Since: 1.0 |
167 **/ | 210 **/ |
168 hb_bool_t | 211 hb_bool_t |
169 hb_feature_from_string (const char *str, int len, | 212 hb_feature_from_string (const char *str, int len, |
170 hb_feature_t *feature) | 213 hb_feature_t *feature) |
171 { | 214 { |
| 215 hb_feature_t feat; |
| 216 |
172 if (len < 0) | 217 if (len < 0) |
173 len = strlen (str); | 218 len = strlen (str); |
174 | 219 |
175 return parse_one_feature (&str, str + len, feature); | 220 if (likely (parse_one_feature (&str, str + len, &feat))) |
| 221 { |
| 222 if (feature) |
| 223 *feature = feat; |
| 224 return true; |
| 225 } |
| 226 |
| 227 if (feature) |
| 228 memset (feature, 0, sizeof (*feature)); |
| 229 return false; |
176 } | 230 } |
177 | 231 |
178 /** | 232 /** |
179 * hb_feature_to_string: | 233 * hb_feature_to_string: |
180 * @feature: | 234 * @feature: |
181 * @buf: (array length=size): | 235 * @buf: (array length=size): |
182 * @size: | 236 * @size: |
183 * | 237 * |
184 * | 238 * |
185 * | 239 * |
(...skipping 10 matching lines...) Expand all Loading... |
196 if (feature->value == 0) | 250 if (feature->value == 0) |
197 s[len++] = '-'; | 251 s[len++] = '-'; |
198 hb_tag_to_string (feature->tag, s + len); | 252 hb_tag_to_string (feature->tag, s + len); |
199 len += 4; | 253 len += 4; |
200 while (len && s[len - 1] == ' ') | 254 while (len && s[len - 1] == ' ') |
201 len--; | 255 len--; |
202 if (feature->start != 0 || feature->end != (unsigned int) -1) | 256 if (feature->start != 0 || feature->end != (unsigned int) -1) |
203 { | 257 { |
204 s[len++] = '['; | 258 s[len++] = '['; |
205 if (feature->start) | 259 if (feature->start) |
206 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->s
tart)); | 260 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->s
tart)); |
207 if (feature->end != feature->start + 1) { | 261 if (feature->end != feature->start + 1) { |
208 s[len++] = ':'; | 262 s[len++] = ':'; |
209 if (feature->end != (unsigned int) -1) | 263 if (feature->end != (unsigned int) -1) |
210 » len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature-
>end)); | 264 » len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature-
>end)); |
211 } | 265 } |
212 s[len++] = ']'; | 266 s[len++] = ']'; |
213 } | 267 } |
214 if (feature->value > 1) | 268 if (feature->value > 1) |
215 { | 269 { |
216 s[len++] = '='; | 270 s[len++] = '='; |
217 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->val
ue)); | 271 len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->val
ue)); |
218 } | 272 } |
219 assert (len < ARRAY_LENGTH (s)); | 273 assert (len < ARRAY_LENGTH (s)); |
220 len = MIN (len, size - 1); | 274 len = MIN (len, size - 1); |
221 memcpy (buf, s, len); | 275 memcpy (buf, s, len); |
222 buf[len] = '\0'; | 276 buf[len] = '\0'; |
223 } | 277 } |
224 | 278 |
225 | 279 |
226 static const char **static_shaper_list; | 280 static const char **static_shaper_list; |
227 | 281 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 unsigned int i; | 313 unsigned int i; |
260 for (i = 0; i < HB_SHAPERS_COUNT; i++) | 314 for (i = 0; i < HB_SHAPERS_COUNT; i++) |
261 shaper_list[i] = shapers[i].name; | 315 shaper_list[i] = shapers[i].name; |
262 shaper_list[i] = NULL; | 316 shaper_list[i] = NULL; |
263 | 317 |
264 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { | 318 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { |
265 free (shaper_list); | 319 free (shaper_list); |
266 goto retry; | 320 goto retry; |
267 } | 321 } |
268 | 322 |
269 #ifdef HAVE_ATEXIT | 323 #ifdef HB_USE_ATEXIT |
270 atexit (free_static_shaper_list); /* First person registers atexit() callbac
k. */ | 324 atexit (free_static_shaper_list); /* First person registers atexit() callbac
k. */ |
271 #endif | 325 #endif |
272 } | 326 } |
273 | 327 |
274 return shaper_list; | 328 return shaper_list; |
275 } | 329 } |
276 | 330 |
277 | 331 |
278 /** | 332 /** |
279 * hb_shape_full: | 333 * hb_shape_full: |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
322 * Since: 1.0 | 376 * Since: 1.0 |
323 **/ | 377 **/ |
324 void | 378 void |
325 hb_shape (hb_font_t *font, | 379 hb_shape (hb_font_t *font, |
326 hb_buffer_t *buffer, | 380 hb_buffer_t *buffer, |
327 const hb_feature_t *features, | 381 const hb_feature_t *features, |
328 unsigned int num_features) | 382 unsigned int num_features) |
329 { | 383 { |
330 hb_shape_full (font, buffer, features, num_features, NULL); | 384 hb_shape_full (font, buffer, features, num_features, NULL); |
331 } | 385 } |
OLD | NEW |