Home | History | Annotate | Download | only in src
      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  Google, Inc.
      6  *
      7  *  This is part of HarfBuzz, a text shaping library.
      8  *
      9  * Permission is hereby granted, without written agreement and without
     10  * license or royalty fees, to use, copy, modify, and distribute this
     11  * software and its documentation for any purpose, provided that the
     12  * above copyright notice and the following two paragraphs appear in
     13  * all copies of this software.
     14  *
     15  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     16  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     17  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     18  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     19  * DAMAGE.
     20  *
     21  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     22  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     23  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     24  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     25  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     26  *
     27  * Red Hat Author(s): Behdad Esfahbod
     28  * Google Author(s): Behdad Esfahbod
     29  */
     30 
     31 #include "hb-ot-layout-private.hh"
     32 
     33 #include "hb-ot-layout-gdef-table.hh"
     34 #include "hb-ot-layout-gsub-table.hh"
     35 #include "hb-ot-layout-gpos-table.hh"
     36 
     37 #include <stdlib.h>
     38 #include <string.h>
     39 
     40 
     41 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
     42 
     43 hb_ot_layout_t *
     44 _hb_ot_layout_create (hb_face_t *face)
     45 {
     46   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
     47   if (unlikely (!layout))
     48     return NULL;
     49 
     50   layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
     51   layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
     52 
     53   layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
     54   layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
     55 
     56   layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
     57   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
     58 
     59   layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
     60   layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
     61 
     62   layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t));
     63   layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t));
     64 
     65   if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) ||
     66 		(layout->gpos_lookup_count && !layout->gpos_digests)))
     67   {
     68     _hb_ot_layout_destroy (layout);
     69     return NULL;
     70   }
     71 
     72   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
     73   {
     74     layout->gsub_digests[i].init ();
     75     layout->gsub->get_lookup (i).add_coverage (&layout->gsub_digests[i]);
     76   }
     77   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
     78   {
     79     layout->gpos_digests[i].init ();
     80     layout->gpos->get_lookup (i).add_coverage (&layout->gpos_digests[i]);
     81   }
     82 
     83   return layout;
     84 }
     85 
     86 void
     87 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
     88 {
     89   hb_blob_destroy (layout->gdef_blob);
     90   hb_blob_destroy (layout->gsub_blob);
     91   hb_blob_destroy (layout->gpos_blob);
     92 
     93   free (layout->gsub_digests);
     94   free (layout->gpos_digests);
     95 
     96   free (layout);
     97 }
     98 
     99 static inline const OT::GDEF&
    100 _get_gdef (hb_face_t *face)
    101 {
    102   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
    103   return *hb_ot_layout_from_face (face)->gdef;
    104 }
    105 static inline const OT::GSUB&
    106 _get_gsub (hb_face_t *face)
    107 {
    108   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
    109   return *hb_ot_layout_from_face (face)->gsub;
    110 }
    111 static inline const OT::GPOS&
    112 _get_gpos (hb_face_t *face)
    113 {
    114   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
    115   return *hb_ot_layout_from_face (face)->gpos;
    116 }
    117 
    118 
    119 /*
    120  * GDEF
    121  */
    122 
    123 hb_bool_t
    124 hb_ot_layout_has_glyph_classes (hb_face_t *face)
    125 {
    126   return _get_gdef (face).has_glyph_classes ();
    127 }
    128 
    129 hb_ot_layout_glyph_class_t
    130 hb_ot_layout_get_glyph_class (hb_face_t      *face,
    131 			      hb_codepoint_t  glyph)
    132 {
    133   return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
    134 }
    135 
    136 void
    137 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
    138 				  hb_ot_layout_glyph_class_t  klass,
    139 				  hb_set_t                   *glyphs /* OUT */)
    140 {
    141   return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
    142 }
    143 
    144 unsigned int
    145 hb_ot_layout_get_attach_points (hb_face_t      *face,
    146 				hb_codepoint_t  glyph,
    147 				unsigned int    start_offset,
    148 				unsigned int   *point_count /* IN/OUT */,
    149 				unsigned int   *point_array /* OUT */)
    150 {
    151   return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
    152 }
    153 
    154 unsigned int
    155 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
    156 				  hb_direction_t  direction,
    157 				  hb_codepoint_t  glyph,
    158 				  unsigned int    start_offset,
    159 				  unsigned int   *caret_count /* IN/OUT */,
    160 				  int            *caret_array /* OUT */)
    161 {
    162   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
    163 }
    164 
    165 
    166 /*
    167  * GSUB/GPOS
    168  */
    169 
    170 static const OT::GSUBGPOS&
    171 get_gsubgpos_table (hb_face_t *face,
    172 		    hb_tag_t   table_tag)
    173 {
    174   switch (table_tag) {
    175     case HB_OT_TAG_GSUB: return _get_gsub (face);
    176     case HB_OT_TAG_GPOS: return _get_gpos (face);
    177     default:             return OT::Null(OT::GSUBGPOS);
    178   }
    179 }
    180 
    181 
    182 unsigned int
    183 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
    184 				    hb_tag_t      table_tag,
    185 				    unsigned int  start_offset,
    186 				    unsigned int *script_count /* IN/OUT */,
    187 				    hb_tag_t     *script_tags /* OUT */)
    188 {
    189   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    190 
    191   return g.get_script_tags (start_offset, script_count, script_tags);
    192 }
    193 
    194 #define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
    195 
    196 hb_bool_t
    197 hb_ot_layout_table_find_script (hb_face_t    *face,
    198 				hb_tag_t      table_tag,
    199 				hb_tag_t      script_tag,
    200 				unsigned int *script_index)
    201 {
    202   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
    203   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    204 
    205   if (g.find_script_index (script_tag, script_index))
    206     return true;
    207 
    208   /* try finding 'DFLT' */
    209   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
    210     return false;
    211 
    212   /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
    213    * including many versions of DejaVu Sans Mono! */
    214   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
    215     return false;
    216 
    217   /* try with 'latn'; some old fonts put their features there even though
    218      they're really trying to support Thai, for example :( */
    219   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
    220     return false;
    221 
    222   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
    223   return false;
    224 }
    225 
    226 hb_bool_t
    227 hb_ot_layout_table_choose_script (hb_face_t      *face,
    228 				  hb_tag_t        table_tag,
    229 				  const hb_tag_t *script_tags,
    230 				  unsigned int   *script_index,
    231 				  hb_tag_t       *chosen_script)
    232 {
    233   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
    234   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    235 
    236   while (*script_tags)
    237   {
    238     if (g.find_script_index (*script_tags, script_index)) {
    239       if (chosen_script)
    240         *chosen_script = *script_tags;
    241       return true;
    242     }
    243     script_tags++;
    244   }
    245 
    246   /* try finding 'DFLT' */
    247   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
    248     if (chosen_script)
    249       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
    250     return false;
    251   }
    252 
    253   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
    254   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
    255     if (chosen_script)
    256       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
    257     return false;
    258   }
    259 
    260   /* try with 'latn'; some old fonts put their features there even though
    261      they're really trying to support Thai, for example :( */
    262   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
    263     if (chosen_script)
    264       *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
    265     return false;
    266   }
    267 
    268   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
    269   if (chosen_script)
    270     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
    271   return false;
    272 }
    273 
    274 unsigned int
    275 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
    276 				     hb_tag_t      table_tag,
    277 				     unsigned int  start_offset,
    278 				     unsigned int *feature_count /* IN/OUT */,
    279 				     hb_tag_t     *feature_tags /* OUT */)
    280 {
    281   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    282 
    283   return g.get_feature_tags (start_offset, feature_count, feature_tags);
    284 }
    285 
    286 
    287 unsigned int
    288 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
    289 				       hb_tag_t      table_tag,
    290 				       unsigned int  script_index,
    291 				       unsigned int  start_offset,
    292 				       unsigned int *language_count /* IN/OUT */,
    293 				       hb_tag_t     *language_tags /* OUT */)
    294 {
    295   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
    296 
    297   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
    298 }
    299 
    300 hb_bool_t
    301 hb_ot_layout_script_find_language (hb_face_t    *face,
    302 				   hb_tag_t      table_tag,
    303 				   unsigned int  script_index,
    304 				   hb_tag_t      language_tag,
    305 				   unsigned int *language_index)
    306 {
    307   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
    308   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
    309 
    310   if (s.find_lang_sys_index (language_tag, language_index))
    311     return true;
    312 
    313   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
    314   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
    315     return false;
    316 
    317   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
    318   return false;
    319 }
    320 
    321 hb_bool_t
    322 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
    323 						  hb_tag_t      table_tag,
    324 						  unsigned int  script_index,
    325 						  unsigned int  language_index,
    326 						  unsigned int *feature_index)
    327 {
    328   const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
    329 
    330   if (feature_index) *feature_index = l.get_required_feature_index ();
    331 
    332   return l.has_required_feature ();
    333 }
    334 
    335 unsigned int
    336 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
    337 					   hb_tag_t      table_tag,
    338 					   unsigned int  script_index,
    339 					   unsigned int  language_index,
    340 					   unsigned int  start_offset,
    341 					   unsigned int *feature_count /* IN/OUT */,
    342 					   unsigned int *feature_indexes /* OUT */)
    343 {
    344   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    345   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    346 
    347   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
    348 }
    349 
    350 unsigned int
    351 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
    352 					hb_tag_t      table_tag,
    353 					unsigned int  script_index,
    354 					unsigned int  language_index,
    355 					unsigned int  start_offset,
    356 					unsigned int *feature_count /* IN/OUT */,
    357 					hb_tag_t     *feature_tags /* OUT */)
    358 {
    359   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    360   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    361 
    362   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
    363   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
    364 
    365   if (feature_tags) {
    366     unsigned int count = *feature_count;
    367     for (unsigned int i = 0; i < count; i++)
    368       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
    369   }
    370 
    371   return ret;
    372 }
    373 
    374 
    375 hb_bool_t
    376 hb_ot_layout_language_find_feature (hb_face_t    *face,
    377 				    hb_tag_t      table_tag,
    378 				    unsigned int  script_index,
    379 				    unsigned int  language_index,
    380 				    hb_tag_t      feature_tag,
    381 				    unsigned int *feature_index)
    382 {
    383   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
    384   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    385   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    386 
    387   unsigned int num_features = l.get_feature_count ();
    388   for (unsigned int i = 0; i < num_features; i++) {
    389     unsigned int f_index = l.get_feature_index (i);
    390 
    391     if (feature_tag == g.get_feature_tag (f_index)) {
    392       if (feature_index) *feature_index = f_index;
    393       return true;
    394     }
    395   }
    396 
    397   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
    398   return false;
    399 }
    400 
    401 unsigned int
    402 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
    403 				  hb_tag_t      table_tag,
    404 				  unsigned int  feature_index,
    405 				  unsigned int  start_offset,
    406 				  unsigned int *lookup_count /* IN/OUT */,
    407 				  unsigned int *lookup_indexes /* OUT */)
    408 {
    409   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    410   const OT::Feature &f = g.get_feature (feature_index);
    411 
    412   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
    413 }
    414 
    415 static void
    416 _hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
    417 				       hb_tag_t        table_tag,
    418 				       unsigned int    feature_index,
    419 				       hb_set_t       *lookup_indexes /* OUT */)
    420 {
    421   unsigned int lookup_indices[32];
    422   unsigned int offset, len;
    423 
    424   offset = 0;
    425   do {
    426     len = ARRAY_LENGTH (lookup_indices);
    427     hb_ot_layout_feature_get_lookups (face,
    428 				      table_tag,
    429 				      feature_index,
    430 				      offset, &len,
    431 				      lookup_indices);
    432 
    433     for (unsigned int i = 0; i < len; i++)
    434       lookup_indexes->add (lookup_indices[i]);
    435 
    436     offset += len;
    437   } while (len == ARRAY_LENGTH (lookup_indices));
    438 }
    439 
    440 static void
    441 _hb_ot_layout_collect_lookups_features (hb_face_t      *face,
    442 					hb_tag_t        table_tag,
    443 					unsigned int    script_index,
    444 					unsigned int    language_index,
    445 					const hb_tag_t *features,
    446 					hb_set_t       *lookup_indexes /* OUT */)
    447 {
    448   unsigned int required_feature_index;
    449   if (hb_ot_layout_language_get_required_feature_index (face,
    450 							table_tag,
    451 							script_index,
    452 							language_index,
    453 							&required_feature_index))
    454     _hb_ot_layout_collect_lookups_lookups (face,
    455 					   table_tag,
    456 					   required_feature_index,
    457 					   lookup_indexes);
    458 
    459   if (!features)
    460   {
    461     /* All features */
    462     unsigned int feature_indices[32];
    463     unsigned int offset, len;
    464 
    465     offset = 0;
    466     do {
    467       len = ARRAY_LENGTH (feature_indices);
    468       hb_ot_layout_language_get_feature_indexes (face,
    469 						 table_tag,
    470 						 script_index,
    471 						 language_index,
    472 						 offset, &len,
    473 						 feature_indices);
    474 
    475       for (unsigned int i = 0; i < len; i++)
    476 	_hb_ot_layout_collect_lookups_lookups (face,
    477 					       table_tag,
    478 					       feature_indices[i],
    479 					       lookup_indexes);
    480 
    481       offset += len;
    482     } while (len == ARRAY_LENGTH (feature_indices));
    483   }
    484   else
    485   {
    486     for (; *features; features++)
    487     {
    488       unsigned int feature_index;
    489       if (hb_ot_layout_language_find_feature (face,
    490 					      table_tag,
    491 					      script_index,
    492 					      language_index,
    493 					      *features,
    494 					      &feature_index))
    495         _hb_ot_layout_collect_lookups_lookups (face,
    496 					       table_tag,
    497 					       feature_index,
    498 					       lookup_indexes);
    499     }
    500   }
    501 }
    502 
    503 static void
    504 _hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
    505 					 hb_tag_t        table_tag,
    506 					 unsigned int    script_index,
    507 					 const hb_tag_t *languages,
    508 					 const hb_tag_t *features,
    509 					 hb_set_t       *lookup_indexes /* OUT */)
    510 {
    511   _hb_ot_layout_collect_lookups_features (face,
    512 					  table_tag,
    513 					  script_index,
    514 					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
    515 					  features,
    516 					  lookup_indexes);
    517 
    518   if (!languages)
    519   {
    520     /* All languages */
    521     unsigned int count = hb_ot_layout_script_get_language_tags (face,
    522 								table_tag,
    523 								script_index,
    524 								0, NULL, NULL);
    525     for (unsigned int language_index = 0; language_index < count; language_index++)
    526       _hb_ot_layout_collect_lookups_features (face,
    527 					      table_tag,
    528 					      script_index,
    529 					      language_index,
    530 					      features,
    531 					      lookup_indexes);
    532   }
    533   else
    534   {
    535     for (; *languages; languages++)
    536     {
    537       unsigned int language_index;
    538       if (hb_ot_layout_script_find_language (face,
    539 					     table_tag,
    540 					     script_index,
    541 					     *languages,
    542 					     &language_index))
    543         _hb_ot_layout_collect_lookups_features (face,
    544 						table_tag,
    545 						script_index,
    546 						language_index,
    547 						features,
    548 						lookup_indexes);
    549     }
    550   }
    551 }
    552 
    553 void
    554 hb_ot_layout_collect_lookups (hb_face_t      *face,
    555 			      hb_tag_t        table_tag,
    556 			      const hb_tag_t *scripts,
    557 			      const hb_tag_t *languages,
    558 			      const hb_tag_t *features,
    559 			      hb_set_t       *lookup_indexes /* OUT */)
    560 {
    561   if (!scripts)
    562   {
    563     /* All scripts */
    564     unsigned int count = hb_ot_layout_table_get_script_tags (face,
    565 							     table_tag,
    566 							     0, NULL, NULL);
    567     for (unsigned int script_index = 0; script_index < count; script_index++)
    568       _hb_ot_layout_collect_lookups_languages (face,
    569 					       table_tag,
    570 					       script_index,
    571 					       languages,
    572 					       features,
    573 					       lookup_indexes);
    574   }
    575   else
    576   {
    577     for (; *scripts; scripts++)
    578     {
    579       unsigned int script_index;
    580       if (hb_ot_layout_table_find_script (face,
    581 					  table_tag,
    582 					  *scripts,
    583 					  &script_index))
    584         _hb_ot_layout_collect_lookups_languages (face,
    585 						 table_tag,
    586 						 script_index,
    587 						 languages,
    588 						 features,
    589 						 lookup_indexes);
    590     }
    591   }
    592 }
    593 
    594 void
    595 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
    596 				    hb_tag_t      table_tag,
    597 				    unsigned int  lookup_index,
    598 				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
    599 				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
    600 				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
    601 				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
    602 {
    603   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
    604 
    605   OT::hb_collect_glyphs_context_t c (face,
    606 				     glyphs_before,
    607 				     glyphs_input,
    608 				     glyphs_after,
    609 				     glyphs_output);
    610 
    611   switch (table_tag)
    612   {
    613     case HB_OT_TAG_GSUB:
    614     {
    615       const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
    616       l.collect_glyphs_lookup (&c);
    617       return;
    618     }
    619     case HB_OT_TAG_GPOS:
    620     {
    621       const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
    622       l.collect_glyphs_lookup (&c);
    623       return;
    624     }
    625   }
    626 }
    627 
    628 
    629 /*
    630  * OT::GSUB
    631  */
    632 
    633 hb_bool_t
    634 hb_ot_layout_has_substitution (hb_face_t *face)
    635 {
    636   return &_get_gsub (face) != &OT::Null(OT::GSUB);
    637 }
    638 
    639 hb_bool_t
    640 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
    641 				      unsigned int          lookup_index,
    642 				      const hb_codepoint_t *glyphs,
    643 				      unsigned int          glyphs_length,
    644 				      hb_bool_t             zero_context)
    645 {
    646   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
    647   return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
    648 }
    649 
    650 hb_bool_t
    651 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
    652 					   unsigned int          lookup_index,
    653 					   const hb_codepoint_t *glyphs,
    654 					   unsigned int          glyphs_length,
    655 					   hb_bool_t             zero_context)
    656 {
    657   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
    658   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
    659 
    660   const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
    661 
    662   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
    663 }
    664 
    665 void
    666 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
    667 {
    668   OT::GSUB::substitute_start (font, buffer);
    669 }
    670 
    671 hb_bool_t
    672 hb_ot_layout_substitute_lookup (hb_font_t    *font,
    673 				hb_buffer_t  *buffer,
    674 				unsigned int  lookup_index,
    675 				hb_mask_t     mask,
    676 				hb_bool_t     auto_zwj)
    677 {
    678   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
    679 
    680   OT::hb_apply_context_t c (0, font, buffer, mask, auto_zwj);
    681 
    682   const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index);
    683 
    684   return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
    685 }
    686 
    687 void
    688 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
    689 {
    690   OT::GSUB::substitute_finish (font, buffer);
    691 }
    692 
    693 void
    694 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
    695 				        unsigned int  lookup_index,
    696 				        hb_set_t     *glyphs)
    697 {
    698   OT::hb_closure_context_t c (face, glyphs);
    699 
    700   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
    701 
    702   l.closure (&c);
    703 }
    704 
    705 /*
    706  * OT::GPOS
    707  */
    708 
    709 hb_bool_t
    710 hb_ot_layout_has_positioning (hb_face_t *face)
    711 {
    712   return &_get_gpos (face) != &OT::Null(OT::GPOS);
    713 }
    714 
    715 void
    716 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
    717 {
    718   OT::GPOS::position_start (font, buffer);
    719 }
    720 
    721 hb_bool_t
    722 hb_ot_layout_position_lookup (hb_font_t    *font,
    723 			      hb_buffer_t  *buffer,
    724 			      unsigned int  lookup_index,
    725 			      hb_mask_t     mask,
    726 			      hb_bool_t     auto_zwj)
    727 {
    728   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
    729 
    730   OT::hb_apply_context_t c (1, font, buffer, mask, auto_zwj);
    731 
    732   const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index);
    733 
    734   return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
    735 }
    736 
    737 void
    738 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
    739 {
    740   OT::GPOS::position_finish (font, buffer);
    741 }
    742 
    743 hb_bool_t
    744 hb_ot_layout_get_size_params (hb_face_t    *face,
    745 			      unsigned int *design_size,       /* OUT.  May be NULL */
    746 			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
    747 			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
    748 			      unsigned int *range_start,       /* OUT.  May be NULL */
    749 			      unsigned int *range_end          /* OUT.  May be NULL */)
    750 {
    751   const OT::GPOS &gpos = _get_gpos (face);
    752   const hb_tag_t tag = HB_TAG ('s','i','z','e');
    753 
    754   unsigned int num_features = gpos.get_feature_count ();
    755   for (unsigned int i = 0; i < num_features; i++)
    756   {
    757     if (tag == gpos.get_feature_tag (i))
    758     {
    759       const OT::Feature &f = gpos.get_feature (i);
    760       const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
    761 
    762       if (params.designSize)
    763       {
    764 #define PARAM(a, A) if (a) *a = params.A
    765 	PARAM (design_size, designSize);
    766 	PARAM (subfamily_id, subfamilyID);
    767 	PARAM (subfamily_name_id, subfamilyNameID);
    768 	PARAM (range_start, rangeStart);
    769 	PARAM (range_end, rangeEnd);
    770 #undef PARAM
    771 
    772 	return true;
    773       }
    774     }
    775   }
    776 
    777 #define PARAM(a, A) if (a) *a = 0
    778   PARAM (design_size, designSize);
    779   PARAM (subfamily_id, subfamilyID);
    780   PARAM (subfamily_name_id, subfamilyNameID);
    781   PARAM (range_start, rangeStart);
    782   PARAM (range_end, rangeEnd);
    783 #undef PARAM
    784 
    785   return false;
    786 }
    787