[HB] Add XXX marks
[framework/uifw/harfbuzz.git] / src / hb-ot-layout.cc
1 /*
2  * Copyright (C) 1998-2004  David Turner and Werner Lemberg
3  * Copyright (C) 2006  Behdad Esfahbod
4  * Copyright (C) 2007,2008,2009  Red Hat, Inc.
5  *
6  *  This is part of HarfBuzz, an OpenType Layout engine library.
7  *
8  * Permission is hereby granted, without written agreement and without
9  * license or royalty fees, to use, copy, modify, and distribute this
10  * software and its documentation for any purpose, provided that the
11  * above copyright notice and the following two paragraphs appear in
12  * all copies of this software.
13  *
14  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18  * DAMAGE.
19  *
20  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25  *
26  * Red Hat Author(s): Behdad Esfahbod
27  */
28
29 #define HB_OT_LAYOUT_CC
30
31 #include "hb-ot-layout-private.h"
32
33 #include "hb-ot-layout-open-private.h"
34 #include "hb-ot-layout-gdef-private.h"
35 #include "hb-ot-layout-gsub-private.h"
36 #include "hb-ot-layout-gpos-private.h"
37
38
39 #include <stdlib.h>
40 #include <string.h>
41
42
43 hb_ot_layout_t *
44 hb_ot_layout_create (void)
45 {
46   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
47
48   layout->gdef = &Null(GDEF);
49   layout->gsub = &Null(GSUB);
50   layout->gpos = &Null(GPOS);
51
52   return layout;
53 }
54
55 hb_bool_t
56 hb_ot_layout_has_substitution (hb_ot_layout_t *layout)
57 {
58   return layout->gsub != &Null(GSUB);
59 }
60
61 hb_bool_t
62 hb_ot_layout_has_positioning (hb_ot_layout_t *layout)
63 {
64   return layout->gpos != &Null(GPOS);
65 }
66
67 hb_ot_layout_t *
68 hb_ot_layout_create_for_data (const char *font_data,
69                               int         face_index)
70 {
71   hb_ot_layout_t *layout;
72
73   if (HB_UNLIKELY (font_data == NULL))
74     return hb_ot_layout_create ();
75
76   layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
77
78   const OpenTypeFontFile &font = OpenTypeFontFile::get_for_data (font_data);
79   const OpenTypeFontFace &face = font.get_face (face_index);
80
81   layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag)));
82   layout->gsub = &GSUB::get_for_data (font.get_table_data (face.get_table_by_tag (GSUB::Tag)));
83   layout->gpos = &GPOS::get_for_data (font.get_table_data (face.get_table_by_tag (GPOS::Tag)));
84
85   return layout;
86 }
87
88 hb_ot_layout_t *
89 hb_ot_layout_create_for_tables (const char *gdef_data,
90                                 const char *gsub_data,
91                                 const char *gpos_data)
92 {
93   hb_ot_layout_t *layout;
94
95   if (HB_UNLIKELY (gdef_data == NULL && gsub_data == NULL && gpos_data == NULL))
96     return hb_ot_layout_create ();
97
98   layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
99
100   layout->gdef = &GDEF::get_for_data (gdef_data);
101   layout->gsub = &GSUB::get_for_data (gsub_data);
102   layout->gpos = &GPOS::get_for_data (gpos_data);
103
104   return layout;
105 }
106
107 void
108 hb_ot_layout_destroy (hb_ot_layout_t *layout)
109 {
110   free (layout);
111 }
112
113 void
114 hb_ot_layout_set_scale (hb_ot_layout_t *layout,
115                         hb_16dot16_t x_scale, hb_16dot16_t y_scale)
116 {
117   layout->gpos_info.x_scale = x_scale;
118   layout->gpos_info.y_scale = y_scale;
119 }
120
121 void
122 hb_ot_layout_set_ppem (hb_ot_layout_t *layout,
123                        unsigned int x_ppem, unsigned int y_ppem)
124 {
125   layout->gpos_info.x_ppem = x_ppem;
126   layout->gpos_info.y_ppem = y_ppem;
127 }
128
129
130 /*
131  * GDEF
132  */
133
134 /* TODO the public class_t is a mess */
135
136 hb_bool_t
137 hb_ot_layout_has_font_glyph_classes (hb_ot_layout_t *layout)
138 {
139   return layout->gdef->has_glyph_classes ();
140 }
141
142 HB_INTERNAL hb_bool_t
143 _hb_ot_layout_has_new_glyph_classes (hb_ot_layout_t *layout)
144 {
145   return layout->new_gdef.len > 0;
146 }
147
148 HB_INTERNAL unsigned int
149 _hb_ot_layout_get_glyph_property (hb_ot_layout_t *layout,
150                                   hb_codepoint_t  glyph)
151 {
152   hb_ot_layout_class_t klass;
153
154   klass = layout->gdef->get_glyph_class (glyph);
155
156   if (!klass && glyph < layout->new_gdef.len)
157     klass = layout->new_gdef.klasses[glyph];
158
159   switch (klass) {
160   default:
161   case GDEF::UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
162   case GDEF::BaseGlyph:         return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
163   case GDEF::LigatureGlyph:     return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
164   case GDEF::ComponentGlyph:    return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
165   case GDEF::MarkGlyph:
166         /* TODO old harfbuzz doesn't always parse mark attachments as it says it was
167          * introduced without a version bump, so it may not be safe */
168         klass = layout->gdef->get_mark_attachment_type (glyph);
169         return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8);
170   }
171 }
172
173 HB_INTERNAL hb_bool_t
174 _hb_ot_layout_check_glyph_property (hb_ot_layout_t  *layout,
175                                     hb_internal_glyph_info_t *ginfo,
176                                     unsigned int     lookup_flags,
177                                     unsigned int    *property_out)
178 {
179   unsigned int property;
180
181   if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
182     ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
183   property = ginfo->gproperty;
184   if (property_out)
185     *property_out = property;
186
187   /* Not covered, if, for example, glyph class is ligature and
188    * lookup_flags includes LookupFlags::IgnoreLigatures
189    */
190   if (property & lookup_flags)
191     return false;
192
193   if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
194   {
195     /* If using mark filtering sets, the high short of
196      * lookup_flags has the set index.
197      */
198     if (lookup_flags & LookupFlag::UseMarkFilteringSet)
199       return layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
200
201     /* The second byte of lookup_flags has the meaning
202      * "ignore marks of attachment type different than
203      * the attachment type specified."
204      */
205     if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
206       return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType);
207   }
208
209   return true;
210 }
211
212 HB_INTERNAL hb_bool_t
213 _hb_ot_layout_skip_mark (hb_ot_layout_t  *layout,
214                          hb_internal_glyph_info_t *ginfo,
215                          unsigned int     lookup_flags,
216                          unsigned int    *property_out)
217 {
218   unsigned int property;
219
220   if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
221     ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
222   property = ginfo->gproperty;
223   if (property_out)
224     *property_out = property;
225
226   if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
227   {
228     /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */
229     if (lookup_flags & LookupFlag::IgnoreMarks)
230       return true;
231
232     /* If using mark filtering sets, the high short of lookup_flags has the set index. */
233     if (lookup_flags & LookupFlag::UseMarkFilteringSet)
234       return !layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
235
236     /* The second byte of lookup_flags has the meaning "ignore marks of attachment type
237      * different than the attachment type specified." */
238     if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
239       return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType);
240   }
241
242   return false;
243 }
244
245 HB_INTERNAL void
246 _hb_ot_layout_set_glyph_property (hb_ot_layout_t *layout,
247                                   hb_codepoint_t  glyph,
248                                   unsigned int    property)
249 { hb_ot_layout_set_glyph_class (layout, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); }
250
251
252 hb_ot_layout_glyph_class_t
253 hb_ot_layout_get_glyph_class (hb_ot_layout_t *layout,
254                               hb_codepoint_t  glyph)
255 { return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (layout, glyph) & 0xff); }
256
257 void
258 hb_ot_layout_set_glyph_class (hb_ot_layout_t             *layout,
259                               hb_codepoint_t              glyph,
260                               hb_ot_layout_glyph_class_t  klass)
261 {
262   /* TODO optimize this? similar to old harfbuzz code for example */
263
264   hb_ot_layout_class_t gdef_klass;
265   int len = layout->new_gdef.len;
266
267   if (HB_UNLIKELY (glyph > 65535))
268     return;
269
270   /* XXX this is not threadsafe */
271   if (glyph >= len) {
272     int new_len;
273     unsigned char *new_klasses;
274
275     new_len = len == 0 ? 120 : 2 * len;
276     if (new_len > 65535)
277       new_len = 65535;
278     new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
279
280     if (HB_UNLIKELY (!new_klasses))
281       return;
282
283     memset (new_klasses + len, 0, new_len - len);
284
285     layout->new_gdef.klasses = new_klasses;
286     layout->new_gdef.len = new_len;
287   }
288
289   switch (klass) {
290   default:
291   case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED:   gdef_klass = GDEF::UnclassifiedGlyph;   break;
292   case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH:     gdef_klass = GDEF::BaseGlyph;           break;
293   case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE:       gdef_klass = GDEF::LigatureGlyph;       break;
294   case HB_OT_LAYOUT_GLYPH_CLASS_MARK:           gdef_klass = GDEF::MarkGlyph;           break;
295   case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT:      gdef_klass = GDEF::ComponentGlyph;      break;
296   }
297
298   layout->new_gdef.klasses[glyph] = gdef_klass;
299   return;
300 }
301
302 void
303 hb_ot_layout_build_glyph_classes (hb_ot_layout_t *layout,
304                                   uint16_t        num_total_glyphs,
305                                   hb_codepoint_t *glyphs,
306                                   unsigned char  *klasses,
307                                   uint16_t        count)
308 {
309   if (HB_UNLIKELY (!count || !glyphs || !klasses))
310     return;
311
312   if (layout->new_gdef.len == 0) {
313     layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
314     layout->new_gdef.len = count;
315   }
316
317   for (unsigned int i = 0; i < count; i++)
318     hb_ot_layout_set_glyph_class (layout, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
319 }
320
321 hb_bool_t
322 hb_ot_layout_get_attach_points (hb_ot_layout_t *layout,
323                                 hb_codepoint_t  glyph,
324                                 unsigned int   *point_count /* IN/OUT */,
325                                 unsigned int   *point_array /* OUT */)
326 {
327   return layout->gdef->get_attach_points (glyph, point_count, point_array);
328 }
329
330 hb_bool_t
331 hb_ot_layout_get_lig_carets (hb_ot_layout_t *layout,
332                              hb_codepoint_t  glyph,
333                              unsigned int   *caret_count /* IN/OUT */,
334                              int            *caret_array /* OUT */)
335 {
336   return layout->gdef->get_lig_carets (layout, glyph, caret_count, caret_array);
337 }
338
339 /*
340  * GSUB/GPOS
341  */
342
343 static const GSUBGPOS&
344 get_gsubgpos_table (hb_ot_layout_t            *layout,
345                     hb_ot_layout_table_type_t  table_type)
346 {
347   switch (table_type) {
348     case HB_OT_LAYOUT_TABLE_TYPE_GSUB: return *(layout->gsub);
349     case HB_OT_LAYOUT_TABLE_TYPE_GPOS: return *(layout->gpos);
350     default:                           return Null(GSUBGPOS);
351   }
352 }
353
354
355 unsigned int
356 hb_ot_layout_table_get_script_count (hb_ot_layout_t            *layout,
357                                      hb_ot_layout_table_type_t  table_type)
358 {
359   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
360
361   return g.get_script_count ();
362 }
363
364 hb_tag_t
365 hb_ot_layout_table_get_script_tag (hb_ot_layout_t            *layout,
366                                    hb_ot_layout_table_type_t  table_type,
367                                    unsigned int               script_index)
368 {
369   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
370
371   return g.get_script_tag (script_index);
372 }
373
374 hb_bool_t
375 hb_ot_layout_table_find_script (hb_ot_layout_t            *layout,
376                                 hb_ot_layout_table_type_t  table_type,
377                                 hb_tag_t                   script_tag,
378                                 unsigned int              *script_index)
379 {
380   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
381   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
382
383   if (g.find_script_index (script_tag, script_index))
384     return TRUE;
385
386   /* try finding 'DFLT' */
387   if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
388     return FALSE;
389
390   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
391   if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
392     return FALSE;
393
394   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
395   return FALSE;
396 }
397
398 unsigned int
399 hb_ot_layout_table_get_feature_count (hb_ot_layout_t            *layout,
400                                       hb_ot_layout_table_type_t  table_type)
401 {
402   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
403
404   return g.get_feature_count ();
405 }
406
407 hb_tag_t
408 hb_ot_layout_table_get_feature_tag (hb_ot_layout_t            *layout,
409                                     hb_ot_layout_table_type_t  table_type,
410                                     unsigned int               feature_index)
411 {
412   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
413
414   return g.get_feature_tag (feature_index);
415 }
416
417 hb_bool_t
418 hb_ot_layout_table_find_feature (hb_ot_layout_t            *layout,
419                                  hb_ot_layout_table_type_t  table_type,
420                                  hb_tag_t                   feature_tag,
421                                  unsigned int              *feature_index)
422 {
423   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
424   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
425
426   if (g.find_feature_index (feature_tag, feature_index))
427     return TRUE;
428
429   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
430   return FALSE;
431 }
432
433 unsigned int
434 hb_ot_layout_table_get_lookup_count (hb_ot_layout_t            *layout,
435                                      hb_ot_layout_table_type_t  table_type)
436 {
437   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
438
439   return g.get_lookup_count ();
440 }
441
442
443 unsigned int
444 hb_ot_layout_script_get_language_count (hb_ot_layout_t            *layout,
445                                         hb_ot_layout_table_type_t  table_type,
446                                         unsigned int               script_index)
447 {
448   const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
449
450   return s.get_lang_sys_count ();
451 }
452
453 hb_tag_t
454 hb_ot_layout_script_get_language_tag (hb_ot_layout_t            *layout,
455                                       hb_ot_layout_table_type_t  table_type,
456                                       unsigned int               script_index,
457                                       unsigned int               language_index)
458 {
459   const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
460
461   return s.get_lang_sys_tag (language_index);
462 }
463
464 hb_bool_t
465 hb_ot_layout_script_find_language (hb_ot_layout_t            *layout,
466                                    hb_ot_layout_table_type_t  table_type,
467                                    unsigned int               script_index,
468                                    hb_tag_t                   language_tag,
469                                    unsigned int              *language_index)
470 {
471   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
472   const Script &s = get_gsubgpos_table (layout, table_type).get_script (script_index);
473
474   if (s.find_lang_sys_index (language_tag, language_index))
475     return TRUE;
476
477   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
478   if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
479     return FALSE;
480
481   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
482   return FALSE;
483 }
484
485 hb_bool_t
486 hb_ot_layout_language_get_required_feature_index (hb_ot_layout_t            *layout,
487                                                   hb_ot_layout_table_type_t  table_type,
488                                                   unsigned int               script_index,
489                                                   unsigned int               language_index,
490                                                   unsigned int              *feature_index)
491 {
492   const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
493
494   if (feature_index) *feature_index = l.get_required_feature_index ();
495
496   return l.has_required_feature ();
497 }
498
499 unsigned int
500 hb_ot_layout_language_get_feature_count (hb_ot_layout_t            *layout,
501                                          hb_ot_layout_table_type_t  table_type,
502                                          unsigned int               script_index,
503                                          unsigned int               language_index)
504 {
505   const LangSys &l = get_gsubgpos_table (layout, table_type).get_script (script_index).get_lang_sys (language_index);
506
507   return l.get_feature_count ();
508 }
509
510 unsigned int
511 hb_ot_layout_language_get_feature_index (hb_ot_layout_t            *layout,
512                                          hb_ot_layout_table_type_t  table_type,
513                                          unsigned int               script_index,
514                                          unsigned int               language_index,
515                                          unsigned int               num_feature)
516 {
517   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
518   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
519
520   return l.get_feature_index (num_feature);
521 }
522
523 hb_tag_t
524 hb_ot_layout_language_get_feature_tag (hb_ot_layout_t            *layout,
525                                        hb_ot_layout_table_type_t  table_type,
526                                        unsigned int               script_index,
527                                        unsigned int               language_index,
528                                        unsigned int               num_feature)
529 {
530   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
531   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
532   unsigned int feature_index = l.get_feature_index (num_feature);
533
534   return g.get_feature_tag (feature_index);
535 }
536
537
538 hb_bool_t
539 hb_ot_layout_language_find_feature (hb_ot_layout_t            *layout,
540                                     hb_ot_layout_table_type_t  table_type,
541                                     unsigned int               script_index,
542                                     unsigned int               language_index,
543                                     hb_tag_t                   feature_tag,
544                                     unsigned int              *feature_index)
545 {
546   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
547   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
548   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
549
550   unsigned int num_features = l.get_feature_count ();
551   for (unsigned int i = 0; i < num_features; i++) {
552     unsigned int f_index = l.get_feature_index (i);
553
554     if (feature_tag == g.get_feature_tag (f_index)) {
555       if (feature_index) *feature_index = f_index;
556       return TRUE;
557     }
558   }
559
560   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
561   return FALSE;
562 }
563
564 unsigned int
565 hb_ot_layout_feature_get_lookup_count (hb_ot_layout_t            *layout,
566                                        hb_ot_layout_table_type_t  table_type,
567                                        unsigned int               feature_index)
568 {
569   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
570   const Feature &f = g.get_feature (feature_index);
571
572   return f.get_lookup_count ();
573 }
574
575 unsigned int
576 hb_ot_layout_feature_get_lookup_index (hb_ot_layout_t            *layout,
577                                        hb_ot_layout_table_type_t  table_type,
578                                        unsigned int               feature_index,
579                                        unsigned int               num_lookup)
580 {
581   const GSUBGPOS &g = get_gsubgpos_table (layout, table_type);
582   const Feature &f = g.get_feature (feature_index);
583
584   return f.get_lookup_index (num_lookup);
585 }
586
587 /*
588  * GSUB
589  */
590
591 hb_bool_t
592 hb_ot_layout_substitute_lookup (hb_ot_layout_t              *layout,
593                                 hb_buffer_t                 *buffer,
594                                 unsigned int                 lookup_index,
595                                 hb_ot_layout_feature_mask_t  mask)
596 {
597   return layout->gsub->substitute_lookup (layout, buffer, lookup_index, mask);
598 }
599
600 /*
601  * GPOS
602  */
603
604 hb_bool_t
605 hb_ot_layout_position_lookup   (hb_ot_layout_t              *layout,
606                                 hb_buffer_t                 *buffer,
607                                 unsigned int                 lookup_index,
608                                 hb_ot_layout_feature_mask_t  mask)
609 {
610   return layout->gpos->position_lookup (layout, buffer, lookup_index, mask);
611 }