|
1 /* |
|
2 * Copyright © 1998-2004 David Turner and Werner Lemberg |
|
3 * Copyright © 2006 Behdad Esfahbod |
|
4 * Copyright © 2007,2008,2009 Red Hat, Inc. |
|
5 * Copyright © 2012,2013 Google, Inc. |
|
6 * |
|
7 * This is part of HarfBuzz, a text shaping library. |
|
8 * |
|
9 * Permission is hereby granted, without written agreement and without |
|
10 * license or royalty fees, to use, copy, modify, and distribute this |
|
11 * software and its documentation for any purpose, provided that the |
|
12 * above copyright notice and the following two paragraphs appear in |
|
13 * all copies of this software. |
|
14 * |
|
15 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
|
16 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
|
17 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
|
18 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
|
19 * DAMAGE. |
|
20 * |
|
21 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
|
22 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
23 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
|
24 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
|
25 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
|
26 * |
|
27 * Red Hat Author(s): Behdad Esfahbod |
|
28 * Google Author(s): Behdad Esfahbod |
|
29 */ |
|
30 |
|
31 #include "hb-ot-layout-private.hh" |
|
32 |
|
33 #include "hb-ot-layout-gdef-table.hh" |
|
34 #include "hb-ot-layout-gsub-table.hh" |
|
35 #include "hb-ot-layout-gpos-table.hh" |
|
36 #include "hb-ot-layout-jstf-table.hh" |
|
37 |
|
38 #include "hb-ot-map-private.hh" |
|
39 |
|
40 #include <stdlib.h> |
|
41 #include <string.h> |
|
42 |
|
43 |
|
44 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face) |
|
45 |
|
46 hb_ot_layout_t * |
|
47 _hb_ot_layout_create (hb_face_t *face) |
|
48 { |
|
49 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t)); |
|
50 if (unlikely (!layout)) |
|
51 return NULL; |
|
52 |
|
53 layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF)); |
|
54 layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob); |
|
55 |
|
56 layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB)); |
|
57 layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob); |
|
58 |
|
59 layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS)); |
|
60 layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob); |
|
61 |
|
62 layout->gsub_lookup_count = layout->gsub->get_lookup_count (); |
|
63 layout->gpos_lookup_count = layout->gpos->get_lookup_count (); |
|
64 |
|
65 layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); |
|
66 layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t)); |
|
67 |
|
68 if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) || |
|
69 (layout->gpos_lookup_count && !layout->gpos_accels))) |
|
70 { |
|
71 _hb_ot_layout_destroy (layout); |
|
72 return NULL; |
|
73 } |
|
74 |
|
75 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) |
|
76 layout->gsub_accels[i].init (layout->gsub->get_lookup (i)); |
|
77 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) |
|
78 layout->gpos_accels[i].init (layout->gpos->get_lookup (i)); |
|
79 |
|
80 return layout; |
|
81 } |
|
82 |
|
83 void |
|
84 _hb_ot_layout_destroy (hb_ot_layout_t *layout) |
|
85 { |
|
86 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++) |
|
87 layout->gsub_accels[i].fini (layout->gsub->get_lookup (i)); |
|
88 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++) |
|
89 layout->gpos_accels[i].fini (layout->gpos->get_lookup (i)); |
|
90 |
|
91 free (layout->gsub_accels); |
|
92 free (layout->gpos_accels); |
|
93 |
|
94 hb_blob_destroy (layout->gdef_blob); |
|
95 hb_blob_destroy (layout->gsub_blob); |
|
96 hb_blob_destroy (layout->gpos_blob); |
|
97 |
|
98 free (layout); |
|
99 } |
|
100 |
|
101 static inline const OT::GDEF& |
|
102 _get_gdef (hb_face_t *face) |
|
103 { |
|
104 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF); |
|
105 return *hb_ot_layout_from_face (face)->gdef; |
|
106 } |
|
107 static inline const OT::GSUB& |
|
108 _get_gsub (hb_face_t *face) |
|
109 { |
|
110 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB); |
|
111 return *hb_ot_layout_from_face (face)->gsub; |
|
112 } |
|
113 static inline const OT::GPOS& |
|
114 _get_gpos (hb_face_t *face) |
|
115 { |
|
116 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS); |
|
117 return *hb_ot_layout_from_face (face)->gpos; |
|
118 } |
|
119 |
|
120 |
|
121 /* |
|
122 * GDEF |
|
123 */ |
|
124 |
|
125 hb_bool_t |
|
126 hb_ot_layout_has_glyph_classes (hb_face_t *face) |
|
127 { |
|
128 return _get_gdef (face).has_glyph_classes (); |
|
129 } |
|
130 |
|
131 hb_ot_layout_glyph_class_t |
|
132 hb_ot_layout_get_glyph_class (hb_face_t *face, |
|
133 hb_codepoint_t glyph) |
|
134 { |
|
135 return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph); |
|
136 } |
|
137 |
|
138 void |
|
139 hb_ot_layout_get_glyphs_in_class (hb_face_t *face, |
|
140 hb_ot_layout_glyph_class_t klass, |
|
141 hb_set_t *glyphs /* OUT */) |
|
142 { |
|
143 return _get_gdef (face).get_glyphs_in_class (klass, glyphs); |
|
144 } |
|
145 |
|
146 unsigned int |
|
147 hb_ot_layout_get_attach_points (hb_face_t *face, |
|
148 hb_codepoint_t glyph, |
|
149 unsigned int start_offset, |
|
150 unsigned int *point_count /* IN/OUT */, |
|
151 unsigned int *point_array /* OUT */) |
|
152 { |
|
153 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array); |
|
154 } |
|
155 |
|
156 unsigned int |
|
157 hb_ot_layout_get_ligature_carets (hb_font_t *font, |
|
158 hb_direction_t direction, |
|
159 hb_codepoint_t glyph, |
|
160 unsigned int start_offset, |
|
161 unsigned int *caret_count /* IN/OUT */, |
|
162 int *caret_array /* OUT */) |
|
163 { |
|
164 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); |
|
165 } |
|
166 |
|
167 |
|
168 /* |
|
169 * GSUB/GPOS |
|
170 */ |
|
171 |
|
172 static const OT::GSUBGPOS& |
|
173 get_gsubgpos_table (hb_face_t *face, |
|
174 hb_tag_t table_tag) |
|
175 { |
|
176 switch (table_tag) { |
|
177 case HB_OT_TAG_GSUB: return _get_gsub (face); |
|
178 case HB_OT_TAG_GPOS: return _get_gpos (face); |
|
179 default: return OT::Null(OT::GSUBGPOS); |
|
180 } |
|
181 } |
|
182 |
|
183 |
|
184 unsigned int |
|
185 hb_ot_layout_table_get_script_tags (hb_face_t *face, |
|
186 hb_tag_t table_tag, |
|
187 unsigned int start_offset, |
|
188 unsigned int *script_count /* IN/OUT */, |
|
189 hb_tag_t *script_tags /* OUT */) |
|
190 { |
|
191 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
192 |
|
193 return g.get_script_tags (start_offset, script_count, script_tags); |
|
194 } |
|
195 |
|
196 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') |
|
197 |
|
198 hb_bool_t |
|
199 hb_ot_layout_table_find_script (hb_face_t *face, |
|
200 hb_tag_t table_tag, |
|
201 hb_tag_t script_tag, |
|
202 unsigned int *script_index) |
|
203 { |
|
204 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); |
|
205 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
206 |
|
207 if (g.find_script_index (script_tag, script_index)) |
|
208 return true; |
|
209 |
|
210 /* try finding 'DFLT' */ |
|
211 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) |
|
212 return false; |
|
213 |
|
214 /* try with 'dflt'; MS site has had typos and many fonts use it now :(. |
|
215 * including many versions of DejaVu Sans Mono! */ |
|
216 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) |
|
217 return false; |
|
218 |
|
219 /* try with 'latn'; some old fonts put their features there even though |
|
220 they're really trying to support Thai, for example :( */ |
|
221 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) |
|
222 return false; |
|
223 |
|
224 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; |
|
225 return false; |
|
226 } |
|
227 |
|
228 hb_bool_t |
|
229 hb_ot_layout_table_choose_script (hb_face_t *face, |
|
230 hb_tag_t table_tag, |
|
231 const hb_tag_t *script_tags, |
|
232 unsigned int *script_index, |
|
233 hb_tag_t *chosen_script) |
|
234 { |
|
235 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX); |
|
236 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
237 |
|
238 while (*script_tags) |
|
239 { |
|
240 if (g.find_script_index (*script_tags, script_index)) { |
|
241 if (chosen_script) |
|
242 *chosen_script = *script_tags; |
|
243 return true; |
|
244 } |
|
245 script_tags++; |
|
246 } |
|
247 |
|
248 /* try finding 'DFLT' */ |
|
249 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { |
|
250 if (chosen_script) |
|
251 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; |
|
252 return false; |
|
253 } |
|
254 |
|
255 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ |
|
256 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { |
|
257 if (chosen_script) |
|
258 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; |
|
259 return false; |
|
260 } |
|
261 |
|
262 /* try with 'latn'; some old fonts put their features there even though |
|
263 they're really trying to support Thai, for example :( */ |
|
264 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { |
|
265 if (chosen_script) |
|
266 *chosen_script = HB_OT_TAG_LATIN_SCRIPT; |
|
267 return false; |
|
268 } |
|
269 |
|
270 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; |
|
271 if (chosen_script) |
|
272 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; |
|
273 return false; |
|
274 } |
|
275 |
|
276 unsigned int |
|
277 hb_ot_layout_table_get_feature_tags (hb_face_t *face, |
|
278 hb_tag_t table_tag, |
|
279 unsigned int start_offset, |
|
280 unsigned int *feature_count /* IN/OUT */, |
|
281 hb_tag_t *feature_tags /* OUT */) |
|
282 { |
|
283 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
284 |
|
285 return g.get_feature_tags (start_offset, feature_count, feature_tags); |
|
286 } |
|
287 |
|
288 |
|
289 unsigned int |
|
290 hb_ot_layout_script_get_language_tags (hb_face_t *face, |
|
291 hb_tag_t table_tag, |
|
292 unsigned int script_index, |
|
293 unsigned int start_offset, |
|
294 unsigned int *language_count /* IN/OUT */, |
|
295 hb_tag_t *language_tags /* OUT */) |
|
296 { |
|
297 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); |
|
298 |
|
299 return s.get_lang_sys_tags (start_offset, language_count, language_tags); |
|
300 } |
|
301 |
|
302 hb_bool_t |
|
303 hb_ot_layout_script_find_language (hb_face_t *face, |
|
304 hb_tag_t table_tag, |
|
305 unsigned int script_index, |
|
306 hb_tag_t language_tag, |
|
307 unsigned int *language_index) |
|
308 { |
|
309 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); |
|
310 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); |
|
311 |
|
312 if (s.find_lang_sys_index (language_tag, language_index)) |
|
313 return true; |
|
314 |
|
315 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ |
|
316 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) |
|
317 return false; |
|
318 |
|
319 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; |
|
320 return false; |
|
321 } |
|
322 |
|
323 hb_bool_t |
|
324 hb_ot_layout_language_get_required_feature_index (hb_face_t *face, |
|
325 hb_tag_t table_tag, |
|
326 unsigned int script_index, |
|
327 unsigned int language_index, |
|
328 unsigned int *feature_index) |
|
329 { |
|
330 const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index); |
|
331 |
|
332 if (feature_index) *feature_index = l.get_required_feature_index (); |
|
333 |
|
334 return l.has_required_feature (); |
|
335 } |
|
336 |
|
337 unsigned int |
|
338 hb_ot_layout_language_get_feature_indexes (hb_face_t *face, |
|
339 hb_tag_t table_tag, |
|
340 unsigned int script_index, |
|
341 unsigned int language_index, |
|
342 unsigned int start_offset, |
|
343 unsigned int *feature_count /* IN/OUT */, |
|
344 unsigned int *feature_indexes /* OUT */) |
|
345 { |
|
346 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
347 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); |
|
348 |
|
349 return l.get_feature_indexes (start_offset, feature_count, feature_indexes); |
|
350 } |
|
351 |
|
352 unsigned int |
|
353 hb_ot_layout_language_get_feature_tags (hb_face_t *face, |
|
354 hb_tag_t table_tag, |
|
355 unsigned int script_index, |
|
356 unsigned int language_index, |
|
357 unsigned int start_offset, |
|
358 unsigned int *feature_count /* IN/OUT */, |
|
359 hb_tag_t *feature_tags /* OUT */) |
|
360 { |
|
361 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
362 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); |
|
363 |
|
364 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t)); |
|
365 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); |
|
366 |
|
367 if (feature_tags) { |
|
368 unsigned int count = *feature_count; |
|
369 for (unsigned int i = 0; i < count; i++) |
|
370 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); |
|
371 } |
|
372 |
|
373 return ret; |
|
374 } |
|
375 |
|
376 |
|
377 hb_bool_t |
|
378 hb_ot_layout_language_find_feature (hb_face_t *face, |
|
379 hb_tag_t table_tag, |
|
380 unsigned int script_index, |
|
381 unsigned int language_index, |
|
382 hb_tag_t feature_tag, |
|
383 unsigned int *feature_index) |
|
384 { |
|
385 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX); |
|
386 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
387 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); |
|
388 |
|
389 unsigned int num_features = l.get_feature_count (); |
|
390 for (unsigned int i = 0; i < num_features; i++) { |
|
391 unsigned int f_index = l.get_feature_index (i); |
|
392 |
|
393 if (feature_tag == g.get_feature_tag (f_index)) { |
|
394 if (feature_index) *feature_index = f_index; |
|
395 return true; |
|
396 } |
|
397 } |
|
398 |
|
399 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; |
|
400 return false; |
|
401 } |
|
402 |
|
403 unsigned int |
|
404 hb_ot_layout_feature_get_lookups (hb_face_t *face, |
|
405 hb_tag_t table_tag, |
|
406 unsigned int feature_index, |
|
407 unsigned int start_offset, |
|
408 unsigned int *lookup_count /* IN/OUT */, |
|
409 unsigned int *lookup_indexes /* OUT */) |
|
410 { |
|
411 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
|
412 const OT::Feature &f = g.get_feature (feature_index); |
|
413 |
|
414 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); |
|
415 } |
|
416 |
|
417 unsigned int |
|
418 hb_ot_layout_table_get_lookup_count (hb_face_t *face, |
|
419 hb_tag_t table_tag) |
|
420 { |
|
421 switch (table_tag) |
|
422 { |
|
423 case HB_OT_TAG_GSUB: |
|
424 { |
|
425 return hb_ot_layout_from_face (face)->gsub_lookup_count; |
|
426 } |
|
427 case HB_OT_TAG_GPOS: |
|
428 { |
|
429 return hb_ot_layout_from_face (face)->gpos_lookup_count; |
|
430 } |
|
431 } |
|
432 return 0; |
|
433 } |
|
434 |
|
435 static void |
|
436 _hb_ot_layout_collect_lookups_lookups (hb_face_t *face, |
|
437 hb_tag_t table_tag, |
|
438 unsigned int feature_index, |
|
439 hb_set_t *lookup_indexes /* OUT */) |
|
440 { |
|
441 unsigned int lookup_indices[32]; |
|
442 unsigned int offset, len; |
|
443 |
|
444 offset = 0; |
|
445 do { |
|
446 len = ARRAY_LENGTH (lookup_indices); |
|
447 hb_ot_layout_feature_get_lookups (face, |
|
448 table_tag, |
|
449 feature_index, |
|
450 offset, &len, |
|
451 lookup_indices); |
|
452 |
|
453 for (unsigned int i = 0; i < len; i++) |
|
454 lookup_indexes->add (lookup_indices[i]); |
|
455 |
|
456 offset += len; |
|
457 } while (len == ARRAY_LENGTH (lookup_indices)); |
|
458 } |
|
459 |
|
460 static void |
|
461 _hb_ot_layout_collect_lookups_features (hb_face_t *face, |
|
462 hb_tag_t table_tag, |
|
463 unsigned int script_index, |
|
464 unsigned int language_index, |
|
465 const hb_tag_t *features, |
|
466 hb_set_t *lookup_indexes /* OUT */) |
|
467 { |
|
468 if (!features) |
|
469 { |
|
470 unsigned int required_feature_index; |
|
471 if (hb_ot_layout_language_get_required_feature_index (face, |
|
472 table_tag, |
|
473 script_index, |
|
474 language_index, |
|
475 &required_feature_index)) |
|
476 _hb_ot_layout_collect_lookups_lookups (face, |
|
477 table_tag, |
|
478 required_feature_index, |
|
479 lookup_indexes); |
|
480 |
|
481 /* All features */ |
|
482 unsigned int feature_indices[32]; |
|
483 unsigned int offset, len; |
|
484 |
|
485 offset = 0; |
|
486 do { |
|
487 len = ARRAY_LENGTH (feature_indices); |
|
488 hb_ot_layout_language_get_feature_indexes (face, |
|
489 table_tag, |
|
490 script_index, |
|
491 language_index, |
|
492 offset, &len, |
|
493 feature_indices); |
|
494 |
|
495 for (unsigned int i = 0; i < len; i++) |
|
496 _hb_ot_layout_collect_lookups_lookups (face, |
|
497 table_tag, |
|
498 feature_indices[i], |
|
499 lookup_indexes); |
|
500 |
|
501 offset += len; |
|
502 } while (len == ARRAY_LENGTH (feature_indices)); |
|
503 } |
|
504 else |
|
505 { |
|
506 for (; *features; features++) |
|
507 { |
|
508 unsigned int feature_index; |
|
509 if (hb_ot_layout_language_find_feature (face, |
|
510 table_tag, |
|
511 script_index, |
|
512 language_index, |
|
513 *features, |
|
514 &feature_index)) |
|
515 _hb_ot_layout_collect_lookups_lookups (face, |
|
516 table_tag, |
|
517 feature_index, |
|
518 lookup_indexes); |
|
519 } |
|
520 } |
|
521 } |
|
522 |
|
523 static void |
|
524 _hb_ot_layout_collect_lookups_languages (hb_face_t *face, |
|
525 hb_tag_t table_tag, |
|
526 unsigned int script_index, |
|
527 const hb_tag_t *languages, |
|
528 const hb_tag_t *features, |
|
529 hb_set_t *lookup_indexes /* OUT */) |
|
530 { |
|
531 _hb_ot_layout_collect_lookups_features (face, |
|
532 table_tag, |
|
533 script_index, |
|
534 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX, |
|
535 features, |
|
536 lookup_indexes); |
|
537 |
|
538 if (!languages) |
|
539 { |
|
540 /* All languages */ |
|
541 unsigned int count = hb_ot_layout_script_get_language_tags (face, |
|
542 table_tag, |
|
543 script_index, |
|
544 0, NULL, NULL); |
|
545 for (unsigned int language_index = 0; language_index < count; language_index++) |
|
546 _hb_ot_layout_collect_lookups_features (face, |
|
547 table_tag, |
|
548 script_index, |
|
549 language_index, |
|
550 features, |
|
551 lookup_indexes); |
|
552 } |
|
553 else |
|
554 { |
|
555 for (; *languages; languages++) |
|
556 { |
|
557 unsigned int language_index; |
|
558 if (hb_ot_layout_script_find_language (face, |
|
559 table_tag, |
|
560 script_index, |
|
561 *languages, |
|
562 &language_index)) |
|
563 _hb_ot_layout_collect_lookups_features (face, |
|
564 table_tag, |
|
565 script_index, |
|
566 language_index, |
|
567 features, |
|
568 lookup_indexes); |
|
569 } |
|
570 } |
|
571 } |
|
572 |
|
573 void |
|
574 hb_ot_layout_collect_lookups (hb_face_t *face, |
|
575 hb_tag_t table_tag, |
|
576 const hb_tag_t *scripts, |
|
577 const hb_tag_t *languages, |
|
578 const hb_tag_t *features, |
|
579 hb_set_t *lookup_indexes /* OUT */) |
|
580 { |
|
581 if (!scripts) |
|
582 { |
|
583 /* All scripts */ |
|
584 unsigned int count = hb_ot_layout_table_get_script_tags (face, |
|
585 table_tag, |
|
586 0, NULL, NULL); |
|
587 for (unsigned int script_index = 0; script_index < count; script_index++) |
|
588 _hb_ot_layout_collect_lookups_languages (face, |
|
589 table_tag, |
|
590 script_index, |
|
591 languages, |
|
592 features, |
|
593 lookup_indexes); |
|
594 } |
|
595 else |
|
596 { |
|
597 for (; *scripts; scripts++) |
|
598 { |
|
599 unsigned int script_index; |
|
600 if (hb_ot_layout_table_find_script (face, |
|
601 table_tag, |
|
602 *scripts, |
|
603 &script_index)) |
|
604 _hb_ot_layout_collect_lookups_languages (face, |
|
605 table_tag, |
|
606 script_index, |
|
607 languages, |
|
608 features, |
|
609 lookup_indexes); |
|
610 } |
|
611 } |
|
612 } |
|
613 |
|
614 void |
|
615 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, |
|
616 hb_tag_t table_tag, |
|
617 unsigned int lookup_index, |
|
618 hb_set_t *glyphs_before, /* OUT. May be NULL */ |
|
619 hb_set_t *glyphs_input, /* OUT. May be NULL */ |
|
620 hb_set_t *glyphs_after, /* OUT. May be NULL */ |
|
621 hb_set_t *glyphs_output /* OUT. May be NULL */) |
|
622 { |
|
623 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return; |
|
624 |
|
625 OT::hb_collect_glyphs_context_t c (face, |
|
626 glyphs_before, |
|
627 glyphs_input, |
|
628 glyphs_after, |
|
629 glyphs_output); |
|
630 |
|
631 switch (table_tag) |
|
632 { |
|
633 case HB_OT_TAG_GSUB: |
|
634 { |
|
635 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); |
|
636 l.collect_glyphs (&c); |
|
637 return; |
|
638 } |
|
639 case HB_OT_TAG_GPOS: |
|
640 { |
|
641 const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index); |
|
642 l.collect_glyphs (&c); |
|
643 return; |
|
644 } |
|
645 } |
|
646 } |
|
647 |
|
648 |
|
649 /* |
|
650 * OT::GSUB |
|
651 */ |
|
652 |
|
653 hb_bool_t |
|
654 hb_ot_layout_has_substitution (hb_face_t *face) |
|
655 { |
|
656 return &_get_gsub (face) != &OT::Null(OT::GSUB); |
|
657 } |
|
658 |
|
659 hb_bool_t |
|
660 hb_ot_layout_lookup_would_substitute (hb_face_t *face, |
|
661 unsigned int lookup_index, |
|
662 const hb_codepoint_t *glyphs, |
|
663 unsigned int glyphs_length, |
|
664 hb_bool_t zero_context) |
|
665 { |
|
666 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false; |
|
667 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context); |
|
668 } |
|
669 |
|
670 hb_bool_t |
|
671 hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, |
|
672 unsigned int lookup_index, |
|
673 const hb_codepoint_t *glyphs, |
|
674 unsigned int glyphs_length, |
|
675 hb_bool_t zero_context) |
|
676 { |
|
677 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false; |
|
678 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context); |
|
679 |
|
680 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index); |
|
681 |
|
682 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest); |
|
683 } |
|
684 |
|
685 void |
|
686 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer) |
|
687 { |
|
688 OT::GSUB::substitute_start (font, buffer); |
|
689 } |
|
690 |
|
691 void |
|
692 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer) |
|
693 { |
|
694 OT::GSUB::substitute_finish (font, buffer); |
|
695 } |
|
696 |
|
697 void |
|
698 hb_ot_layout_lookup_substitute_closure (hb_face_t *face, |
|
699 unsigned int lookup_index, |
|
700 hb_set_t *glyphs) |
|
701 { |
|
702 OT::hb_closure_context_t c (face, glyphs); |
|
703 |
|
704 const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index); |
|
705 |
|
706 l.closure (&c); |
|
707 } |
|
708 |
|
709 /* |
|
710 * OT::GPOS |
|
711 */ |
|
712 |
|
713 hb_bool_t |
|
714 hb_ot_layout_has_positioning (hb_face_t *face) |
|
715 { |
|
716 return &_get_gpos (face) != &OT::Null(OT::GPOS); |
|
717 } |
|
718 |
|
719 void |
|
720 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) |
|
721 { |
|
722 OT::GPOS::position_start (font, buffer); |
|
723 } |
|
724 |
|
725 void |
|
726 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer) |
|
727 { |
|
728 OT::GPOS::position_finish (font, buffer); |
|
729 } |
|
730 |
|
731 hb_bool_t |
|
732 hb_ot_layout_get_size_params (hb_face_t *face, |
|
733 unsigned int *design_size, /* OUT. May be NULL */ |
|
734 unsigned int *subfamily_id, /* OUT. May be NULL */ |
|
735 unsigned int *subfamily_name_id, /* OUT. May be NULL */ |
|
736 unsigned int *range_start, /* OUT. May be NULL */ |
|
737 unsigned int *range_end /* OUT. May be NULL */) |
|
738 { |
|
739 const OT::GPOS &gpos = _get_gpos (face); |
|
740 const hb_tag_t tag = HB_TAG ('s','i','z','e'); |
|
741 |
|
742 unsigned int num_features = gpos.get_feature_count (); |
|
743 for (unsigned int i = 0; i < num_features; i++) |
|
744 { |
|
745 if (tag == gpos.get_feature_tag (i)) |
|
746 { |
|
747 const OT::Feature &f = gpos.get_feature (i); |
|
748 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); |
|
749 |
|
750 if (params.designSize) |
|
751 { |
|
752 #define PARAM(a, A) if (a) *a = params.A |
|
753 PARAM (design_size, designSize); |
|
754 PARAM (subfamily_id, subfamilyID); |
|
755 PARAM (subfamily_name_id, subfamilyNameID); |
|
756 PARAM (range_start, rangeStart); |
|
757 PARAM (range_end, rangeEnd); |
|
758 #undef PARAM |
|
759 |
|
760 return true; |
|
761 } |
|
762 } |
|
763 } |
|
764 |
|
765 #define PARAM(a, A) if (a) *a = 0 |
|
766 PARAM (design_size, designSize); |
|
767 PARAM (subfamily_id, subfamilyID); |
|
768 PARAM (subfamily_name_id, subfamilyNameID); |
|
769 PARAM (range_start, rangeStart); |
|
770 PARAM (range_end, rangeEnd); |
|
771 #undef PARAM |
|
772 |
|
773 return false; |
|
774 } |
|
775 |
|
776 |
|
777 /* |
|
778 * Parts of different types are implemented here such that they have direct |
|
779 * access to GSUB/GPOS lookups. |
|
780 */ |
|
781 |
|
782 |
|
783 struct GSUBProxy |
|
784 { |
|
785 static const unsigned int table_index = 0; |
|
786 static const bool inplace = false; |
|
787 typedef OT::SubstLookup Lookup; |
|
788 |
|
789 GSUBProxy (hb_face_t *face) : |
|
790 table (*hb_ot_layout_from_face (face)->gsub), |
|
791 accels (hb_ot_layout_from_face (face)->gsub_accels) {} |
|
792 |
|
793 const OT::GSUB &table; |
|
794 const hb_ot_layout_lookup_accelerator_t *accels; |
|
795 }; |
|
796 |
|
797 struct GPOSProxy |
|
798 { |
|
799 static const unsigned int table_index = 1; |
|
800 static const bool inplace = true; |
|
801 typedef OT::PosLookup Lookup; |
|
802 |
|
803 GPOSProxy (hb_face_t *face) : |
|
804 table (*hb_ot_layout_from_face (face)->gpos), |
|
805 accels (hb_ot_layout_from_face (face)->gpos_accels) {} |
|
806 |
|
807 const OT::GPOS &table; |
|
808 const hb_ot_layout_lookup_accelerator_t *accels; |
|
809 }; |
|
810 |
|
811 |
|
812 template <typename Lookup> |
|
813 static inline bool apply_once (OT::hb_apply_context_t *c, |
|
814 const Lookup &lookup) |
|
815 { |
|
816 if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props)) |
|
817 return false; |
|
818 return lookup.dispatch (c); |
|
819 } |
|
820 |
|
821 template <typename Proxy> |
|
822 static inline bool |
|
823 apply_string (OT::hb_apply_context_t *c, |
|
824 const typename Proxy::Lookup &lookup, |
|
825 const hb_ot_layout_lookup_accelerator_t &accel) |
|
826 { |
|
827 bool ret = false; |
|
828 hb_buffer_t *buffer = c->buffer; |
|
829 |
|
830 if (unlikely (!buffer->len || !c->lookup_mask)) |
|
831 return false; |
|
832 |
|
833 c->set_lookup (lookup); |
|
834 |
|
835 if (likely (!lookup.is_reverse ())) |
|
836 { |
|
837 /* in/out forward substitution/positioning */ |
|
838 if (Proxy::table_index == 0) |
|
839 buffer->clear_output (); |
|
840 buffer->idx = 0; |
|
841 |
|
842 while (buffer->idx < buffer->len) |
|
843 { |
|
844 if (accel.digest.may_have (buffer->cur().codepoint) && |
|
845 (buffer->cur().mask & c->lookup_mask) && |
|
846 apply_once (c, lookup)) |
|
847 ret = true; |
|
848 else |
|
849 buffer->next_glyph (); |
|
850 } |
|
851 if (ret) |
|
852 { |
|
853 if (!Proxy::inplace) |
|
854 buffer->swap_buffers (); |
|
855 else |
|
856 assert (!buffer->has_separate_output ()); |
|
857 } |
|
858 } |
|
859 else |
|
860 { |
|
861 /* in-place backward substitution/positioning */ |
|
862 if (Proxy::table_index == 0) |
|
863 buffer->remove_output (); |
|
864 buffer->idx = buffer->len - 1; |
|
865 do |
|
866 { |
|
867 if (accel.digest.may_have (buffer->cur().codepoint) && |
|
868 (buffer->cur().mask & c->lookup_mask) && |
|
869 apply_once (c, lookup)) |
|
870 ret = true; |
|
871 /* The reverse lookup doesn't "advance" cursor (for good reason). */ |
|
872 buffer->idx--; |
|
873 |
|
874 } |
|
875 while ((int) buffer->idx >= 0); |
|
876 } |
|
877 |
|
878 return ret; |
|
879 } |
|
880 |
|
881 template <typename Proxy> |
|
882 inline void hb_ot_map_t::apply (const Proxy &proxy, |
|
883 const hb_ot_shape_plan_t *plan, |
|
884 hb_font_t *font, |
|
885 hb_buffer_t *buffer) const |
|
886 { |
|
887 const unsigned int table_index = proxy.table_index; |
|
888 unsigned int i = 0; |
|
889 OT::hb_apply_context_t c (table_index, font, buffer); |
|
890 c.set_recurse_func (Proxy::Lookup::apply_recurse_func); |
|
891 |
|
892 for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) { |
|
893 const stage_map_t *stage = &stages[table_index][stage_index]; |
|
894 for (; i < stage->last_lookup; i++) |
|
895 { |
|
896 unsigned int lookup_index = lookups[table_index][i].index; |
|
897 c.set_lookup_mask (lookups[table_index][i].mask); |
|
898 c.set_auto_zwj (lookups[table_index][i].auto_zwj); |
|
899 apply_string<Proxy> (&c, |
|
900 proxy.table.get_lookup (lookup_index), |
|
901 proxy.accels[lookup_index]); |
|
902 } |
|
903 |
|
904 if (stage->pause_func) |
|
905 { |
|
906 buffer->clear_output (); |
|
907 stage->pause_func (plan, font, buffer); |
|
908 } |
|
909 } |
|
910 } |
|
911 |
|
912 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const |
|
913 { |
|
914 GSUBProxy proxy (font->face); |
|
915 apply (proxy, plan, font, buffer); |
|
916 } |
|
917 |
|
918 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const |
|
919 { |
|
920 GPOSProxy proxy (font->face); |
|
921 apply (proxy, plan, font, buffer); |
|
922 } |
|
923 |
|
924 HB_INTERNAL void |
|
925 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c, |
|
926 const OT::SubstLookup &lookup, |
|
927 const hb_ot_layout_lookup_accelerator_t &accel) |
|
928 { |
|
929 apply_string<GSUBProxy> (c, lookup, accel); |
|
930 } |