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-open-type-private.hh"
32 #include "hb-ot-layout-private.hh"
33
34 #include "hb-ot-layout-gdef-table.hh"
35 #include "hb-ot-layout-gsub-table.hh"
36 #include "hb-ot-layout-gpos-table.hh"
37 #include "hb-ot-layout-jstf-table.hh"
38
39 #include "hb-ot-map-private.hh"
40
41 #include <stdlib.h>
42 #include <string.h>
43
44
HB_SHAPER_DATA_ENSURE_DECLARE(ot,face)45 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
46
47 hb_ot_layout_t *
48 _hb_ot_layout_create (hb_face_t *face)
49 {
50 hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
51 if (unlikely (!layout))
52 return NULL;
53
54 layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
55 layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
56
57 layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
58 layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
59
60 layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
61 layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
62
63 {
64 /*
65 * The ugly business of blacklisting individual fonts' tables happen here!
66 * See this thread for why we finally had to bend in and do this:
67 * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
68 */
69 unsigned int gdef_len = hb_blob_get_length (layout->gdef_blob);
70 unsigned int gsub_len = hb_blob_get_length (layout->gsub_blob);
71 unsigned int gpos_len = hb_blob_get_length (layout->gpos_blob);
72 if (0
73 || (442 == gdef_len && 42038 == gpos_len && 2874 == gsub_len) /* Windows 7 timesi.ttf */
74 || (430 == gdef_len && 40662 == gpos_len && 2874 == gsub_len) /* Windows 7 timesbi.ttf */
75 || (442 == gdef_len && 39116 == gpos_len && 2874 == gsub_len) /* Windows ??? timesi.ttf */
76 || (430 == gdef_len && 39374 == gpos_len && 2874 == gsub_len) /* Windows ??? timesbi.ttf */
77 || (490 == gdef_len && 41638 == gpos_len && 3046 == gsub_len) /* OS X 10.11.3 Times New Roman Italic.ttf */
78 || (478 == gdef_len && 41902 == gpos_len && 3046 == gsub_len) /* OS X 10.11.3 Times New Roman Bold Italic.ttf */
79 )
80 {
81 /* In certain versions of Times New Roman Italic and Bold Italic,
82 * ASCII double quotation mark U+0022, mapped to glyph 5, has wrong
83 * glyph class 3 (mark) in GDEF. Nuke the GDEF to avoid zero-width
84 * double-quote. See:
85 * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
86 */
87 if (3 == layout->gdef->get_glyph_class (5))
88 layout->gdef = &OT::Null(OT::GDEF);
89 }
90 }
91
92 layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
93 layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
94
95 layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
96 layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
97
98 if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) ||
99 (layout->gpos_lookup_count && !layout->gpos_accels)))
100 {
101 _hb_ot_layout_destroy (layout);
102 return NULL;
103 }
104
105 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
106 layout->gsub_accels[i].init (layout->gsub->get_lookup (i));
107 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
108 layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
109
110 return layout;
111 }
112
113 void
_hb_ot_layout_destroy(hb_ot_layout_t * layout)114 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
115 {
116 for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
117 layout->gsub_accels[i].fini ();
118 for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
119 layout->gpos_accels[i].fini ();
120
121 free (layout->gsub_accels);
122 free (layout->gpos_accels);
123
124 hb_blob_destroy (layout->gdef_blob);
125 hb_blob_destroy (layout->gsub_blob);
126 hb_blob_destroy (layout->gpos_blob);
127
128 free (layout);
129 }
130
131 static inline const OT::GDEF&
_get_gdef(hb_face_t * face)132 _get_gdef (hb_face_t *face)
133 {
134 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
135 return *hb_ot_layout_from_face (face)->gdef;
136 }
137 static inline const OT::GSUB&
_get_gsub(hb_face_t * face)138 _get_gsub (hb_face_t *face)
139 {
140 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
141 return *hb_ot_layout_from_face (face)->gsub;
142 }
143 static inline const OT::GPOS&
_get_gpos(hb_face_t * face)144 _get_gpos (hb_face_t *face)
145 {
146 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
147 return *hb_ot_layout_from_face (face)->gpos;
148 }
149
150
151 /*
152 * GDEF
153 */
154
155 hb_bool_t
hb_ot_layout_has_glyph_classes(hb_face_t * face)156 hb_ot_layout_has_glyph_classes (hb_face_t *face)
157 {
158 return _get_gdef (face).has_glyph_classes ();
159 }
160
161 /**
162 * hb_ot_layout_get_glyph_class:
163 *
164 * Since: 0.9.7
165 **/
166 hb_ot_layout_glyph_class_t
hb_ot_layout_get_glyph_class(hb_face_t * face,hb_codepoint_t glyph)167 hb_ot_layout_get_glyph_class (hb_face_t *face,
168 hb_codepoint_t glyph)
169 {
170 return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
171 }
172
173 /**
174 * hb_ot_layout_get_glyphs_in_class:
175 *
176 * Since: 0.9.7
177 **/
178 void
hb_ot_layout_get_glyphs_in_class(hb_face_t * face,hb_ot_layout_glyph_class_t klass,hb_set_t * glyphs)179 hb_ot_layout_get_glyphs_in_class (hb_face_t *face,
180 hb_ot_layout_glyph_class_t klass,
181 hb_set_t *glyphs /* OUT */)
182 {
183 return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
184 }
185
186 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)187 hb_ot_layout_get_attach_points (hb_face_t *face,
188 hb_codepoint_t glyph,
189 unsigned int start_offset,
190 unsigned int *point_count /* IN/OUT */,
191 unsigned int *point_array /* OUT */)
192 {
193 return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
194 }
195
196 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)197 hb_ot_layout_get_ligature_carets (hb_font_t *font,
198 hb_direction_t direction,
199 hb_codepoint_t glyph,
200 unsigned int start_offset,
201 unsigned int *caret_count /* IN/OUT */,
202 int *caret_array /* OUT */)
203 {
204 return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
205 }
206
207
208 /*
209 * GSUB/GPOS
210 */
211
212 static const OT::GSUBGPOS&
get_gsubgpos_table(hb_face_t * face,hb_tag_t table_tag)213 get_gsubgpos_table (hb_face_t *face,
214 hb_tag_t table_tag)
215 {
216 switch (table_tag) {
217 case HB_OT_TAG_GSUB: return _get_gsub (face);
218 case HB_OT_TAG_GPOS: return _get_gpos (face);
219 default: return OT::Null(OT::GSUBGPOS);
220 }
221 }
222
223
224 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)225 hb_ot_layout_table_get_script_tags (hb_face_t *face,
226 hb_tag_t table_tag,
227 unsigned int start_offset,
228 unsigned int *script_count /* IN/OUT */,
229 hb_tag_t *script_tags /* OUT */)
230 {
231 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
232
233 return g.get_script_tags (start_offset, script_count, script_tags);
234 }
235
236 #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n')
237
238 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)239 hb_ot_layout_table_find_script (hb_face_t *face,
240 hb_tag_t table_tag,
241 hb_tag_t script_tag,
242 unsigned int *script_index)
243 {
244 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
245 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
246
247 if (g.find_script_index (script_tag, script_index))
248 return true;
249
250 /* try finding 'DFLT' */
251 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
252 return false;
253
254 /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
255 * including many versions of DejaVu Sans Mono! */
256 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
257 return false;
258
259 /* try with 'latn'; some old fonts put their features there even though
260 they're really trying to support Thai, for example :( */
261 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
262 return false;
263
264 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
265 return false;
266 }
267
268 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)269 hb_ot_layout_table_choose_script (hb_face_t *face,
270 hb_tag_t table_tag,
271 const hb_tag_t *script_tags,
272 unsigned int *script_index,
273 hb_tag_t *chosen_script)
274 {
275 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
276 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
277
278 while (*script_tags)
279 {
280 if (g.find_script_index (*script_tags, script_index)) {
281 if (chosen_script)
282 *chosen_script = *script_tags;
283 return true;
284 }
285 script_tags++;
286 }
287
288 /* try finding 'DFLT' */
289 if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
290 if (chosen_script)
291 *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
292 return false;
293 }
294
295 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
296 if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
297 if (chosen_script)
298 *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
299 return false;
300 }
301
302 /* try with 'latn'; some old fonts put their features there even though
303 they're really trying to support Thai, for example :( */
304 if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
305 if (chosen_script)
306 *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
307 return false;
308 }
309
310 if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
311 if (chosen_script)
312 *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
313 return false;
314 }
315
316 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)317 hb_ot_layout_table_get_feature_tags (hb_face_t *face,
318 hb_tag_t table_tag,
319 unsigned int start_offset,
320 unsigned int *feature_count /* IN/OUT */,
321 hb_tag_t *feature_tags /* OUT */)
322 {
323 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
324
325 return g.get_feature_tags (start_offset, feature_count, feature_tags);
326 }
327
328 hb_bool_t
hb_ot_layout_table_find_feature(hb_face_t * face,hb_tag_t table_tag,hb_tag_t feature_tag,unsigned int * feature_index)329 hb_ot_layout_table_find_feature (hb_face_t *face,
330 hb_tag_t table_tag,
331 hb_tag_t feature_tag,
332 unsigned int *feature_index)
333 {
334 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
335 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
336
337 unsigned int num_features = g.get_feature_count ();
338 for (unsigned int i = 0; i < num_features; i++)
339 {
340 if (feature_tag == g.get_feature_tag (i)) {
341 if (feature_index) *feature_index = i;
342 return true;
343 }
344 }
345
346 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
347 return false;
348 }
349
350
351 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)352 hb_ot_layout_script_get_language_tags (hb_face_t *face,
353 hb_tag_t table_tag,
354 unsigned int script_index,
355 unsigned int start_offset,
356 unsigned int *language_count /* IN/OUT */,
357 hb_tag_t *language_tags /* OUT */)
358 {
359 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
360
361 return s.get_lang_sys_tags (start_offset, language_count, language_tags);
362 }
363
364 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)365 hb_ot_layout_script_find_language (hb_face_t *face,
366 hb_tag_t table_tag,
367 unsigned int script_index,
368 hb_tag_t language_tag,
369 unsigned int *language_index)
370 {
371 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
372 const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
373
374 if (s.find_lang_sys_index (language_tag, language_index))
375 return true;
376
377 /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
378 if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
379 return false;
380
381 if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
382 return false;
383 }
384
385 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)386 hb_ot_layout_language_get_required_feature_index (hb_face_t *face,
387 hb_tag_t table_tag,
388 unsigned int script_index,
389 unsigned int language_index,
390 unsigned int *feature_index)
391 {
392 return hb_ot_layout_language_get_required_feature (face,
393 table_tag,
394 script_index,
395 language_index,
396 feature_index,
397 NULL);
398 }
399
400 /**
401 * hb_ot_layout_language_get_required_feature:
402 *
403 * Since: 0.9.30
404 **/
405 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)406 hb_ot_layout_language_get_required_feature (hb_face_t *face,
407 hb_tag_t table_tag,
408 unsigned int script_index,
409 unsigned int language_index,
410 unsigned int *feature_index,
411 hb_tag_t *feature_tag)
412 {
413 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
414 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
415
416 unsigned int index = l.get_required_feature_index ();
417 if (feature_index) *feature_index = index;
418 if (feature_tag) *feature_tag = g.get_feature_tag (index);
419
420 return l.has_required_feature ();
421 }
422
423 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)424 hb_ot_layout_language_get_feature_indexes (hb_face_t *face,
425 hb_tag_t table_tag,
426 unsigned int script_index,
427 unsigned int language_index,
428 unsigned int start_offset,
429 unsigned int *feature_count /* IN/OUT */,
430 unsigned int *feature_indexes /* OUT */)
431 {
432 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
433 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
434
435 return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
436 }
437
438 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)439 hb_ot_layout_language_get_feature_tags (hb_face_t *face,
440 hb_tag_t table_tag,
441 unsigned int script_index,
442 unsigned int language_index,
443 unsigned int start_offset,
444 unsigned int *feature_count /* IN/OUT */,
445 hb_tag_t *feature_tags /* OUT */)
446 {
447 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
448 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
449
450 ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
451 unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
452
453 if (feature_tags) {
454 unsigned int count = *feature_count;
455 for (unsigned int i = 0; i < count; i++)
456 feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
457 }
458
459 return ret;
460 }
461
462
463 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)464 hb_ot_layout_language_find_feature (hb_face_t *face,
465 hb_tag_t table_tag,
466 unsigned int script_index,
467 unsigned int language_index,
468 hb_tag_t feature_tag,
469 unsigned int *feature_index)
470 {
471 ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
472 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
473 const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
474
475 unsigned int num_features = l.get_feature_count ();
476 for (unsigned int i = 0; i < num_features; i++) {
477 unsigned int f_index = l.get_feature_index (i);
478
479 if (feature_tag == g.get_feature_tag (f_index)) {
480 if (feature_index) *feature_index = f_index;
481 return true;
482 }
483 }
484
485 if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
486 return false;
487 }
488
489 /**
490 * hb_ot_layout_feature_get_lookups:
491 *
492 * Since: 0.9.7
493 **/
494 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)495 hb_ot_layout_feature_get_lookups (hb_face_t *face,
496 hb_tag_t table_tag,
497 unsigned int feature_index,
498 unsigned int start_offset,
499 unsigned int *lookup_count /* IN/OUT */,
500 unsigned int *lookup_indexes /* OUT */)
501 {
502 const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
503 const OT::Feature &f = g.get_feature (feature_index);
504
505 return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
506 }
507
508 /**
509 * hb_ot_layout_table_get_lookup_count:
510 *
511 * Since: 0.9.22
512 **/
513 unsigned int
hb_ot_layout_table_get_lookup_count(hb_face_t * face,hb_tag_t table_tag)514 hb_ot_layout_table_get_lookup_count (hb_face_t *face,
515 hb_tag_t table_tag)
516 {
517 switch (table_tag)
518 {
519 case HB_OT_TAG_GSUB:
520 {
521 return hb_ot_layout_from_face (face)->gsub_lookup_count;
522 }
523 case HB_OT_TAG_GPOS:
524 {
525 return hb_ot_layout_from_face (face)->gpos_lookup_count;
526 }
527 }
528 return 0;
529 }
530
531 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)532 _hb_ot_layout_collect_lookups_lookups (hb_face_t *face,
533 hb_tag_t table_tag,
534 unsigned int feature_index,
535 hb_set_t *lookup_indexes /* OUT */)
536 {
537 unsigned int lookup_indices[32];
538 unsigned int offset, len;
539
540 offset = 0;
541 do {
542 len = ARRAY_LENGTH (lookup_indices);
543 hb_ot_layout_feature_get_lookups (face,
544 table_tag,
545 feature_index,
546 offset, &len,
547 lookup_indices);
548
549 for (unsigned int i = 0; i < len; i++)
550 lookup_indexes->add (lookup_indices[i]);
551
552 offset += len;
553 } while (len == ARRAY_LENGTH (lookup_indices));
554 }
555
556 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)557 _hb_ot_layout_collect_lookups_features (hb_face_t *face,
558 hb_tag_t table_tag,
559 unsigned int script_index,
560 unsigned int language_index,
561 const hb_tag_t *features,
562 hb_set_t *lookup_indexes /* OUT */)
563 {
564 if (!features)
565 {
566 unsigned int required_feature_index;
567 if (hb_ot_layout_language_get_required_feature (face,
568 table_tag,
569 script_index,
570 language_index,
571 &required_feature_index,
572 NULL))
573 _hb_ot_layout_collect_lookups_lookups (face,
574 table_tag,
575 required_feature_index,
576 lookup_indexes);
577
578 /* All features */
579 unsigned int feature_indices[32];
580 unsigned int offset, len;
581
582 offset = 0;
583 do {
584 len = ARRAY_LENGTH (feature_indices);
585 hb_ot_layout_language_get_feature_indexes (face,
586 table_tag,
587 script_index,
588 language_index,
589 offset, &len,
590 feature_indices);
591
592 for (unsigned int i = 0; i < len; i++)
593 _hb_ot_layout_collect_lookups_lookups (face,
594 table_tag,
595 feature_indices[i],
596 lookup_indexes);
597
598 offset += len;
599 } while (len == ARRAY_LENGTH (feature_indices));
600 }
601 else
602 {
603 for (; *features; features++)
604 {
605 unsigned int feature_index;
606 if (hb_ot_layout_language_find_feature (face,
607 table_tag,
608 script_index,
609 language_index,
610 *features,
611 &feature_index))
612 _hb_ot_layout_collect_lookups_lookups (face,
613 table_tag,
614 feature_index,
615 lookup_indexes);
616 }
617 }
618 }
619
620 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)621 _hb_ot_layout_collect_lookups_languages (hb_face_t *face,
622 hb_tag_t table_tag,
623 unsigned int script_index,
624 const hb_tag_t *languages,
625 const hb_tag_t *features,
626 hb_set_t *lookup_indexes /* OUT */)
627 {
628 _hb_ot_layout_collect_lookups_features (face,
629 table_tag,
630 script_index,
631 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
632 features,
633 lookup_indexes);
634
635 if (!languages)
636 {
637 /* All languages */
638 unsigned int count = hb_ot_layout_script_get_language_tags (face,
639 table_tag,
640 script_index,
641 0, NULL, NULL);
642 for (unsigned int language_index = 0; language_index < count; language_index++)
643 _hb_ot_layout_collect_lookups_features (face,
644 table_tag,
645 script_index,
646 language_index,
647 features,
648 lookup_indexes);
649 }
650 else
651 {
652 for (; *languages; languages++)
653 {
654 unsigned int language_index;
655 if (hb_ot_layout_script_find_language (face,
656 table_tag,
657 script_index,
658 *languages,
659 &language_index))
660 _hb_ot_layout_collect_lookups_features (face,
661 table_tag,
662 script_index,
663 language_index,
664 features,
665 lookup_indexes);
666 }
667 }
668 }
669
670 /**
671 * hb_ot_layout_collect_lookups:
672 *
673 * Since: 0.9.8
674 **/
675 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)676 hb_ot_layout_collect_lookups (hb_face_t *face,
677 hb_tag_t table_tag,
678 const hb_tag_t *scripts,
679 const hb_tag_t *languages,
680 const hb_tag_t *features,
681 hb_set_t *lookup_indexes /* OUT */)
682 {
683 if (!scripts)
684 {
685 /* All scripts */
686 unsigned int count = hb_ot_layout_table_get_script_tags (face,
687 table_tag,
688 0, NULL, NULL);
689 for (unsigned int script_index = 0; script_index < count; script_index++)
690 _hb_ot_layout_collect_lookups_languages (face,
691 table_tag,
692 script_index,
693 languages,
694 features,
695 lookup_indexes);
696 }
697 else
698 {
699 for (; *scripts; scripts++)
700 {
701 unsigned int script_index;
702 if (hb_ot_layout_table_find_script (face,
703 table_tag,
704 *scripts,
705 &script_index))
706 _hb_ot_layout_collect_lookups_languages (face,
707 table_tag,
708 script_index,
709 languages,
710 features,
711 lookup_indexes);
712 }
713 }
714 }
715
716 /**
717 * hb_ot_layout_lookup_collect_glyphs:
718 *
719 * Since: 0.9.7
720 **/
721 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)722 hb_ot_layout_lookup_collect_glyphs (hb_face_t *face,
723 hb_tag_t table_tag,
724 unsigned int lookup_index,
725 hb_set_t *glyphs_before, /* OUT. May be NULL */
726 hb_set_t *glyphs_input, /* OUT. May be NULL */
727 hb_set_t *glyphs_after, /* OUT. May be NULL */
728 hb_set_t *glyphs_output /* OUT. May be NULL */)
729 {
730 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
731
732 OT::hb_collect_glyphs_context_t c (face,
733 glyphs_before,
734 glyphs_input,
735 glyphs_after,
736 glyphs_output);
737
738 switch (table_tag)
739 {
740 case HB_OT_TAG_GSUB:
741 {
742 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
743 l.collect_glyphs (&c);
744 return;
745 }
746 case HB_OT_TAG_GPOS:
747 {
748 const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
749 l.collect_glyphs (&c);
750 return;
751 }
752 }
753 }
754
755
756 /*
757 * OT::GSUB
758 */
759
760 hb_bool_t
hb_ot_layout_has_substitution(hb_face_t * face)761 hb_ot_layout_has_substitution (hb_face_t *face)
762 {
763 return &_get_gsub (face) != &OT::Null(OT::GSUB);
764 }
765
766 /**
767 * hb_ot_layout_lookup_would_substitute:
768 *
769 * Since: 0.9.7
770 **/
771 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)772 hb_ot_layout_lookup_would_substitute (hb_face_t *face,
773 unsigned int lookup_index,
774 const hb_codepoint_t *glyphs,
775 unsigned int glyphs_length,
776 hb_bool_t zero_context)
777 {
778 if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
779 return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
780 }
781
782 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)783 hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face,
784 unsigned int lookup_index,
785 const hb_codepoint_t *glyphs,
786 unsigned int glyphs_length,
787 hb_bool_t zero_context)
788 {
789 if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
790 OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
791
792 const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
793
794 return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
795 }
796
797 void
hb_ot_layout_substitute_start(hb_font_t * font,hb_buffer_t * buffer)798 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
799 {
800 OT::GSUB::substitute_start (font, buffer);
801 }
802
803 /**
804 * hb_ot_layout_lookup_substitute_closure:
805 *
806 * Since: 0.9.7
807 **/
808 void
hb_ot_layout_lookup_substitute_closure(hb_face_t * face,unsigned int lookup_index,hb_set_t * glyphs)809 hb_ot_layout_lookup_substitute_closure (hb_face_t *face,
810 unsigned int lookup_index,
811 hb_set_t *glyphs)
812 {
813 OT::hb_closure_context_t c (face, glyphs);
814
815 const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
816
817 l.closure (&c);
818 }
819
820 /*
821 * OT::GPOS
822 */
823
824 hb_bool_t
hb_ot_layout_has_positioning(hb_face_t * face)825 hb_ot_layout_has_positioning (hb_face_t *face)
826 {
827 return &_get_gpos (face) != &OT::Null(OT::GPOS);
828 }
829
830 void
hb_ot_layout_position_start(hb_font_t * font,hb_buffer_t * buffer)831 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
832 {
833 OT::GPOS::position_start (font, buffer);
834 }
835
836 void
hb_ot_layout_position_finish_advances(hb_font_t * font,hb_buffer_t * buffer)837 hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
838 {
839 OT::GPOS::position_finish_advances (font, buffer);
840 }
841
842 void
hb_ot_layout_position_finish_offsets(hb_font_t * font,hb_buffer_t * buffer)843 hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
844 {
845 OT::GPOS::position_finish_offsets (font, buffer);
846 }
847
848 /**
849 * hb_ot_layout_get_size_params:
850 *
851 * Since: 0.9.10
852 **/
853 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)854 hb_ot_layout_get_size_params (hb_face_t *face,
855 unsigned int *design_size, /* OUT. May be NULL */
856 unsigned int *subfamily_id, /* OUT. May be NULL */
857 unsigned int *subfamily_name_id, /* OUT. May be NULL */
858 unsigned int *range_start, /* OUT. May be NULL */
859 unsigned int *range_end /* OUT. May be NULL */)
860 {
861 const OT::GPOS &gpos = _get_gpos (face);
862 const hb_tag_t tag = HB_TAG ('s','i','z','e');
863
864 unsigned int num_features = gpos.get_feature_count ();
865 for (unsigned int i = 0; i < num_features; i++)
866 {
867 if (tag == gpos.get_feature_tag (i))
868 {
869 const OT::Feature &f = gpos.get_feature (i);
870 const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag);
871
872 if (params.designSize)
873 {
874 #define PARAM(a, A) if (a) *a = params.A
875 PARAM (design_size, designSize);
876 PARAM (subfamily_id, subfamilyID);
877 PARAM (subfamily_name_id, subfamilyNameID);
878 PARAM (range_start, rangeStart);
879 PARAM (range_end, rangeEnd);
880 #undef PARAM
881
882 return true;
883 }
884 }
885 }
886
887 #define PARAM(a, A) if (a) *a = 0
888 PARAM (design_size, designSize);
889 PARAM (subfamily_id, subfamilyID);
890 PARAM (subfamily_name_id, subfamilyNameID);
891 PARAM (range_start, rangeStart);
892 PARAM (range_end, rangeEnd);
893 #undef PARAM
894
895 return false;
896 }
897
898
899 /*
900 * Parts of different types are implemented here such that they have direct
901 * access to GSUB/GPOS lookups.
902 */
903
904
905 struct GSUBProxy
906 {
907 static const unsigned int table_index = 0;
908 static const bool inplace = false;
909 typedef OT::SubstLookup Lookup;
910
GSUBProxyGSUBProxy911 GSUBProxy (hb_face_t *face) :
912 table (*hb_ot_layout_from_face (face)->gsub),
913 accels (hb_ot_layout_from_face (face)->gsub_accels) {}
914
915 const OT::GSUB &table;
916 const hb_ot_layout_lookup_accelerator_t *accels;
917 };
918
919 struct GPOSProxy
920 {
921 static const unsigned int table_index = 1;
922 static const bool inplace = true;
923 typedef OT::PosLookup Lookup;
924
GPOSProxyGPOSProxy925 GPOSProxy (hb_face_t *face) :
926 table (*hb_ot_layout_from_face (face)->gpos),
927 accels (hb_ot_layout_from_face (face)->gpos_accels) {}
928
929 const OT::GPOS &table;
930 const hb_ot_layout_lookup_accelerator_t *accels;
931 };
932
933
934 struct hb_get_subtables_context_t :
935 OT::hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY>
936 {
937 template <typename Type>
apply_tohb_get_subtables_context_t938 static inline bool apply_to (const void *obj, OT::hb_apply_context_t *c)
939 {
940 const Type *typed_obj = (const Type *) obj;
941 return typed_obj->apply (c);
942 }
943
944 typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_apply_context_t *c);
945
946 struct hb_applicable_t
947 {
inithb_get_subtables_context_t::hb_applicable_t948 inline void init (const void *obj_, hb_apply_func_t apply_func_)
949 {
950 obj = obj_;
951 apply_func = apply_func_;
952 }
953
applyhb_get_subtables_context_t::hb_applicable_t954 inline bool apply (OT::hb_apply_context_t *c) const { return apply_func (obj, c); }
955
956 private:
957 const void *obj;
958 hb_apply_func_t apply_func;
959 };
960
961 typedef hb_auto_array_t<hb_applicable_t> array_t;
962
963 /* Dispatch interface. */
get_namehb_get_subtables_context_t964 inline const char *get_name (void) { return "GET_SUBTABLES"; }
965 template <typename T>
dispatchhb_get_subtables_context_t966 inline return_t dispatch (const T &obj)
967 {
968 hb_applicable_t *entry = array.push();
969 if (likely (entry))
970 entry->init (&obj, apply_to<T>);
971 return HB_VOID;
972 }
default_return_valuehb_get_subtables_context_t973 static return_t default_return_value (void) { return HB_VOID; }
stop_sublookup_iterationhb_get_subtables_context_t974 bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
975
hb_get_subtables_context_thb_get_subtables_context_t976 hb_get_subtables_context_t (array_t &array_) :
977 array (array_),
978 debug_depth (0) {}
979
980 array_t &array;
981 unsigned int debug_depth;
982 };
983
984 static inline bool
apply_forward(OT::hb_apply_context_t * c,const hb_ot_layout_lookup_accelerator_t & accel,const hb_get_subtables_context_t::array_t & subtables)985 apply_forward (OT::hb_apply_context_t *c,
986 const hb_ot_layout_lookup_accelerator_t &accel,
987 const hb_get_subtables_context_t::array_t &subtables)
988 {
989 bool ret = false;
990 hb_buffer_t *buffer = c->buffer;
991 while (buffer->idx < buffer->len && !buffer->in_error)
992 {
993 bool applied = false;
994 if (accel.may_have (buffer->cur().codepoint) &&
995 (buffer->cur().mask & c->lookup_mask) &&
996 c->check_glyph_property (&buffer->cur(), c->lookup_props))
997 {
998 for (unsigned int i = 0; i < subtables.len; i++)
999 if (subtables[i].apply (c))
1000 {
1001 applied = true;
1002 break;
1003 }
1004 }
1005
1006 if (applied)
1007 ret = true;
1008 else
1009 buffer->next_glyph ();
1010 }
1011 return ret;
1012 }
1013
1014 static inline bool
apply_backward(OT::hb_apply_context_t * c,const hb_ot_layout_lookup_accelerator_t & accel,const hb_get_subtables_context_t::array_t & subtables)1015 apply_backward (OT::hb_apply_context_t *c,
1016 const hb_ot_layout_lookup_accelerator_t &accel,
1017 const hb_get_subtables_context_t::array_t &subtables)
1018 {
1019 bool ret = false;
1020 hb_buffer_t *buffer = c->buffer;
1021 do
1022 {
1023 if (accel.may_have (buffer->cur().codepoint) &&
1024 (buffer->cur().mask & c->lookup_mask) &&
1025 c->check_glyph_property (&buffer->cur(), c->lookup_props))
1026 {
1027 for (unsigned int i = 0; i < subtables.len; i++)
1028 if (subtables[i].apply (c))
1029 {
1030 ret = true;
1031 break;
1032 }
1033 }
1034 /* The reverse lookup doesn't "advance" cursor (for good reason). */
1035 buffer->idx--;
1036
1037 }
1038 while ((int) buffer->idx >= 0);
1039 return ret;
1040 }
1041
1042 template <typename Proxy>
1043 static inline void
apply_string(OT::hb_apply_context_t * c,const typename Proxy::Lookup & lookup,const hb_ot_layout_lookup_accelerator_t & accel)1044 apply_string (OT::hb_apply_context_t *c,
1045 const typename Proxy::Lookup &lookup,
1046 const hb_ot_layout_lookup_accelerator_t &accel)
1047 {
1048 hb_buffer_t *buffer = c->buffer;
1049
1050 if (unlikely (!buffer->len || !c->lookup_mask))
1051 return;
1052
1053 c->set_lookup_props (lookup.get_props ());
1054
1055 hb_get_subtables_context_t::array_t subtables;
1056 hb_get_subtables_context_t c_get_subtables (subtables);
1057 lookup.dispatch (&c_get_subtables);
1058
1059 if (likely (!lookup.is_reverse ()))
1060 {
1061 /* in/out forward substitution/positioning */
1062 if (Proxy::table_index == 0)
1063 buffer->clear_output ();
1064 buffer->idx = 0;
1065
1066 bool ret;
1067 ret = apply_forward (c, accel, subtables);
1068 if (ret)
1069 {
1070 if (!Proxy::inplace)
1071 buffer->swap_buffers ();
1072 else
1073 assert (!buffer->has_separate_output ());
1074 }
1075 }
1076 else
1077 {
1078 /* in-place backward substitution/positioning */
1079 if (Proxy::table_index == 0)
1080 buffer->remove_output ();
1081 buffer->idx = buffer->len - 1;
1082
1083 apply_backward (c, accel, subtables);
1084 }
1085 }
1086
1087 template <typename Proxy>
apply(const Proxy & proxy,const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const1088 inline void hb_ot_map_t::apply (const Proxy &proxy,
1089 const hb_ot_shape_plan_t *plan,
1090 hb_font_t *font,
1091 hb_buffer_t *buffer) const
1092 {
1093 const unsigned int table_index = proxy.table_index;
1094 unsigned int i = 0;
1095 OT::hb_apply_context_t c (table_index, font, buffer);
1096 c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
1097
1098 for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
1099 const stage_map_t *stage = &stages[table_index][stage_index];
1100 for (; i < stage->last_lookup; i++)
1101 {
1102 unsigned int lookup_index = lookups[table_index][i].index;
1103 if (!buffer->message (font, "start lookup %d", lookup_index)) continue;
1104 c.set_lookup_index (lookup_index);
1105 c.set_lookup_mask (lookups[table_index][i].mask);
1106 c.set_auto_zwj (lookups[table_index][i].auto_zwj);
1107 apply_string<Proxy> (&c,
1108 proxy.table.get_lookup (lookup_index),
1109 proxy.accels[lookup_index]);
1110 (void) buffer->message (font, "end lookup %d", lookup_index);
1111 }
1112
1113 if (stage->pause_func)
1114 {
1115 buffer->clear_output ();
1116 stage->pause_func (plan, font, buffer);
1117 }
1118 }
1119 }
1120
substitute(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const1121 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1122 {
1123 GSUBProxy proxy (font->face);
1124 apply (proxy, plan, font, buffer);
1125 }
1126
position(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const1127 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1128 {
1129 GPOSProxy proxy (font->face);
1130 apply (proxy, plan, font, buffer);
1131 }
1132
1133 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)1134 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
1135 const OT::SubstLookup &lookup,
1136 const hb_ot_layout_lookup_accelerator_t &accel)
1137 {
1138 apply_string<GSUBProxy> (c, lookup, accel);
1139 }
1140