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