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