OLD | NEW |
1 /* | 1 /* |
2 * Copyright © 2009 Red Hat, Inc. | 2 * Copyright © 2009 Red Hat, Inc. |
| 3 * Copyright © 2012 Google, Inc. |
3 * | 4 * |
4 * This is part of HarfBuzz, a text shaping library. | 5 * This is part of HarfBuzz, a text shaping library. |
5 * | 6 * |
6 * Permission is hereby granted, without written agreement and without | 7 * Permission is hereby granted, without written agreement and without |
7 * license or royalty fees, to use, copy, modify, and distribute this | 8 * license or royalty fees, to use, copy, modify, and distribute this |
8 * software and its documentation for any purpose, provided that the | 9 * software and its documentation for any purpose, provided that the |
9 * above copyright notice and the following two paragraphs appear in | 10 * above copyright notice and the following two paragraphs appear in |
10 * all copies of this software. | 11 * all copies of this software. |
11 * | 12 * |
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | 15 * 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 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 * DAMAGE. | 17 * DAMAGE. |
17 * | 18 * |
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | 21 * 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 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 * | 24 * |
24 * Red Hat Author(s): Behdad Esfahbod | 25 * Red Hat Author(s): Behdad Esfahbod |
| 26 * Google Author(s): Behdad Esfahbod |
25 */ | 27 */ |
26 | 28 |
27 #include "hb-private.hh" | 29 #include "hb-private.hh" |
28 | 30 |
29 #include "hb-shape.h" | 31 #include "hb-shaper-private.hh" |
30 | 32 #include "hb-shape-plan-private.hh" |
31 #include "hb-buffer-private.hh" | 33 #include "hb-buffer-private.hh" |
32 | 34 #include "hb-font-private.hh" |
33 #ifdef HAVE_GRAPHITE | |
34 #include "hb-graphite2-private.hh" | |
35 #endif | |
36 #ifdef HAVE_UNISCRIBE | |
37 # include "hb-uniscribe-private.hh" | |
38 #endif | |
39 #ifdef HAVE_OT | |
40 # include "hb-ot-shape-private.hh" | |
41 #endif | |
42 #include "hb-fallback-shape-private.hh" | |
43 | |
44 typedef hb_bool_t (*hb_shape_func_t) (hb_font_t *font, | |
45 » » » » hb_buffer_t *buffer, | |
46 » » » » const hb_feature_t *features, | |
47 » » » » unsigned int num_features); | |
48 | |
49 #define HB_SHAPER_IMPLEMENT(name) {#name, _hb_##name##_shape} | |
50 static const struct hb_shaper_pair_t { | |
51 char name[16]; | |
52 hb_shape_func_t func; | |
53 } all_shapers[] = { | |
54 /* v--- Add new shapers in the right place here */ | |
55 #ifdef HAVE_GRAPHITE | |
56 HB_SHAPER_IMPLEMENT (graphite2), | |
57 #endif | |
58 #ifdef HAVE_UNISCRIBE | |
59 HB_SHAPER_IMPLEMENT (uniscribe), | |
60 #endif | |
61 #ifdef HAVE_OT | |
62 HB_SHAPER_IMPLEMENT (ot), | |
63 #endif | |
64 HB_SHAPER_IMPLEMENT (fallback) /* should be last */ | |
65 }; | |
66 #undef HB_SHAPER_IMPLEMENT | |
67 | 35 |
68 | 36 |
69 /* Thread-safe, lock-free, shapers */ | 37 static void |
70 | 38 parse_space (const char **pp, const char *end) |
71 static hb_shaper_pair_t *static_shapers; | |
72 | |
73 static | |
74 void free_static_shapers (void) | |
75 { | 39 { |
76 if (unlikely (static_shapers != all_shapers)) | 40 char c; |
77 free (static_shapers); | 41 #define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='
\v') |
| 42 while (*pp < end && (c = **pp, ISSPACE (c))) |
| 43 (*pp)++; |
| 44 #undef ISSPACE |
78 } | 45 } |
79 | 46 |
80 static const hb_shaper_pair_t * | 47 static hb_bool_t |
81 get_shapers (void) | 48 parse_char (const char **pp, const char *end, char c) |
82 { | 49 { |
83 retry: | 50 parse_space (pp, end); |
84 hb_shaper_pair_t *shapers = (hb_shaper_pair_t *) hb_atomic_ptr_get (&static_sh
apers); | |
85 | 51 |
86 if (unlikely (!shapers)) | 52 if (*pp == end || **pp != c) |
87 { | 53 return false; |
88 char *env = getenv ("HB_SHAPER_LIST"); | |
89 if (!env || !*env) { | |
90 (void) hb_atomic_ptr_cmpexch (&static_shapers, NULL, (const hb_shaper_pair
_t *) all_shapers); | |
91 return (const hb_shaper_pair_t *) all_shapers; | |
92 } | |
93 | 54 |
94 /* Not found; allocate one. */ | 55 (*pp)++; |
95 shapers = (hb_shaper_pair_t *) malloc (sizeof (all_shapers)); | 56 return true; |
96 if (unlikely (!shapers)) | 57 } |
97 return (const hb_shaper_pair_t *) all_shapers; | |
98 memcpy (shapers, all_shapers, sizeof (all_shapers)); | |
99 | 58 |
100 /* Reorder shaper list to prefer requested shapers. */ | 59 static hb_bool_t |
101 unsigned int i = 0; | 60 parse_uint (const char **pp, const char *end, unsigned int *pv) |
102 char *end, *p = env; | 61 { |
103 for (;;) { | 62 char buf[32]; |
104 end = strchr (p, ','); | 63 strncpy (buf, *pp, end - *pp); |
105 if (!end) | 64 buf[ARRAY_LENGTH (buf) - 1] = '\0'; |
106 » end = p + strlen (p); | |
107 | 65 |
108 for (unsigned int j = i; j < ARRAY_LENGTH (all_shapers); j++) | 66 char *p = buf; |
109 » if (end - p == (int) strlen (shapers[j].name) && | 67 char *pend = p; |
110 » 0 == strncmp (shapers[j].name, p, end - p)) | 68 unsigned int v; |
111 » { | |
112 » /* Reorder this shaper to position i */ | |
113 » struct hb_shaper_pair_t t = shapers[j]; | |
114 » memmove (&shapers[i + 1], &shapers[i], sizeof (shapers[i]) * (j - i)); | |
115 » shapers[i] = t; | |
116 » i++; | |
117 » } | |
118 | 69 |
119 if (!*end) | 70 v = strtol (p, &pend, 0); |
120 » break; | |
121 else | |
122 » p = end + 1; | |
123 } | |
124 | 71 |
125 if (!hb_atomic_ptr_cmpexch (&static_shapers, NULL, shapers)) { | 72 if (p == pend) |
126 free (shapers); | 73 return false; |
127 goto retry; | |
128 } | |
129 | 74 |
130 #ifdef HAVE_ATEXIT | 75 *pv = v; |
131 atexit (free_static_shapers); /* First person registers atexit() callback. *
/ | 76 *pp += pend - p; |
132 #endif | 77 return true; |
| 78 } |
| 79 |
| 80 static hb_bool_t |
| 81 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feat
ure) |
| 82 { |
| 83 if (parse_char (pp, end, '-')) |
| 84 feature->value = 0; |
| 85 else { |
| 86 parse_char (pp, end, '+'); |
| 87 feature->value = 1; |
133 } | 88 } |
134 | 89 |
135 return shapers; | 90 return true; |
| 91 } |
| 92 |
| 93 static hb_bool_t |
| 94 parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature) |
| 95 { |
| 96 const char *p = *pp; |
| 97 char c; |
| 98 |
| 99 parse_space (pp, end); |
| 100 |
| 101 #define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') ||
('0' <= (c) && (c) <= '9')) |
| 102 while (*pp < end && (c = **pp, ISALNUM(c))) |
| 103 (*pp)++; |
| 104 #undef ISALNUM |
| 105 |
| 106 if (p == *pp) |
| 107 return false; |
| 108 |
| 109 feature->tag = hb_tag_from_string (p, *pp - p); |
| 110 return true; |
| 111 } |
| 112 |
| 113 static hb_bool_t |
| 114 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature) |
| 115 { |
| 116 parse_space (pp, end); |
| 117 |
| 118 hb_bool_t has_start; |
| 119 |
| 120 feature->start = 0; |
| 121 feature->end = (unsigned int) -1; |
| 122 |
| 123 if (!parse_char (pp, end, '[')) |
| 124 return true; |
| 125 |
| 126 has_start = parse_uint (pp, end, &feature->start); |
| 127 |
| 128 if (parse_char (pp, end, ':')) { |
| 129 parse_uint (pp, end, &feature->end); |
| 130 } else { |
| 131 if (has_start) |
| 132 feature->end = feature->start + 1; |
| 133 } |
| 134 |
| 135 return parse_char (pp, end, ']'); |
| 136 } |
| 137 |
| 138 static hb_bool_t |
| 139 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *fea
ture) |
| 140 { |
| 141 return !parse_char (pp, end, '=') || parse_uint (pp, end, &feature->value); |
136 } | 142 } |
137 | 143 |
138 | 144 |
| 145 static hb_bool_t |
| 146 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) |
| 147 { |
| 148 return parse_feature_value_prefix (pp, end, feature) && |
| 149 parse_feature_tag (pp, end, feature) && |
| 150 parse_feature_indices (pp, end, feature) && |
| 151 parse_feature_value_postfix (pp, end, feature) && |
| 152 *pp == end; |
| 153 } |
| 154 |
| 155 hb_bool_t |
| 156 hb_feature_from_string (const char *str, int len, |
| 157 hb_feature_t *feature) |
| 158 { |
| 159 if (len < 0) |
| 160 len = strlen (str); |
| 161 |
| 162 return parse_one_feature (&str, str + len, feature); |
| 163 } |
| 164 |
| 165 void |
| 166 hb_feature_to_string (hb_feature_t *feature, |
| 167 char *buf, unsigned int size) |
| 168 { |
| 169 if (unlikely (!size)) return; |
| 170 |
| 171 char s[128]; |
| 172 unsigned int len = 0; |
| 173 if (feature->value == 0) |
| 174 s[len++] = '-'; |
| 175 hb_tag_to_string (feature->tag, s + len); |
| 176 len += 4; |
| 177 while (len && s[len - 1] == ' ') |
| 178 len--; |
| 179 if (feature->start != 0 || feature->start != (unsigned int) -1) |
| 180 { |
| 181 s[len++] = '['; |
| 182 if (feature->start) |
| 183 len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->start); |
| 184 if (feature->end != feature->start + 1) { |
| 185 s[len++] = ':'; |
| 186 if (feature->end != (unsigned int) -1) |
| 187 len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->end); |
| 188 } |
| 189 s[len++] = ']'; |
| 190 } |
| 191 if (feature->value > 1) |
| 192 { |
| 193 s[len++] = '='; |
| 194 len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->value); |
| 195 } |
| 196 assert (len < ARRAY_LENGTH (s)); |
| 197 len = MIN (len, size - 1); |
| 198 memcpy (buf, s, len); |
| 199 s[len] = '\0'; |
| 200 } |
| 201 |
| 202 |
139 static const char **static_shaper_list; | 203 static const char **static_shaper_list; |
140 | 204 |
141 static | 205 static |
142 void free_static_shaper_list (void) | 206 void free_static_shaper_list (void) |
143 { | 207 { |
144 free (static_shaper_list); | 208 free (static_shaper_list); |
145 } | 209 } |
146 | 210 |
147 const char ** | 211 const char ** |
148 hb_shape_list_shapers (void) | 212 hb_shape_list_shapers (void) |
149 { | 213 { |
150 retry: | 214 retry: |
151 const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_l
ist); | 215 const char **shaper_list = (const char **) hb_atomic_ptr_get (&static_shaper_l
ist); |
152 | 216 |
153 if (unlikely (!shaper_list)) | 217 if (unlikely (!shaper_list)) |
154 { | 218 { |
155 /* Not found; allocate one. */ | 219 /* Not found; allocate one. */ |
156 shaper_list = (const char **) calloc (1 + ARRAY_LENGTH (all_shapers), sizeof
(const char *)); | 220 shaper_list = (const char **) calloc (1 + HB_SHAPERS_COUNT, sizeof (const ch
ar *)); |
157 if (unlikely (!shaper_list)) { | 221 if (unlikely (!shaper_list)) { |
158 static const char *nil_shaper_list[] = {NULL}; | 222 static const char *nil_shaper_list[] = {NULL}; |
159 return nil_shaper_list; | 223 return nil_shaper_list; |
160 } | 224 } |
161 | 225 |
162 const hb_shaper_pair_t *shapers = get_shapers (); | 226 const hb_shaper_pair_t *shapers = _hb_shapers_get (); |
163 unsigned int i; | 227 unsigned int i; |
164 for (i = 0; i < ARRAY_LENGTH (all_shapers); i++) | 228 for (i = 0; i < HB_SHAPERS_COUNT; i++) |
165 shaper_list[i] = shapers[i].name; | 229 shaper_list[i] = shapers[i].name; |
166 shaper_list[i] = NULL; | 230 shaper_list[i] = NULL; |
167 | 231 |
168 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { | 232 if (!hb_atomic_ptr_cmpexch (&static_shaper_list, NULL, shaper_list)) { |
169 free (shaper_list); | 233 free (shaper_list); |
170 goto retry; | 234 goto retry; |
171 } | 235 } |
172 | 236 |
173 #ifdef HAVE_ATEXIT | 237 #ifdef HAVE_ATEXIT |
174 atexit (free_static_shaper_list); /* First person registers atexit() callbac
k. */ | 238 atexit (free_static_shaper_list); /* First person registers atexit() callbac
k. */ |
175 #endif | 239 #endif |
176 } | 240 } |
177 | 241 |
178 return shaper_list; | 242 return shaper_list; |
179 } | 243 } |
180 | 244 |
181 | 245 |
182 hb_bool_t | 246 hb_bool_t |
183 hb_shape_full (hb_font_t *font, | 247 hb_shape_full (hb_font_t *font, |
184 hb_buffer_t *buffer, | 248 hb_buffer_t *buffer, |
185 const hb_feature_t *features, | 249 const hb_feature_t *features, |
186 unsigned int num_features, | 250 unsigned int num_features, |
187 const char * const *shaper_list) | 251 const char * const *shaper_list) |
188 { | 252 { |
189 hb_font_make_immutable (font); /* So we can safely cache stuff on it */ | 253 if (unlikely (!buffer->len)) |
| 254 return true; |
190 | 255 |
191 if (likely (!shaper_list)) { | 256 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); |
192 const hb_shaper_pair_t *shapers = get_shapers (); | 257 |
193 for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) | 258 buffer->guess_properties (); |
194 if (likely (shapers[i].func (font, buffer, features, num_features))) | 259 |
195 return true; | 260 hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer
->props, features, num_features, shaper_list); |
196 } else { | 261 hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num
_features); |
197 while (*shaper_list) { | 262 hb_shape_plan_destroy (shape_plan); |
198 for (unsigned int i = 0; i < ARRAY_LENGTH (all_shapers); i++) | 263 |
199 » if (0 == strcmp (*shaper_list, all_shapers[i].name)) { | 264 if (res) |
200 » if (likely (all_shapers[i].func (font, buffer, features, num_features)
)) | 265 buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; |
201 » return true; | 266 return res; |
202 » break; | |
203 » } | |
204 shaper_list++; | |
205 } | |
206 } | |
207 return false; | |
208 } | 267 } |
209 | 268 |
210 void | 269 void |
211 hb_shape (hb_font_t *font, | 270 hb_shape (hb_font_t *font, |
212 hb_buffer_t *buffer, | 271 hb_buffer_t *buffer, |
213 const hb_feature_t *features, | 272 const hb_feature_t *features, |
214 unsigned int num_features) | 273 unsigned int num_features) |
215 { | 274 { |
216 hb_shape_full (font, buffer, features, num_features, NULL); | 275 hb_shape_full (font, buffer, features, num_features, NULL); |
217 } | 276 } |
OLD | NEW |