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 ();
     88   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
     89     layout->gpos_accels[i].fini ();
     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   return hb_ot_layout_language_get_required_feature (face,
    331 						     table_tag,
    332 						     script_index,
    333 						     language_index,
    334 						     feature_index,
    335 						     NULL);
    336 }
    337 
    338 hb_bool_t
    339 hb_ot_layout_language_get_required_feature (hb_face_t    *face,
    340 					    hb_tag_t      table_tag,
    341 					    unsigned int  script_index,
    342 					    unsigned int  language_index,
    343 					    unsigned int *feature_index,
    344 					    hb_tag_t     *feature_tag)
    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   unsigned int index = l.get_required_feature_index ();
    350   if (feature_index) *feature_index = index;
    351   if (feature_tag) *feature_tag = g.get_feature_tag (index);
    352 
    353   return l.has_required_feature ();
    354 }
    355 
    356 unsigned int
    357 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
    358 					   hb_tag_t      table_tag,
    359 					   unsigned int  script_index,
    360 					   unsigned int  language_index,
    361 					   unsigned int  start_offset,
    362 					   unsigned int *feature_count /* IN/OUT */,
    363 					   unsigned int *feature_indexes /* OUT */)
    364 {
    365   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    366   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    367 
    368   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
    369 }
    370 
    371 unsigned int
    372 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
    373 					hb_tag_t      table_tag,
    374 					unsigned int  script_index,
    375 					unsigned int  language_index,
    376 					unsigned int  start_offset,
    377 					unsigned int *feature_count /* IN/OUT */,
    378 					hb_tag_t     *feature_tags /* OUT */)
    379 {
    380   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    381   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    382 
    383   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
    384   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
    385 
    386   if (feature_tags) {
    387     unsigned int count = *feature_count;
    388     for (unsigned int i = 0; i < count; i++)
    389       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
    390   }
    391 
    392   return ret;
    393 }
    394 
    395 
    396 hb_bool_t
    397 hb_ot_layout_language_find_feature (hb_face_t    *face,
    398 				    hb_tag_t      table_tag,
    399 				    unsigned int  script_index,
    400 				    unsigned int  language_index,
    401 				    hb_tag_t      feature_tag,
    402 				    unsigned int *feature_index)
    403 {
    404   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
    405   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    406   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    407 
    408   unsigned int num_features = l.get_feature_count ();
    409   for (unsigned int i = 0; i < num_features; i++) {
    410     unsigned int f_index = l.get_feature_index (i);
    411 
    412     if (feature_tag == g.get_feature_tag (f_index)) {
    413       if (feature_index) *feature_index = f_index;
    414       return true;
    415     }
    416   }
    417 
    418   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
    419   return false;
    420 }
    421 
    422 unsigned int
    423 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
    424 				  hb_tag_t      table_tag,
    425 				  unsigned int  feature_index,
    426 				  unsigned int  start_offset,
    427 				  unsigned int *lookup_count /* IN/OUT */,
    428 				  unsigned int *lookup_indexes /* OUT */)
    429 {
    430   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    431   const OT::Feature &f = g.get_feature (feature_index);
    432 
    433   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
    434 }
    435 
    436 unsigned int
    437 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
    438 				     hb_tag_t      table_tag)
    439 {
    440   switch (table_tag)
    441   {
    442     case HB_OT_TAG_GSUB:
    443     {
    444       return hb_ot_layout_from_face (face)->gsub_lookup_count;
    445     }
    446     case HB_OT_TAG_GPOS:
    447     {
    448       return hb_ot_layout_from_face (face)->gpos_lookup_count;
    449     }
    450   }
    451   return 0;
    452 }
    453 
    454 static void
    455 _hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
    456 				       hb_tag_t        table_tag,
    457 				       unsigned int    feature_index,
    458 				       hb_set_t       *lookup_indexes /* OUT */)
    459 {
    460   unsigned int lookup_indices[32];
    461   unsigned int offset, len;
    462 
    463   offset = 0;
    464   do {
    465     len = ARRAY_LENGTH (lookup_indices);
    466     hb_ot_layout_feature_get_lookups (face,
    467 				      table_tag,
    468 				      feature_index,
    469 				      offset, &len,
    470 				      lookup_indices);
    471 
    472     for (unsigned int i = 0; i < len; i++)
    473       lookup_indexes->add (lookup_indices[i]);
    474 
    475     offset += len;
    476   } while (len == ARRAY_LENGTH (lookup_indices));
    477 }
    478 
    479 static void
    480 _hb_ot_layout_collect_lookups_features (hb_face_t      *face,
    481 					hb_tag_t        table_tag,
    482 					unsigned int    script_index,
    483 					unsigned int    language_index,
    484 					const hb_tag_t *features,
    485 					hb_set_t       *lookup_indexes /* OUT */)
    486 {
    487   if (!features)
    488   {
    489     unsigned int required_feature_index;
    490     if (hb_ot_layout_language_get_required_feature (face,
    491 						    table_tag,
    492 						    script_index,
    493 						    language_index,
    494 						    &required_feature_index,
    495 						    NULL))
    496       _hb_ot_layout_collect_lookups_lookups (face,
    497 					     table_tag,
    498 					     required_feature_index,
    499 					     lookup_indexes);
    500 
    501     /* All features */
    502     unsigned int feature_indices[32];
    503     unsigned int offset, len;
    504 
    505     offset = 0;
    506     do {
    507       len = ARRAY_LENGTH (feature_indices);
    508       hb_ot_layout_language_get_feature_indexes (face,
    509 						 table_tag,
    510 						 script_index,
    511 						 language_index,
    512 						 offset, &len,
    513 						 feature_indices);
    514 
    515       for (unsigned int i = 0; i < len; i++)
    516 	_hb_ot_layout_collect_lookups_lookups (face,
    517 					       table_tag,
    518 					       feature_indices[i],
    519 					       lookup_indexes);
    520 
    521       offset += len;
    522     } while (len == ARRAY_LENGTH (feature_indices));
    523   }
    524   else
    525   {
    526     for (; *features; features++)
    527     {
    528       unsigned int feature_index;
    529       if (hb_ot_layout_language_find_feature (face,
    530 					      table_tag,
    531 					      script_index,
    532 					      language_index,
    533 					      *features,
    534 					      &feature_index))
    535         _hb_ot_layout_collect_lookups_lookups (face,
    536 					       table_tag,
    537 					       feature_index,
    538 					       lookup_indexes);
    539     }
    540   }
    541 }
    542 
    543 static void
    544 _hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
    545 					 hb_tag_t        table_tag,
    546 					 unsigned int    script_index,
    547 					 const hb_tag_t *languages,
    548 					 const hb_tag_t *features,
    549 					 hb_set_t       *lookup_indexes /* OUT */)
    550 {
    551   _hb_ot_layout_collect_lookups_features (face,
    552 					  table_tag,
    553 					  script_index,
    554 					  HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
    555 					  features,
    556 					  lookup_indexes);
    557 
    558   if (!languages)
    559   {
    560     /* All languages */
    561     unsigned int count = hb_ot_layout_script_get_language_tags (face,
    562 								table_tag,
    563 								script_index,
    564 								0, NULL, NULL);
    565     for (unsigned int language_index = 0; language_index < count; language_index++)
    566       _hb_ot_layout_collect_lookups_features (face,
    567 					      table_tag,
    568 					      script_index,
    569 					      language_index,
    570 					      features,
    571 					      lookup_indexes);
    572   }
    573   else
    574   {
    575     for (; *languages; languages++)
    576     {
    577       unsigned int language_index;
    578       if (hb_ot_layout_script_find_language (face,
    579 					     table_tag,
    580 					     script_index,
    581 					     *languages,
    582 					     &language_index))
    583         _hb_ot_layout_collect_lookups_features (face,
    584 						table_tag,
    585 						script_index,
    586 						language_index,
    587 						features,
    588 						lookup_indexes);
    589     }
    590   }
    591 }
    592 
    593 void
    594 hb_ot_layout_collect_lookups (hb_face_t      *face,
    595 			      hb_tag_t        table_tag,
    596 			      const hb_tag_t *scripts,
    597 			      const hb_tag_t *languages,
    598 			      const hb_tag_t *features,
    599 			      hb_set_t       *lookup_indexes /* OUT */)
    600 {
    601   if (!scripts)
    602   {
    603     /* All scripts */
    604     unsigned int count = hb_ot_layout_table_get_script_tags (face,
    605 							     table_tag,
    606 							     0, NULL, NULL);
    607     for (unsigned int script_index = 0; script_index < count; script_index++)
    608       _hb_ot_layout_collect_lookups_languages (face,
    609 					       table_tag,
    610 					       script_index,
    611 					       languages,
    612 					       features,
    613 					       lookup_indexes);
    614   }
    615   else
    616   {
    617     for (; *scripts; scripts++)
    618     {
    619       unsigned int script_index;
    620       if (hb_ot_layout_table_find_script (face,
    621 					  table_tag,
    622 					  *scripts,
    623 					  &script_index))
    624         _hb_ot_layout_collect_lookups_languages (face,
    625 						 table_tag,
    626 						 script_index,
    627 						 languages,
    628 						 features,
    629 						 lookup_indexes);
    630     }
    631   }
    632 }
    633 
    634 void
    635 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
    636 				    hb_tag_t      table_tag,
    637 				    unsigned int  lookup_index,
    638 				    hb_set_t     *glyphs_before, /* OUT. May be NULL */
    639 				    hb_set_t     *glyphs_input,  /* OUT. May be NULL */
    640 				    hb_set_t     *glyphs_after,  /* OUT. May be NULL */
    641 				    hb_set_t     *glyphs_output  /* OUT. May be NULL */)
    642 {
    643   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
    644 
    645   OT::hb_collect_glyphs_context_t c (face,
    646 				     glyphs_before,
    647 				     glyphs_input,
    648 				     glyphs_after,
    649 				     glyphs_output);
    650 
    651   switch (table_tag)
    652   {
    653     case HB_OT_TAG_GSUB:
    654     {
    655       const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
    656       l.collect_glyphs (&c);
    657       return;
    658     }
    659     case HB_OT_TAG_GPOS:
    660     {
    661       const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
    662       l.collect_glyphs (&c);
    663       return;
    664     }
    665   }
    666 }
    667 
    668 
    669 /*
    670  * OT::GSUB
    671  */
    672 
    673 hb_bool_t
    674 hb_ot_layout_has_substitution (hb_face_t *face)
    675 {
    676   return &_get_gsub (face) != &OT::Null(OT::GSUB);
    677 }
    678 
    679 hb_bool_t
    680 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
    681 				      unsigned int          lookup_index,
    682 				      const hb_codepoint_t *glyphs,
    683 				      unsigned int          glyphs_length,
    684 				      hb_bool_t             zero_context)
    685 {
    686   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
    687   return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
    688 }
    689 
    690 hb_bool_t
    691 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
    692 					   unsigned int          lookup_index,
    693 					   const hb_codepoint_t *glyphs,
    694 					   unsigned int          glyphs_length,
    695 					   hb_bool_t             zero_context)
    696 {
    697   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
    698   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
    699 
    700   const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
    701 
    702   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index].digest);
    703 }
    704 
    705 void
    706 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
    707 {
    708   OT::GSUB::substitute_start (font, buffer);
    709 }
    710 
    711 void
    712 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
    713 {
    714   OT::GSUB::substitute_finish (font, buffer);
    715 }
    716 
    717 void
    718 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
    719 				        unsigned int  lookup_index,
    720 				        hb_set_t     *glyphs)
    721 {
    722   OT::hb_closure_context_t c (face, glyphs);
    723 
    724   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
    725 
    726   l.closure (&c);
    727 }
    728 
    729 /*
    730  * OT::GPOS
    731  */
    732 
    733 hb_bool_t
    734 hb_ot_layout_has_positioning (hb_face_t *face)
    735 {
    736   return &_get_gpos (face) != &OT::Null(OT::GPOS);
    737 }
    738 
    739 void
    740 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
    741 {
    742   OT::GPOS::position_start (font, buffer);
    743 }
    744 
    745 void
    746 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
    747 {
    748   OT::GPOS::position_finish (font, buffer);
    749 }
    750 
    751 hb_bool_t
    752 hb_ot_layout_get_size_params (hb_face_t    *face,
    753 			      unsigned int *design_size,       /* OUT.  May be NULL */
    754 			      unsigned int *subfamily_id,      /* OUT.  May be NULL */
    755 			      unsigned int *subfamily_name_id, /* OUT.  May be NULL */
    756 			      unsigned int *range_start,       /* OUT.  May be NULL */
    757 			      unsigned int *range_end          /* OUT.  May be NULL */)
    758 {
    759   const OT::GPOS &gpos = _get_gpos (face);
    760   const hb_tag_t tag = HB_TAG ('s','i','z','e');
    761 
    762   unsigned int num_features = gpos.get_feature_count ();
    763   for (unsigned int i = 0; i < num_features; i++)
    764   {
    765     if (tag == gpos.get_feature_tag (i))
    766     {
    767       const OT::Feature &f = gpos.get_feature (i);
    768       const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
    769 
    770       if (params.designSize)
    771       {
    772 #define PARAM(a, A) if (a) *a = params.A
    773 	PARAM (design_size, designSize);
    774 	PARAM (subfamily_id, subfamilyID);
    775 	PARAM (subfamily_name_id, subfamilyNameID);
    776 	PARAM (range_start, rangeStart);
    777 	PARAM (range_end, rangeEnd);
    778 #undef PARAM
    779 
    780 	return true;
    781       }
    782     }
    783   }
    784 
    785 #define PARAM(a, A) if (a) *a = 0
    786   PARAM (design_size, designSize);
    787   PARAM (subfamily_id, subfamilyID);
    788   PARAM (subfamily_name_id, subfamilyNameID);
    789   PARAM (range_start, rangeStart);
    790   PARAM (range_end, rangeEnd);
    791 #undef PARAM
    792 
    793   return false;
    794 }
    795 
    796 
    797 /*
    798  * Parts of different types are implemented here such that they have direct
    799  * access to GSUB/GPOS lookups.
    800  */
    801 
    802 
    803 struct GSUBProxy
    804 {
    805   static const unsigned int table_index = 0;
    806   static const bool inplace = false;
    807   typedef OT::SubstLookup Lookup;
    808 
    809   GSUBProxy (hb_face_t *face) :
    810     table (*hb_ot_layout_from_face (face)->gsub),
    811     accels (hb_ot_layout_from_face (face)->gsub_accels) {}
    812 
    813   const OT::GSUB &table;
    814   const hb_ot_layout_lookup_accelerator_t *accels;
    815 };
    816 
    817 struct GPOSProxy
    818 {
    819   static const unsigned int table_index = 1;
    820   static const bool inplace = true;
    821   typedef OT::PosLookup Lookup;
    822 
    823   GPOSProxy (hb_face_t *face) :
    824     table (*hb_ot_layout_from_face (face)->gpos),
    825     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
    826 
    827   const OT::GPOS &table;
    828   const hb_ot_layout_lookup_accelerator_t *accels;
    829 };
    830 
    831 
    832 template <typename Lookup>
    833 static inline bool apply_once (OT::hb_apply_context_t *c,
    834 			       const Lookup &lookup)
    835 {
    836   if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
    837     return false;
    838   return lookup.dispatch (c);
    839 }
    840 
    841 template <typename Proxy>
    842 static inline bool
    843 apply_string (OT::hb_apply_context_t *c,
    844 	      const typename Proxy::Lookup &lookup,
    845 	      const hb_ot_layout_lookup_accelerator_t &accel)
    846 {
    847   bool ret = false;
    848   hb_buffer_t *buffer = c->buffer;
    849 
    850   if (unlikely (!buffer->len || !c->lookup_mask))
    851     return false;
    852 
    853   c->set_lookup (lookup);
    854 
    855   if (likely (!lookup.is_reverse ()))
    856   {
    857     /* in/out forward substitution/positioning */
    858     if (Proxy::table_index == 0)
    859       buffer->clear_output ();
    860     buffer->idx = 0;
    861 
    862     while (buffer->idx < buffer->len)
    863     {
    864       if (accel.digest.may_have (buffer->cur().codepoint) &&
    865 	  (buffer->cur().mask & c->lookup_mask) &&
    866 	  apply_once (c, lookup))
    867 	ret = true;
    868       else
    869 	buffer->next_glyph ();
    870     }
    871     if (ret)
    872     {
    873       if (!Proxy::inplace)
    874 	buffer->swap_buffers ();
    875       else
    876         assert (!buffer->has_separate_output ());
    877     }
    878   }
    879   else
    880   {
    881     /* in-place backward substitution/positioning */
    882     if (Proxy::table_index == 0)
    883       buffer->remove_output ();
    884     buffer->idx = buffer->len - 1;
    885     do
    886     {
    887       if (accel.digest.may_have (buffer->cur().codepoint) &&
    888 	  (buffer->cur().mask & c->lookup_mask) &&
    889 	  apply_once (c, lookup))
    890 	ret = true;
    891       /* The reverse lookup doesn't "advance" cursor (for good reason). */
    892       buffer->idx--;
    893 
    894     }
    895     while ((int) buffer->idx >= 0);
    896   }
    897 
    898   return ret;
    899 }
    900 
    901 template <typename Proxy>
    902 inline void hb_ot_map_t::apply (const Proxy &proxy,
    903 				const hb_ot_shape_plan_t *plan,
    904 				hb_font_t *font,
    905 				hb_buffer_t *buffer) const
    906 {
    907   const unsigned int table_index = proxy.table_index;
    908   unsigned int i = 0;
    909   OT::hb_apply_context_t c (table_index, font, buffer);
    910   c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
    911 
    912   for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
    913     const stage_map_t *stage = &stages[table_index][stage_index];
    914     for (; i < stage->last_lookup; i++)
    915     {
    916       unsigned int lookup_index = lookups[table_index][i].index;
    917       c.set_lookup_mask (lookups[table_index][i].mask);
    918       c.set_auto_zwj (lookups[table_index][i].auto_zwj);
    919       apply_string<Proxy> (&c,
    920 			   proxy.table.get_lookup (lookup_index),
    921 			   proxy.accels[lookup_index]);
    922     }
    923 
    924     if (stage->pause_func)
    925     {
    926       buffer->clear_output ();
    927       stage->pause_func (plan, font, buffer);
    928     }
    929   }
    930 }
    931 
    932 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
    933 {
    934   GSUBProxy proxy (font->face);
    935   apply (proxy, plan, font, buffer);
    936 }
    937 
    938 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
    939 {
    940   GPOSProxy proxy (font->face);
    941   apply (proxy, plan, font, buffer);
    942 }
    943 
    944 HB_INTERNAL void
    945 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
    946 				const OT::SubstLookup &lookup,
    947 				const hb_ot_layout_lookup_accelerator_t &accel)
    948 {
    949   apply_string<GSUBProxy> (c, lookup, accel);
    950 }
    951