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 &params = 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