[HB] Move OT file handling out of ot-layout
[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-open-file-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 void
44 _hb_ot_layout_init (hb_ot_layout_t *layout,
45                     hb_face_t      *face)
46 {
47   layout->gdef = &Null(GDEF);
48   layout->gsub = &Null(GSUB);
49   layout->gpos = &Null(GPOS);
50 }
51
52 #if 0
53 hb_ot_layout_t *
54 hb_ot_layout_create_for_data (const char *font_data,
55                               int         face_index)
56 {
57   hb_ot_layout_t *layout;
58
59   if (HB_UNLIKELY (font_data == NULL))
60     return hb_ot_layout_create ();
61
62   layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
63
64   const OpenTypeFontFile &font = OpenTypeFontFile::get_for_data (font_data);
65   const OpenTypeFontFace &face = font.get_face (face_index);
66
67   layout->gdef = &GDEF::get_for_data (font.get_table_data (face.get_table_by_tag (GDEF::Tag)));
68   layout->gsub = &GSUB::get_for_data (font.get_table_data (face.get_table_by_tag (GSUB::Tag)));
69   layout->gpos = &GPOS::get_for_data (font.get_table_data (face.get_table_by_tag (GPOS::Tag)));
70
71   return layout;
72 }
73
74 hb_ot_layout_t *
75 hb_ot_layout_create_for_tables (const char *gdef_data,
76                                 const char *gsub_data,
77                                 const char *gpos_data)
78 {
79   hb_ot_layout_t *layout;
80
81   if (HB_UNLIKELY (gdef_data == NULL && gsub_data == NULL && gpos_data == NULL))
82     return hb_ot_layout_create ();
83
84   layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
85
86   layout->gdef = &GDEF::get_for_data (gdef_data);
87   layout->gsub = &GSUB::get_for_data (gsub_data);
88   layout->gpos = &GPOS::get_for_data (gpos_data);
89
90   return layout;
91 }
92 #endif
93
94 void
95 _hb_ot_layout_fini (hb_ot_layout_t *layout,
96                     hb_face_t      *face)
97 {
98 }
99
100 static hb_ot_layout_t *
101 _hb_ot_face_get_layout (hb_face_t *face)
102 {
103   return NULL; /* XXX */
104 }
105
106 /*
107  * GDEF
108  */
109
110 /* TODO the public class_t is a mess */
111
112 hb_bool_t
113 hb_ot_layout_has_font_glyph_classes (hb_face_t *face)
114 {
115   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
116   return layout->gdef->has_glyph_classes ();
117 }
118
119 HB_INTERNAL hb_bool_t
120 _hb_ot_layout_has_new_glyph_classes (hb_ot_layout_t *layout)
121 {
122   return layout->new_gdef.len > 0;
123 }
124
125 HB_INTERNAL unsigned int
126 _hb_ot_layout_get_glyph_property (hb_ot_layout_t *layout,
127                                   hb_codepoint_t  glyph)
128 {
129   hb_ot_layout_class_t klass;
130
131   klass = layout->gdef->get_glyph_class (glyph);
132
133   if (!klass && glyph < layout->new_gdef.len)
134     klass = layout->new_gdef.klasses[glyph];
135
136   switch (klass) {
137   default:
138   case GDEF::UnclassifiedGlyph: return HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED;
139   case GDEF::BaseGlyph:         return HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
140   case GDEF::LigatureGlyph:     return HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
141   case GDEF::ComponentGlyph:    return HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT;
142   case GDEF::MarkGlyph:
143         /* TODO old harfbuzz doesn't always parse mark attachments as it says it was
144          * introduced without a version bump, so it may not be safe */
145         klass = layout->gdef->get_mark_attachment_type (glyph);
146         return HB_OT_LAYOUT_GLYPH_CLASS_MARK + (klass << 8);
147   }
148 }
149
150 HB_INTERNAL hb_bool_t
151 _hb_ot_layout_check_glyph_property (hb_ot_layout_t  *layout,
152                                     hb_internal_glyph_info_t *ginfo,
153                                     unsigned int     lookup_flags,
154                                     unsigned int    *property_out)
155 {
156   unsigned int property;
157
158   if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
159     ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
160   property = ginfo->gproperty;
161   if (property_out)
162     *property_out = property;
163
164   /* Not covered, if, for example, glyph class is ligature and
165    * lookup_flags includes LookupFlags::IgnoreLigatures
166    */
167   if (property & lookup_flags)
168     return false;
169
170   if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
171   {
172     /* If using mark filtering sets, the high short of
173      * lookup_flags has the set index.
174      */
175     if (lookup_flags & LookupFlag::UseMarkFilteringSet)
176       return layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
177
178     /* The second byte of lookup_flags has the meaning
179      * "ignore marks of attachment type different than
180      * the attachment type specified."
181      */
182     if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
183       return (lookup_flags & LookupFlag::MarkAttachmentType) == (property & LookupFlag::MarkAttachmentType);
184   }
185
186   return true;
187 }
188
189 HB_INTERNAL hb_bool_t
190 _hb_ot_layout_skip_mark (hb_ot_layout_t  *layout,
191                          hb_internal_glyph_info_t *ginfo,
192                          unsigned int     lookup_flags,
193                          unsigned int    *property_out)
194 {
195   unsigned int property;
196
197   if (ginfo->gproperty == HB_BUFFER_GLYPH_PROPERTIES_UNKNOWN)
198     ginfo->gproperty = _hb_ot_layout_get_glyph_property (layout, ginfo->codepoint);
199   property = ginfo->gproperty;
200   if (property_out)
201     *property_out = property;
202
203   if (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
204   {
205     /* Skip mark if lookup_flags includes LookupFlags::IgnoreMarks */
206     if (lookup_flags & LookupFlag::IgnoreMarks)
207       return true;
208
209     /* If using mark filtering sets, the high short of lookup_flags has the set index. */
210     if (lookup_flags & LookupFlag::UseMarkFilteringSet)
211       return !layout->gdef->mark_set_covers (lookup_flags >> 16, ginfo->codepoint);
212
213     /* The second byte of lookup_flags has the meaning "ignore marks of attachment type
214      * different than the attachment type specified." */
215     if (lookup_flags & LookupFlag::MarkAttachmentType && property & LookupFlag::MarkAttachmentType)
216       return (lookup_flags & LookupFlag::MarkAttachmentType) != (property & LookupFlag::MarkAttachmentType);
217   }
218
219   return false;
220 }
221
222 HB_INTERNAL void
223 _hb_ot_layout_set_glyph_class (hb_ot_layout_t             *layout,
224                                hb_codepoint_t              glyph,
225                                hb_ot_layout_glyph_class_t  klass)
226 {
227   /* TODO optimize this? similar to old harfbuzz code for example */
228
229   hb_ot_layout_class_t gdef_klass;
230   int len = layout->new_gdef.len;
231
232   if (HB_UNLIKELY (glyph > 65535))
233     return;
234
235   /* XXX this is not threadsafe */
236   if (glyph >= len) {
237     int new_len;
238     unsigned char *new_klasses;
239
240     new_len = len == 0 ? 120 : 2 * len;
241     if (new_len > 65535)
242       new_len = 65535;
243     new_klasses = (unsigned char *) realloc (layout->new_gdef.klasses, new_len * sizeof (unsigned char));
244
245     if (HB_UNLIKELY (!new_klasses))
246       return;
247
248     memset (new_klasses + len, 0, new_len - len);
249
250     layout->new_gdef.klasses = new_klasses;
251     layout->new_gdef.len = new_len;
252   }
253
254   switch (klass) {
255   default:
256   case HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED:   gdef_klass = GDEF::UnclassifiedGlyph;   break;
257   case HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH:     gdef_klass = GDEF::BaseGlyph;           break;
258   case HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE:       gdef_klass = GDEF::LigatureGlyph;       break;
259   case HB_OT_LAYOUT_GLYPH_CLASS_MARK:           gdef_klass = GDEF::MarkGlyph;           break;
260   case HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT:      gdef_klass = GDEF::ComponentGlyph;      break;
261   }
262
263   layout->new_gdef.klasses[glyph] = gdef_klass;
264   return;
265 }
266
267 HB_INTERNAL void
268 _hb_ot_layout_set_glyph_property (hb_ot_layout_t *layout,
269                                   hb_codepoint_t  glyph,
270                                   unsigned int    property)
271 { _hb_ot_layout_set_glyph_class (layout, glyph, (hb_ot_layout_glyph_class_t) (property & 0xff)); }
272
273
274 hb_ot_layout_glyph_class_t
275 hb_ot_layout_get_glyph_class (hb_face_t      *face,
276                               hb_codepoint_t  glyph)
277 {
278   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
279   return (hb_ot_layout_glyph_class_t) (_hb_ot_layout_get_glyph_property (layout, glyph) & 0xff);
280 }
281
282 void
283 hb_ot_layout_set_glyph_class (hb_face_t                 *face,
284                               hb_codepoint_t             glyph,
285                               hb_ot_layout_glyph_class_t klass)
286 {
287   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
288   _hb_ot_layout_set_glyph_class (layout, glyph, klass);
289 }
290
291 void
292 hb_ot_layout_build_glyph_classes (hb_face_t      *face,
293                                   uint16_t        num_total_glyphs,
294                                   hb_codepoint_t *glyphs,
295                                   unsigned char  *klasses,
296                                   uint16_t        count)
297 {
298   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
299
300   if (HB_UNLIKELY (!count || !glyphs || !klasses))
301     return;
302
303   if (layout->new_gdef.len == 0) {
304     layout->new_gdef.klasses = (unsigned char *) calloc (num_total_glyphs, sizeof (unsigned char));
305     layout->new_gdef.len = count;
306   }
307
308   for (unsigned int i = 0; i < count; i++)
309     _hb_ot_layout_set_glyph_class (layout, glyphs[i], (hb_ot_layout_glyph_class_t) klasses[i]);
310 }
311
312 hb_bool_t
313 hb_ot_layout_get_attach_points (hb_face_t      *face,
314                                 hb_codepoint_t  glyph,
315                                 unsigned int   *point_count /* IN/OUT */,
316                                 unsigned int   *point_array /* OUT */)
317 {
318   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
319   return layout->gdef->get_attach_points (glyph, point_count, point_array);
320 }
321
322 hb_bool_t
323 hb_ot_layout_get_lig_carets (hb_face_t      *face,
324                              hb_font_t      *font,
325                              hb_codepoint_t  glyph,
326                              unsigned int   *caret_count /* IN/OUT */,
327                              int            *caret_array /* OUT */)
328 {
329   hb_ot_layout_context_t context;
330   context.font = font;
331   context.layout = _hb_ot_face_get_layout (face);
332   return context.layout->gdef->get_lig_carets (&context, glyph, caret_count, caret_array);
333 }
334
335 /*
336  * GSUB/GPOS
337  */
338
339 static const GSUBGPOS&
340 get_gsubgpos_table (hb_ot_layout_t *layout,
341                     hb_tag_t        table_tag)
342 {
343   switch (table_tag) {
344     case HB_OT_TAG_GSUB: return *(layout->gsub);
345     case HB_OT_TAG_GPOS: return *(layout->gpos);
346     default:             return Null(GSUBGPOS);
347   }
348 }
349
350
351 unsigned int
352 hb_ot_layout_table_get_script_count (hb_face_t *face,
353                                      hb_tag_t   table_tag)
354 {
355   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
356   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
357
358   return g.get_script_count ();
359 }
360
361 hb_tag_t
362 hb_ot_layout_table_get_script_tag (hb_face_t    *face,
363                                    hb_tag_t      table_tag,
364                                    unsigned int  script_index)
365 {
366   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
367   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
368
369   return g.get_script_tag (script_index);
370 }
371
372 hb_bool_t
373 hb_ot_layout_table_find_script (hb_face_t    *face,
374                                 hb_tag_t      table_tag,
375                                 hb_tag_t      script_tag,
376                                 unsigned int *script_index)
377 {
378   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
379   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
380   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
381
382   if (g.find_script_index (script_tag, script_index))
383     return TRUE;
384
385   /* try finding 'DFLT' */
386   if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_SCRIPT, script_index))
387     return FALSE;
388
389   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
390   if (g.find_script_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, script_index))
391     return FALSE;
392
393   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
394   return FALSE;
395 }
396
397 unsigned int
398 hb_ot_layout_table_get_feature_count (hb_face_t *face,
399                                       hb_tag_t   table_tag)
400 {
401   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
402   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
403
404   return g.get_feature_count ();
405 }
406
407 hb_tag_t
408 hb_ot_layout_table_get_feature_tag (hb_face_t    *face,
409                                     hb_tag_t      table_tag,
410                                     unsigned int  feature_index)
411 {
412   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
413   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
414
415   return g.get_feature_tag (feature_index);
416 }
417
418 hb_bool_t
419 hb_ot_layout_table_find_feature (hb_face_t    *face,
420                                  hb_tag_t      table_tag,
421                                  hb_tag_t      feature_tag,
422                                  unsigned int *feature_index)
423 {
424   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
425   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
426   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
427
428   if (g.find_feature_index (feature_tag, feature_index))
429     return TRUE;
430
431   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
432   return FALSE;
433 }
434
435 unsigned int
436 hb_ot_layout_table_get_lookup_count (hb_face_t *face,
437                                      hb_tag_t   table_tag)
438 {
439   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
440   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
441
442   return g.get_lookup_count ();
443 }
444
445
446 unsigned int
447 hb_ot_layout_script_get_language_count (hb_face_t    *face,
448                                         hb_tag_t      table_tag,
449                                         unsigned int  script_index)
450 {
451   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
452   const Script &s = get_gsubgpos_table (layout, table_tag).get_script (script_index);
453
454   return s.get_lang_sys_count ();
455 }
456
457 hb_tag_t
458 hb_ot_layout_script_get_language_tag (hb_face_t    *face,
459                                       hb_tag_t      table_tag,
460                                       unsigned int  script_index,
461                                       unsigned int  language_index)
462 {
463   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
464   const Script &s = get_gsubgpos_table (layout, table_tag).get_script (script_index);
465
466   return s.get_lang_sys_tag (language_index);
467 }
468
469 hb_bool_t
470 hb_ot_layout_script_find_language (hb_face_t    *face,
471                                    hb_tag_t      table_tag,
472                                    unsigned int  script_index,
473                                    hb_tag_t      language_tag,
474                                    unsigned int *language_index)
475 {
476   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
477   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
478   const Script &s = get_gsubgpos_table (layout, table_tag).get_script (script_index);
479
480   if (s.find_lang_sys_index (language_tag, language_index))
481     return TRUE;
482
483   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
484   if (s.find_lang_sys_index (HB_OT_LAYOUT_TAG_DEFAULT_LANGUAGE, language_index))
485     return FALSE;
486
487   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
488   return FALSE;
489 }
490
491 hb_bool_t
492 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
493                                                   hb_tag_t      table_tag,
494                                                   unsigned int  script_index,
495                                                   unsigned int  language_index,
496                                                   unsigned int *feature_index)
497 {
498   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
499   const LangSys &l = get_gsubgpos_table (layout, table_tag).get_script (script_index).get_lang_sys (language_index);
500
501   if (feature_index) *feature_index = l.get_required_feature_index ();
502
503   return l.has_required_feature ();
504 }
505
506 unsigned int
507 hb_ot_layout_language_get_feature_count (hb_face_t    *face,
508                                          hb_tag_t      table_tag,
509                                          unsigned int  script_index,
510                                          unsigned int  language_index)
511 {
512   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
513   const LangSys &l = get_gsubgpos_table (layout, table_tag).get_script (script_index).get_lang_sys (language_index);
514
515   return l.get_feature_count ();
516 }
517
518 unsigned int
519 hb_ot_layout_language_get_feature_index (hb_face_t    *face,
520                                          hb_tag_t      table_tag,
521                                          unsigned int  script_index,
522                                          unsigned int  language_index,
523                                          unsigned int  num_feature)
524 {
525   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
526   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
527   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
528
529   return l.get_feature_index (num_feature);
530 }
531
532 hb_tag_t
533 hb_ot_layout_language_get_feature_tag (hb_face_t    *face,
534                                        hb_tag_t      table_tag,
535                                        unsigned int  script_index,
536                                        unsigned int  language_index,
537                                        unsigned int  num_feature)
538 {
539   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
540   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
541   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
542   unsigned int feature_index = l.get_feature_index (num_feature);
543
544   return g.get_feature_tag (feature_index);
545 }
546
547
548 hb_bool_t
549 hb_ot_layout_language_find_feature (hb_face_t    *face,
550                                     hb_tag_t      table_tag,
551                                     unsigned int  script_index,
552                                     unsigned int  language_index,
553                                     hb_tag_t      feature_tag,
554                                     unsigned int *feature_index)
555 {
556   ASSERT_STATIC (NO_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
557   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
558   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
559   const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
560
561   unsigned int num_features = l.get_feature_count ();
562   for (unsigned int i = 0; i < num_features; i++) {
563     unsigned int f_index = l.get_feature_index (i);
564
565     if (feature_tag == g.get_feature_tag (f_index)) {
566       if (feature_index) *feature_index = f_index;
567       return TRUE;
568     }
569   }
570
571   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
572   return FALSE;
573 }
574
575 unsigned int
576 hb_ot_layout_feature_get_lookup_count (hb_face_t    *face,
577                                        hb_tag_t      table_tag,
578                                        unsigned int  feature_index)
579 {
580   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
581   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
582   const Feature &f = g.get_feature (feature_index);
583
584   return f.get_lookup_count ();
585 }
586
587 unsigned int
588 hb_ot_layout_feature_get_lookup_index (hb_face_t    *face,
589                                        hb_tag_t      table_tag,
590                                        unsigned int  feature_index,
591                                        unsigned int  num_lookup)
592 {
593   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
594   const GSUBGPOS &g = get_gsubgpos_table (layout, table_tag);
595   const Feature &f = g.get_feature (feature_index);
596
597   return f.get_lookup_index (num_lookup);
598 }
599
600 /*
601  * GSUB
602  */
603
604 hb_bool_t
605 hb_ot_layout_has_substitution (hb_face_t *face)
606 {
607   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
608   return layout->gsub != &Null(GSUB);
609 }
610
611 hb_bool_t
612 hb_ot_layout_substitute_lookup (hb_face_t                   *face,
613                                 hb_buffer_t                 *buffer,
614                                 unsigned int                 lookup_index,
615                                 hb_ot_layout_feature_mask_t  mask)
616 {
617   hb_ot_layout_context_t context;
618   context.font = NULL;
619   context.layout = _hb_ot_face_get_layout (face);
620   return context.layout->gsub->substitute_lookup (&context, buffer, lookup_index, mask);
621 }
622
623 /*
624  * GPOS
625  */
626
627 hb_bool_t
628 hb_ot_layout_has_positioning (hb_face_t *face)
629 {
630   hb_ot_layout_t *layout = _hb_ot_face_get_layout (face);
631   return layout->gpos != &Null(GPOS);
632 }
633
634 hb_bool_t
635 hb_ot_layout_position_lookup   (hb_face_t                   *face,
636                                 hb_font_t                   *font,
637                                 hb_buffer_t                 *buffer,
638                                 unsigned int                 lookup_index,
639                                 hb_ot_layout_feature_mask_t  mask)
640 {
641   hb_ot_layout_context_t context;
642   context.font = font;
643   context.layout = _hb_ot_face_get_layout (font->face);
644   return context.layout->gpos->position_lookup (&context, buffer, lookup_index, mask);
645 }