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
HB_SHAPER_DATA_ENSURE_DECLARE(ot,face)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
_hb_ot_layout_destroy(hb_ot_layout_t * layout)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 ();
88 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
89 layout->gpos_accels[i].fini ();
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&
_get_gdef(hb_face_t * face)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&
_get_gsub(hb_face_t * face)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&
_get_gpos(hb_face_t * face)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
hb_ot_layout_has_glyph_classes(hb_face_t * face)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
hb_ot_layout_get_glyph_class(hb_face_t * face,hb_codepoint_t glyph)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
hb_ot_layout_get_glyphs_in_class(hb_face_t * face,hb_ot_layout_glyph_class_t klass,hb_set_t * glyphs)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
hb_ot_layout_get_attach_points(hb_face_t * face,hb_codepoint_t glyph,unsigned int start_offset,unsigned int * point_count,unsigned int * point_array)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
hb_ot_layout_get_ligature_carets(hb_font_t * font,hb_direction_t direction,hb_codepoint_t glyph,unsigned int start_offset,unsigned int * caret_count,int * caret_array)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&
get_gsubgpos_table(hb_face_t * face,hb_tag_t table_tag)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
hb_ot_layout_table_get_script_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int start_offset,unsigned int * script_count,hb_tag_t * script_tags)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
hb_ot_layout_table_find_script(hb_face_t * face,hb_tag_t table_tag,hb_tag_t script_tag,unsigned int * script_index)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
hb_ot_layout_table_choose_script(hb_face_t * face,hb_tag_t table_tag,const hb_tag_t * script_tags,unsigned int * script_index,hb_tag_t * chosen_script)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
hb_ot_layout_table_get_feature_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int start_offset,unsigned int * feature_count,hb_tag_t * feature_tags)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
hb_ot_layout_script_get_language_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int start_offset,unsigned int * language_count,hb_tag_t * language_tags)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
hb_ot_layout_script_find_language(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,hb_tag_t language_tag,unsigned int * language_index)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
hb_ot_layout_language_get_required_feature_index(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,unsigned int * feature_index)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 return hb_ot_layout_language_get_required_feature (face,
331 table_tag,
332 script_index,
333 language_index,
334 feature_index,
335 NULL);
336 }
337
338 hb_bool_t
hb_ot_layout_language_get_required_feature(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,unsigned int * feature_index,hb_tag_t * feature_tag)339 hb_ot_layout_language_get_required_feature (hb_face_t *face,
340 hb_tag_t table_tag,
341 unsigned int script_index,
342 unsigned int language_index,
343 unsigned int *feature_index,
344 hb_tag_t *feature_tag)
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 unsigned int index = l.get_required_feature_index ();
350 if (feature_index) *feature_index = index;
351 if (feature_tag) *feature_tag = g.get_feature_tag (index);
352
353 return l.has_required_feature ();
354 }
355
356 unsigned int
hb_ot_layout_language_get_feature_indexes(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,unsigned int start_offset,unsigned int * feature_count,unsigned int * feature_indexes)357 hb_ot_layout_language_get_feature_indexes (hb_face_t *face,
358 hb_tag_t table_tag,
359 unsigned int script_index,
360 unsigned int language_index,
361 unsigned int start_offset,
362 unsigned int *feature_count /* IN/OUT */,
363 unsigned int *feature_indexes /* OUT */)
364 {
365 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
366 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
367
368 return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
369 }
370
371 unsigned int
hb_ot_layout_language_get_feature_tags(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,unsigned int start_offset,unsigned int * feature_count,hb_tag_t * feature_tags)372 hb_ot_layout_language_get_feature_tags (hb_face_t *face,
373 hb_tag_t table_tag,
374 unsigned int script_index,
375 unsigned int language_index,
376 unsigned int start_offset,
377 unsigned int *feature_count /* IN/OUT */,
378 hb_tag_t *feature_tags /* OUT */)
379 {
380 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
381 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
382
383 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
384 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
385
386 if (feature_tags) {
387 unsigned int count = *feature_count;
388 for (unsigned int i = 0; i < count; i++)
389 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
390 }
391
392 return ret;
393 }
394
395
396 hb_bool_t
hb_ot_layout_language_find_feature(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,hb_tag_t feature_tag,unsigned int * feature_index)397 hb_ot_layout_language_find_feature (hb_face_t *face,
398 hb_tag_t table_tag,
399 unsigned int script_index,
400 unsigned int language_index,
401 hb_tag_t feature_tag,
402 unsigned int *feature_index)
403 {
404 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
405 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
406 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
407
408 unsigned int num_features = l.get_feature_count ();
409 for (unsigned int i = 0; i < num_features; i++) {
410 unsigned int f_index = l.get_feature_index (i);
411
412 if (feature_tag == g.get_feature_tag (f_index)) {
413 if (feature_index) *feature_index = f_index;
414 return true;
415 }
416 }
417
418 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
419 return false;
420 }
421
422 unsigned int
hb_ot_layout_feature_get_lookups(hb_face_t * face,hb_tag_t table_tag,unsigned int feature_index,unsigned int start_offset,unsigned int * lookup_count,unsigned int * lookup_indexes)423 hb_ot_layout_feature_get_lookups (hb_face_t *face,
424 hb_tag_t table_tag,
425 unsigned int feature_index,
426 unsigned int start_offset,
427 unsigned int *lookup_count /* IN/OUT */,
428 unsigned int *lookup_indexes /* OUT */)
429 {
430 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
431 const OT::Feature &f = g.get_feature (feature_index);
432
433 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
434 }
435
436 unsigned int
hb_ot_layout_table_get_lookup_count(hb_face_t * face,hb_tag_t table_tag)437 hb_ot_layout_table_get_lookup_count (hb_face_t *face,
438 hb_tag_t table_tag)
439 {
440 switch (table_tag)
441 {
442 case HB_OT_TAG_GSUB:
443 {
444 return hb_ot_layout_from_face (face)->gsub_lookup_count;
445 }
446 case HB_OT_TAG_GPOS:
447 {
448 return hb_ot_layout_from_face (face)->gpos_lookup_count;
449 }
450 }
451 return 0;
452 }
453
454 static void
_hb_ot_layout_collect_lookups_lookups(hb_face_t * face,hb_tag_t table_tag,unsigned int feature_index,hb_set_t * lookup_indexes)455 _hb_ot_layout_collect_lookups_lookups (hb_face_t *face,
456 hb_tag_t table_tag,
457 unsigned int feature_index,
458 hb_set_t *lookup_indexes /* OUT */)
459 {
460 unsigned int lookup_indices[32];
461 unsigned int offset, len;
462
463 offset = 0;
464 do {
465 len = ARRAY_LENGTH (lookup_indices);
466 hb_ot_layout_feature_get_lookups (face,
467 table_tag,
468 feature_index,
469 offset, &len,
470 lookup_indices);
471
472 for (unsigned int i = 0; i < len; i++)
473 lookup_indexes->add (lookup_indices[i]);
474
475 offset += len;
476 } while (len == ARRAY_LENGTH (lookup_indices));
477 }
478
479 static void
_hb_ot_layout_collect_lookups_features(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,unsigned int language_index,const hb_tag_t * features,hb_set_t * lookup_indexes)480 _hb_ot_layout_collect_lookups_features (hb_face_t *face,
481 hb_tag_t table_tag,
482 unsigned int script_index,
483 unsigned int language_index,
484 const hb_tag_t *features,
485 hb_set_t *lookup_indexes /* OUT */)
486 {
487 if (!features)
488 {
489 unsigned int required_feature_index;
490 if (hb_ot_layout_language_get_required_feature (face,
491 table_tag,
492 script_index,
493 language_index,
494 &required_feature_index,
495 NULL))
496 _hb_ot_layout_collect_lookups_lookups (face,
497 table_tag,
498 required_feature_index,
499 lookup_indexes);
500
501 /* All features */
502 unsigned int feature_indices[32];
503 unsigned int offset, len;
504
505 offset = 0;
506 do {
507 len = ARRAY_LENGTH (feature_indices);
508 hb_ot_layout_language_get_feature_indexes (face,
509 table_tag,
510 script_index,
511 language_index,
512 offset, &len,
513 feature_indices);
514
515 for (unsigned int i = 0; i < len; i++)
516 _hb_ot_layout_collect_lookups_lookups (face,
517 table_tag,
518 feature_indices[i],
519 lookup_indexes);
520
521 offset += len;
522 } while (len == ARRAY_LENGTH (feature_indices));
523 }
524 else
525 {
526 for (; *features; features++)
527 {
528 unsigned int feature_index;
529 if (hb_ot_layout_language_find_feature (face,
530 table_tag,
531 script_index,
532 language_index,
533 *features,
534 &feature_index))
535 _hb_ot_layout_collect_lookups_lookups (face,
536 table_tag,
537 feature_index,
538 lookup_indexes);
539 }
540 }
541 }
542
543 static void
_hb_ot_layout_collect_lookups_languages(hb_face_t * face,hb_tag_t table_tag,unsigned int script_index,const hb_tag_t * languages,const hb_tag_t * features,hb_set_t * lookup_indexes)544 _hb_ot_layout_collect_lookups_languages (hb_face_t *face,
545 hb_tag_t table_tag,
546 unsigned int script_index,
547 const hb_tag_t *languages,
548 const hb_tag_t *features,
549 hb_set_t *lookup_indexes /* OUT */)
550 {
551 _hb_ot_layout_collect_lookups_features (face,
552 table_tag,
553 script_index,
554 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
555 features,
556 lookup_indexes);
557
558 if (!languages)
559 {
560 /* All languages */
561 unsigned int count = hb_ot_layout_script_get_language_tags (face,
562 table_tag,
563 script_index,
564 0, NULL, NULL);
565 for (unsigned int language_index = 0; language_index < count; language_index++)
566 _hb_ot_layout_collect_lookups_features (face,
567 table_tag,
568 script_index,
569 language_index,
570 features,
571 lookup_indexes);
572 }
573 else
574 {
575 for (; *languages; languages++)
576 {
577 unsigned int language_index;
578 if (hb_ot_layout_script_find_language (face,
579 table_tag,
580 script_index,
581 *languages,
582 &language_index))
583 _hb_ot_layout_collect_lookups_features (face,
584 table_tag,
585 script_index,
586 language_index,
587 features,
588 lookup_indexes);
589 }
590 }
591 }
592
593 void
hb_ot_layout_collect_lookups(hb_face_t * face,hb_tag_t table_tag,const hb_tag_t * scripts,const hb_tag_t * languages,const hb_tag_t * features,hb_set_t * lookup_indexes)594 hb_ot_layout_collect_lookups (hb_face_t *face,
595 hb_tag_t table_tag,
596 const hb_tag_t *scripts,
597 const hb_tag_t *languages,
598 const hb_tag_t *features,
599 hb_set_t *lookup_indexes /* OUT */)
600 {
601 if (!scripts)
602 {
603 /* All scripts */
604 unsigned int count = hb_ot_layout_table_get_script_tags (face,
605 table_tag,
606 0, NULL, NULL);
607 for (unsigned int script_index = 0; script_index < count; script_index++)
608 _hb_ot_layout_collect_lookups_languages (face,
609 table_tag,
610 script_index,
611 languages,
612 features,
613 lookup_indexes);
614 }
615 else
616 {
617 for (; *scripts; scripts++)
618 {
619 unsigned int script_index;
620 if (hb_ot_layout_table_find_script (face,
621 table_tag,
622 *scripts,
623 &script_index))
624 _hb_ot_layout_collect_lookups_languages (face,
625 table_tag,
626 script_index,
627 languages,
628 features,
629 lookup_indexes);
630 }
631 }
632 }
633
634 void
hb_ot_layout_lookup_collect_glyphs(hb_face_t * face,hb_tag_t table_tag,unsigned int lookup_index,hb_set_t * glyphs_before,hb_set_t * glyphs_input,hb_set_t * glyphs_after,hb_set_t * glyphs_output)635 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face,
636 hb_tag_t table_tag,
637 unsigned int lookup_index,
638 hb_set_t *glyphs_before, /* OUT. May be NULL */
639 hb_set_t *glyphs_input, /* OUT. May be NULL */
640 hb_set_t *glyphs_after, /* OUT. May be NULL */
641 hb_set_t *glyphs_output /* OUT. May be NULL */)
642 {
643 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
644
645 OT::hb_collect_glyphs_context_t c (face,
646 glyphs_before,
647 glyphs_input,
648 glyphs_after,
649 glyphs_output);
650
651 switch (table_tag)
652 {
653 case HB_OT_TAG_GSUB:
654 {
655 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
656 l.collect_glyphs (&c);
657 return;
658 }
659 case HB_OT_TAG_GPOS:
660 {
661 const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
662 l.collect_glyphs (&c);
663 return;
664 }
665 }
666 }
667
668
669 /*
670 * OT::GSUB
671 */
672
673 hb_bool_t
hb_ot_layout_has_substitution(hb_face_t * face)674 hb_ot_layout_has_substitution (hb_face_t *face)
675 {
676 return &_get_gsub (face) != &OT::Null(OT::GSUB);
677 }
678
679 hb_bool_t
hb_ot_layout_lookup_would_substitute(hb_face_t * face,unsigned int lookup_index,const hb_codepoint_t * glyphs,unsigned int glyphs_length,hb_bool_t zero_context)680 hb_ot_layout_lookup_would_substitute (hb_face_t *face,
681 unsigned int lookup_index,
682 const hb_codepoint_t *glyphs,
683 unsigned int glyphs_length,
684 hb_bool_t zero_context)
685 {
686 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
687 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
688 }
689
690 hb_bool_t
hb_ot_layout_lookup_would_substitute_fast(hb_face_t * face,unsigned int lookup_index,const hb_codepoint_t * glyphs,unsigned int glyphs_length,hb_bool_t zero_context)691 hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face,
692 unsigned int lookup_index,
693 const hb_codepoint_t *glyphs,
694 unsigned int glyphs_length,
695 hb_bool_t zero_context)
696 {
697 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
698 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
699
700 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
701
702 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest);
703 }
704
705 void
hb_ot_layout_substitute_start(hb_font_t * font,hb_buffer_t * buffer)706 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
707 {
708 OT::GSUB::substitute_start (font, buffer);
709 }
710
711 void
hb_ot_layout_substitute_finish(hb_font_t * font,hb_buffer_t * buffer)712 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
713 {
714 OT::GSUB::substitute_finish (font, buffer);
715 }
716
717 void
hb_ot_layout_lookup_substitute_closure(hb_face_t * face,unsigned int lookup_index,hb_set_t * glyphs)718 hb_ot_layout_lookup_substitute_closure (hb_face_t *face,
719 unsigned int lookup_index,
720 hb_set_t *glyphs)
721 {
722 OT::hb_closure_context_t c (face, glyphs);
723
724 const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
725
726 l.closure (&c);
727 }
728
729 /*
730 * OT::GPOS
731 */
732
733 hb_bool_t
hb_ot_layout_has_positioning(hb_face_t * face)734 hb_ot_layout_has_positioning (hb_face_t *face)
735 {
736 return &_get_gpos (face) != &OT::Null(OT::GPOS);
737 }
738
739 void
hb_ot_layout_position_start(hb_font_t * font,hb_buffer_t * buffer)740 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
741 {
742 OT::GPOS::position_start (font, buffer);
743 }
744
745 void
hb_ot_layout_position_finish(hb_font_t * font,hb_buffer_t * buffer)746 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
747 {
748 OT::GPOS::position_finish (font, buffer);
749 }
750
751 hb_bool_t
hb_ot_layout_get_size_params(hb_face_t * face,unsigned int * design_size,unsigned int * subfamily_id,unsigned int * subfamily_name_id,unsigned int * range_start,unsigned int * range_end)752 hb_ot_layout_get_size_params (hb_face_t *face,
753 unsigned int *design_size, /* OUT. May be NULL */
754 unsigned int *subfamily_id, /* OUT. May be NULL */
755 unsigned int *subfamily_name_id, /* OUT. May be NULL */
756 unsigned int *range_start, /* OUT. May be NULL */
757 unsigned int *range_end /* OUT. May be NULL */)
758 {
759 const OT::GPOS &gpos = _get_gpos (face);
760 const hb_tag_t tag = HB_TAG ('s','i','z','e');
761
762 unsigned int num_features = gpos.get_feature_count ();
763 for (unsigned int i = 0; i < num_features; i++)
764 {
765 if (tag == gpos.get_feature_tag (i))
766 {
767 const OT::Feature &f = gpos.get_feature (i);
768 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag);
769
770 if (params.designSize)
771 {
772 #define PARAM(a, A) if (a) *a = params.A
773 PARAM (design_size, designSize);
774 PARAM (subfamily_id, subfamilyID);
775 PARAM (subfamily_name_id, subfamilyNameID);
776 PARAM (range_start, rangeStart);
777 PARAM (range_end, rangeEnd);
778 #undef PARAM
779
780 return true;
781 }
782 }
783 }
784
785 #define PARAM(a, A) if (a) *a = 0
786 PARAM (design_size, designSize);
787 PARAM (subfamily_id, subfamilyID);
788 PARAM (subfamily_name_id, subfamilyNameID);
789 PARAM (range_start, rangeStart);
790 PARAM (range_end, rangeEnd);
791 #undef PARAM
792
793 return false;
794 }
795
796
797 /*
798 * Parts of different types are implemented here such that they have direct
799 * access to GSUB/GPOS lookups.
800 */
801
802
803 struct GSUBProxy
804 {
805 static const unsigned int table_index = 0;
806 static const bool inplace = false;
807 typedef OT::SubstLookup Lookup;
808
GSUBProxyGSUBProxy809 GSUBProxy (hb_face_t *face) :
810 table (*hb_ot_layout_from_face (face)->gsub),
811 accels (hb_ot_layout_from_face (face)->gsub_accels) {}
812
813 const OT::GSUB &table;
814 const hb_ot_layout_lookup_accelerator_t *accels;
815 };
816
817 struct GPOSProxy
818 {
819 static const unsigned int table_index = 1;
820 static const bool inplace = true;
821 typedef OT::PosLookup Lookup;
822
GPOSProxyGPOSProxy823 GPOSProxy (hb_face_t *face) :
824 table (*hb_ot_layout_from_face (face)->gpos),
825 accels (hb_ot_layout_from_face (face)->gpos_accels) {}
826
827 const OT::GPOS &table;
828 const hb_ot_layout_lookup_accelerator_t *accels;
829 };
830
831
832 template <typename Lookup>
apply_once(OT::hb_apply_context_t * c,const Lookup & lookup)833 static inline bool apply_once (OT::hb_apply_context_t *c,
834 const Lookup &lookup)
835 {
836 if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
837 return false;
838 return lookup.dispatch (c);
839 }
840
841 template <typename Proxy>
842 static inline bool
apply_string(OT::hb_apply_context_t * c,const typename Proxy::Lookup & lookup,const hb_ot_layout_lookup_accelerator_t & accel)843 apply_string (OT::hb_apply_context_t *c,
844 const typename Proxy::Lookup &lookup,
845 const hb_ot_layout_lookup_accelerator_t &accel)
846 {
847 bool ret = false;
848 hb_buffer_t *buffer = c->buffer;
849
850 if (unlikely (!buffer->len || !c->lookup_mask))
851 return false;
852
853 c->set_lookup (lookup);
854
855 if (likely (!lookup.is_reverse ()))
856 {
857 /* in/out forward substitution/positioning */
858 if (Proxy::table_index == 0)
859 buffer->clear_output ();
860 buffer->idx = 0;
861
862 while (buffer->idx < buffer->len)
863 {
864 if (accel.digest.may_have (buffer->cur().codepoint) &&
865 (buffer->cur().mask & c->lookup_mask) &&
866 apply_once (c, lookup))
867 ret = true;
868 else
869 buffer->next_glyph ();
870 }
871 if (ret)
872 {
873 if (!Proxy::inplace)
874 buffer->swap_buffers ();
875 else
876 assert (!buffer->has_separate_output ());
877 }
878 }
879 else
880 {
881 /* in-place backward substitution/positioning */
882 if (Proxy::table_index == 0)
883 buffer->remove_output ();
884 buffer->idx = buffer->len - 1;
885 do
886 {
887 if (accel.digest.may_have (buffer->cur().codepoint) &&
888 (buffer->cur().mask & c->lookup_mask) &&
889 apply_once (c, lookup))
890 ret = true;
891 /* The reverse lookup doesn't "advance" cursor (for good reason). */
892 buffer->idx--;
893
894 }
895 while ((int) buffer->idx >= 0);
896 }
897
898 return ret;
899 }
900
901 template <typename Proxy>
apply(const Proxy & proxy,const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const902 inline void hb_ot_map_t::apply (const Proxy &proxy,
903 const hb_ot_shape_plan_t *plan,
904 hb_font_t *font,
905 hb_buffer_t *buffer) const
906 {
907 const unsigned int table_index = proxy.table_index;
908 unsigned int i = 0;
909 OT::hb_apply_context_t c (table_index, font, buffer);
910 c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
911
912 for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
913 const stage_map_t *stage = &stages[table_index][stage_index];
914 for (; i < stage->last_lookup; i++)
915 {
916 unsigned int lookup_index = lookups[table_index][i].index;
917 c.set_lookup_mask (lookups[table_index][i].mask);
918 c.set_auto_zwj (lookups[table_index][i].auto_zwj);
919 apply_string<Proxy> (&c,
920 proxy.table.get_lookup (lookup_index),
921 proxy.accels[lookup_index]);
922 }
923
924 if (stage->pause_func)
925 {
926 buffer->clear_output ();
927 stage->pause_func (plan, font, buffer);
928 }
929 }
930 }
931
substitute(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const932 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
933 {
934 GSUBProxy proxy (font->face);
935 apply (proxy, plan, font, buffer);
936 }
937
position(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const938 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
939 {
940 GPOSProxy proxy (font->face);
941 apply (proxy, plan, font, buffer);
942 }
943
944 HB_INTERNAL void
hb_ot_layout_substitute_lookup(OT::hb_apply_context_t * c,const OT::SubstLookup & lookup,const hb_ot_layout_lookup_accelerator_t & accel)945 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
946 const OT::SubstLookup &lookup,
947 const hb_ot_layout_lookup_accelerator_t &accel)
948 {
949 apply_string<GSUBProxy> (c, lookup, accel);
950 }
951