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-open-type-private.hh"
  32 #include "hb-ot-layout-private.hh"
  33 
  34 #include "hb-ot-layout-gdef-table.hh"
  35 #include "hb-ot-layout-gsub-table.hh"
  36 #include "hb-ot-layout-gpos-table.hh"
  37 #include "hb-ot-layout-jstf-table.hh"
  38 
  39 #include "hb-ot-map-private.hh"
  40 
  41 #include <stdlib.h>
  42 #include <string.h>
  43 
  44 
  45 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
  46 
  47 hb_ot_layout_t *
  48 _hb_ot_layout_create (hb_face_t *face)
  49 {
  50   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
  51   if (unlikely (!layout))
  52     return NULL;
  53 
  54   layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
  55   layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
  56 
  57   layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
  58   layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
  59 
  60   layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
  61   layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
  62 
  63   {
  64     /*
  65      * The ugly business of blacklisting individual fonts' tables happen here!
  66      * See this thread for why we finally had to bend in and do this:
  67      * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
  68      */
  69     unsigned int gdef_len = hb_blob_get_length (layout->gdef_blob);
  70     unsigned int gsub_len = hb_blob_get_length (layout->gsub_blob);
  71     unsigned int gpos_len = hb_blob_get_length (layout->gpos_blob);
  72     if (0
  73       /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
  74       || (442 == gdef_len && 42038 == gpos_len && 2874 == gsub_len)
  75       /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
  76       || (430 == gdef_len && 40662 == gpos_len && 2874 == gsub_len)
  77       /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
  78       || (442 == gdef_len && 39116 == gpos_len && 2874 == gsub_len)
  79       /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
  80       || (430 == gdef_len && 39374 == gpos_len && 2874 == gsub_len)
  81       /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
  82       || (490 == gdef_len && 41638 == gpos_len && 3046 == gsub_len)
  83       /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
  84       || (478 == gdef_len && 41902 == gpos_len && 3046 == gsub_len)
  85     )
  86     {
  87       /* In certain versions of Times New Roman Italic and Bold Italic,
  88        * ASCII double quotation mark U+0022, mapped to glyph 5, has wrong
  89        * glyph class 3 (mark) in GDEF.  Nuke the GDEF to avoid zero-width
  90        * double-quote.  See:
  91        * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
  92        */
  93      if (3 == layout->gdef->get_glyph_class (5))
  94        layout->gdef = &OT::Null(OT::GDEF);
  95     }
  96     else if (0
  97       /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
  98       || (898 == gdef_len && 46470 == gpos_len && 12554 == gsub_len)
  99       /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
 100       || (910 == gdef_len && 47732 == gpos_len && 12566 == gsub_len)
 101       /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
 102       || (928 == gdef_len && 59332 == gpos_len && 23298 == gsub_len)
 103       /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
 104       || (940 == gdef_len && 60732 == gpos_len && 23310 == gsub_len)
 105       /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
 106       || (994 == gdef_len && 60336 == gpos_len && 24474 == gsub_len)
 107       /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
 108       || (1006 == gdef_len && 61740 == gpos_len && 24470 == gsub_len)
 109       /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
 110       || (832 == gdef_len && 47162 == gpos_len && 7324 == gsub_len)
 111       /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
 112       || (844 == gdef_len && 45474 == gpos_len && 7302 == gsub_len)
 113       /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
 114       || (192 == gdef_len && 7254 == gpos_len && 12638 == gsub_len)
 115       /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
 116       || (192 == gdef_len && 7254 == gpos_len && 12690 == gsub_len)
 117       /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
 118       /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
 119       || (188 == gdef_len && 3852 == gpos_len && 248 == gsub_len)
 120       /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
 121       /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
 122       || (188 == gdef_len && 3426 == gpos_len && 264 == gsub_len)
 123     )
 124     {
 125       /* Many versions of Tahoma have bad GDEF tables that incorrectly classify some spacing marks
 126        * such as certain IPA symbols as glyph class 3. So do older versions of Microsoft Himalaya,
 127        * and the version of Cantarell shipped by Ubuntu 16.04.
 128        * Nuke the GDEF tables of these fonts to avoid unwanted width-zeroing.
 129        * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
 130        *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
 131        *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
 132        */
 133       layout->gdef = &OT::Null(OT::GDEF);
 134     }
 135   }
 136 
 137   layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
 138   layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
 139 
 140   layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
 141   layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
 142 
 143   if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) ||
 144                 (layout->gpos_lookup_count && !layout->gpos_accels)))
 145   {
 146     _hb_ot_layout_destroy (layout);
 147     return NULL;
 148   }
 149 
 150   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
 151     layout->gsub_accels[i].init (layout->gsub->get_lookup (i));
 152   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
 153     layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
 154 
 155   return layout;
 156 }
 157 
 158 void
 159 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
 160 {
 161   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
 162     layout->gsub_accels[i].fini ();
 163   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
 164     layout->gpos_accels[i].fini ();
 165 
 166   free (layout->gsub_accels);
 167   free (layout->gpos_accels);
 168 
 169   hb_blob_destroy (layout->gdef_blob);
 170   hb_blob_destroy (layout->gsub_blob);
 171   hb_blob_destroy (layout->gpos_blob);
 172 
 173   free (layout);
 174 }
 175 
 176 static inline const OT::GDEF&
 177 _get_gdef (hb_face_t *face)
 178 {
 179   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
 180   return *hb_ot_layout_from_face (face)->gdef;
 181 }
 182 static inline const OT::GSUB&
 183 _get_gsub (hb_face_t *face)
 184 {
 185   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
 186   return *hb_ot_layout_from_face (face)->gsub;
 187 }
 188 static inline const OT::GPOS&
 189 _get_gpos (hb_face_t *face)
 190 {
 191   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
 192   return *hb_ot_layout_from_face (face)->gpos;
 193 }
 194 
 195 
 196 /*
 197  * GDEF
 198  */
 199 
 200 hb_bool_t
 201 hb_ot_layout_has_glyph_classes (hb_face_t *face)
 202 {
 203   return _get_gdef (face).has_glyph_classes ();
 204 }
 205 
 206 /**
 207  * hb_ot_layout_get_glyph_class:
 208  *
 209  * Since: 0.9.7
 210  **/
 211 hb_ot_layout_glyph_class_t
 212 hb_ot_layout_get_glyph_class (hb_face_t      *face,
 213                               hb_codepoint_t  glyph)
 214 {
 215   return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
 216 }
 217 
 218 /**
 219  * hb_ot_layout_get_glyphs_in_class:
 220  *
 221  * Since: 0.9.7
 222  **/
 223 void
 224 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
 225                                   hb_ot_layout_glyph_class_t  klass,
 226                                   hb_set_t                   *glyphs /* OUT */)
 227 {
 228   return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
 229 }
 230 
 231 unsigned int
 232 hb_ot_layout_get_attach_points (hb_face_t      *face,
 233                                 hb_codepoint_t  glyph,
 234                                 unsigned int    start_offset,
 235                                 unsigned int   *point_count /* IN/OUT */,
 236                                 unsigned int   *point_array /* OUT */)
 237 {
 238   return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
 239 }
 240 
 241 unsigned int
 242 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
 243                                   hb_direction_t  direction,
 244                                   hb_codepoint_t  glyph,
 245                                   unsigned int    start_offset,
 246                                   unsigned int   *caret_count /* IN/OUT */,
 247                                   int            *caret_array /* OUT */)
 248 {
 249   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
 250 }
 251 
 252 
 253 /*
 254  * GSUB/GPOS
 255  */
 256 
 257 static const OT::GSUBGPOS&
 258 get_gsubgpos_table (hb_face_t *face,
 259                     hb_tag_t   table_tag)
 260 {
 261   switch (table_tag) {
 262     case HB_OT_TAG_GSUB: return _get_gsub (face);
 263     case HB_OT_TAG_GPOS: return _get_gpos (face);
 264     default:             return OT::Null(OT::GSUBGPOS);
 265   }
 266 }
 267 
 268 
 269 unsigned int
 270 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
 271                                     hb_tag_t      table_tag,
 272                                     unsigned int  start_offset,
 273                                     unsigned int *script_count /* IN/OUT */,
 274                                     hb_tag_t     *script_tags /* OUT */)
 275 {
 276   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 277 
 278   return g.get_script_tags (start_offset, script_count, script_tags);
 279 }
 280 
 281 #define HB_OT_TAG_LATIN_SCRIPT          HB_TAG ('l', 'a', 't', 'n')
 282 
 283 hb_bool_t
 284 hb_ot_layout_table_find_script (hb_face_t    *face,
 285                                 hb_tag_t      table_tag,
 286                                 hb_tag_t      script_tag,
 287                                 unsigned int *script_index)
 288 {
 289   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
 290   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 291 
 292   if (g.find_script_index (script_tag, script_index))
 293     return true;
 294 
 295   /* try finding 'DFLT' */
 296   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
 297     return false;
 298 
 299   /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
 300    * including many versions of DejaVu Sans Mono! */
 301   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
 302     return false;
 303 
 304   /* try with 'latn'; some old fonts put their features there even though
 305      they're really trying to support Thai, for example :( */
 306   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
 307     return false;
 308 
 309   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
 310   return false;
 311 }
 312 
 313 hb_bool_t
 314 hb_ot_layout_table_choose_script (hb_face_t      *face,
 315                                   hb_tag_t        table_tag,
 316                                   const hb_tag_t *script_tags,
 317                                   unsigned int   *script_index,
 318                                   hb_tag_t       *chosen_script)
 319 {
 320   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
 321   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 322 
 323   while (*script_tags)
 324   {
 325     if (g.find_script_index (*script_tags, script_index)) {
 326       if (chosen_script)
 327         *chosen_script = *script_tags;
 328       return true;
 329     }
 330     script_tags++;
 331   }
 332 
 333   /* try finding 'DFLT' */
 334   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
 335     if (chosen_script)
 336       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
 337     return false;
 338   }
 339 
 340   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
 341   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
 342     if (chosen_script)
 343       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
 344     return false;
 345   }
 346 
 347   /* try with 'latn'; some old fonts put their features there even though
 348      they're really trying to support Thai, for example :( */
 349   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
 350     if (chosen_script)
 351       *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
 352     return false;
 353   }
 354 
 355   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
 356   if (chosen_script)
 357     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
 358   return false;
 359 }
 360 
 361 unsigned int
 362 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
 363                                      hb_tag_t      table_tag,
 364                                      unsigned int  start_offset,
 365                                      unsigned int *feature_count /* IN/OUT */,
 366                                      hb_tag_t     *feature_tags /* OUT */)
 367 {
 368   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 369 
 370   return g.get_feature_tags (start_offset, feature_count, feature_tags);
 371 }
 372 
 373 hb_bool_t
 374 hb_ot_layout_table_find_feature (hb_face_t    *face,
 375                                  hb_tag_t      table_tag,
 376                                  hb_tag_t      feature_tag,
 377                                  unsigned int *feature_index)
 378 {
 379   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
 380   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 381 
 382   unsigned int num_features = g.get_feature_count ();
 383   for (unsigned int i = 0; i < num_features; i++)
 384   {
 385     if (feature_tag == g.get_feature_tag (i)) {
 386       if (feature_index) *feature_index = i;
 387       return true;
 388     }
 389   }
 390 
 391   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
 392   return false;
 393 }
 394 
 395 
 396 unsigned int
 397 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
 398                                        hb_tag_t      table_tag,
 399                                        unsigned int  script_index,
 400                                        unsigned int  start_offset,
 401                                        unsigned int *language_count /* IN/OUT */,
 402                                        hb_tag_t     *language_tags /* OUT */)
 403 {
 404   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 405 
 406   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
 407 }
 408 
 409 hb_bool_t
 410 hb_ot_layout_script_find_language (hb_face_t    *face,
 411                                    hb_tag_t      table_tag,
 412                                    unsigned int  script_index,
 413                                    hb_tag_t      language_tag,
 414                                    unsigned int *language_index)
 415 {
 416   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
 417   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 418 
 419   if (s.find_lang_sys_index (language_tag, language_index))
 420     return true;
 421 
 422   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
 423   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
 424     return false;
 425 
 426   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
 427   return false;
 428 }
 429 
 430 hb_bool_t
 431 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
 432                                                   hb_tag_t      table_tag,
 433                                                   unsigned int  script_index,
 434                                                   unsigned int  language_index,
 435                                                   unsigned int *feature_index)
 436 {
 437   return hb_ot_layout_language_get_required_feature (face,
 438                                                      table_tag,
 439                                                      script_index,
 440                                                      language_index,
 441                                                      feature_index,
 442                                                      NULL);
 443 }
 444 
 445 /**
 446  * hb_ot_layout_language_get_required_feature:
 447  *
 448  * Since: 0.9.30
 449  **/
 450 hb_bool_t
 451 hb_ot_layout_language_get_required_feature (hb_face_t    *face,
 452                                             hb_tag_t      table_tag,
 453                                             unsigned int  script_index,
 454                                             unsigned int  language_index,
 455                                             unsigned int *feature_index,
 456                                             hb_tag_t     *feature_tag)
 457 {
 458   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 459   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 460 
 461   unsigned int index = l.get_required_feature_index ();
 462   if (feature_index) *feature_index = index;
 463   if (feature_tag) *feature_tag = g.get_feature_tag (index);
 464 
 465   return l.has_required_feature ();
 466 }
 467 
 468 unsigned int
 469 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
 470                                            hb_tag_t      table_tag,
 471                                            unsigned int  script_index,
 472                                            unsigned int  language_index,
 473                                            unsigned int  start_offset,
 474                                            unsigned int *feature_count /* IN/OUT */,
 475                                            unsigned int *feature_indexes /* OUT */)
 476 {
 477   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 478   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 479 
 480   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
 481 }
 482 
 483 unsigned int
 484 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
 485                                         hb_tag_t      table_tag,
 486                                         unsigned int  script_index,
 487                                         unsigned int  language_index,
 488                                         unsigned int  start_offset,
 489                                         unsigned int *feature_count /* IN/OUT */,
 490                                         hb_tag_t     *feature_tags /* OUT */)
 491 {
 492   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 493   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 494 
 495   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
 496   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
 497 
 498   if (feature_tags) {
 499     unsigned int count = *feature_count;
 500     for (unsigned int i = 0; i < count; i++)
 501       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
 502   }
 503 
 504   return ret;
 505 }
 506 
 507 
 508 hb_bool_t
 509 hb_ot_layout_language_find_feature (hb_face_t    *face,
 510                                     hb_tag_t      table_tag,
 511                                     unsigned int  script_index,
 512                                     unsigned int  language_index,
 513                                     hb_tag_t      feature_tag,
 514                                     unsigned int *feature_index)
 515 {
 516   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
 517   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 518   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 519 
 520   unsigned int num_features = l.get_feature_count ();
 521   for (unsigned int i = 0; i < num_features; i++) {
 522     unsigned int f_index = l.get_feature_index (i);
 523 
 524     if (feature_tag == g.get_feature_tag (f_index)) {
 525       if (feature_index) *feature_index = f_index;
 526       return true;
 527     }
 528   }
 529 
 530   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
 531   return false;
 532 }
 533 
 534 /**
 535  * hb_ot_layout_feature_get_lookups:
 536  *
 537  * Since: 0.9.7
 538  **/
 539 unsigned int
 540 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
 541                                   hb_tag_t      table_tag,
 542                                   unsigned int  feature_index,
 543                                   unsigned int  start_offset,
 544                                   unsigned int *lookup_count /* IN/OUT */,
 545                                   unsigned int *lookup_indexes /* OUT */)
 546 {
 547   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 548   const OT::Feature &f = g.get_feature (feature_index);
 549 
 550   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
 551 }
 552 
 553 /**
 554  * hb_ot_layout_table_get_lookup_count:
 555  *
 556  * Since: 0.9.22
 557  **/
 558 unsigned int
 559 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 560                                      hb_tag_t      table_tag)
 561 {
 562   switch (table_tag)
 563   {
 564     case HB_OT_TAG_GSUB:
 565     {
 566       return hb_ot_layout_from_face (face)->gsub_lookup_count;
 567     }
 568     case HB_OT_TAG_GPOS:
 569     {
 570       return hb_ot_layout_from_face (face)->gpos_lookup_count;
 571     }
 572   }
 573   return 0;
 574 }
 575 
 576 static void
 577 _hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
 578                                        hb_tag_t        table_tag,
 579                                        unsigned int    feature_index,
 580                                        hb_set_t       *lookup_indexes /* OUT */)
 581 {
 582   unsigned int lookup_indices[32];
 583   unsigned int offset, len;
 584 
 585   offset = 0;
 586   do {
 587     len = ARRAY_LENGTH (lookup_indices);
 588     hb_ot_layout_feature_get_lookups (face,
 589                                       table_tag,
 590                                       feature_index,
 591                                       offset, &len,
 592                                       lookup_indices);
 593 
 594     for (unsigned int i = 0; i < len; i++)
 595       lookup_indexes->add (lookup_indices[i]);
 596 
 597     offset += len;
 598   } while (len == ARRAY_LENGTH (lookup_indices));
 599 }
 600 
 601 static void
 602 _hb_ot_layout_collect_lookups_features (hb_face_t      *face,
 603                                         hb_tag_t        table_tag,
 604                                         unsigned int    script_index,
 605                                         unsigned int    language_index,
 606                                         const hb_tag_t *features,
 607                                         hb_set_t       *lookup_indexes /* OUT */)
 608 {
 609   if (!features)
 610   {
 611     unsigned int required_feature_index;
 612     if (hb_ot_layout_language_get_required_feature (face,
 613                                                     table_tag,
 614                                                     script_index,
 615                                                     language_index,
 616                                                     &required_feature_index,
 617                                                     NULL))
 618       _hb_ot_layout_collect_lookups_lookups (face,
 619                                              table_tag,
 620                                              required_feature_index,
 621                                              lookup_indexes);
 622 
 623     /* All features */
 624     unsigned int feature_indices[32];
 625     unsigned int offset, len;
 626 
 627     offset = 0;
 628     do {
 629       len = ARRAY_LENGTH (feature_indices);
 630       hb_ot_layout_language_get_feature_indexes (face,
 631                                                  table_tag,
 632                                                  script_index,
 633                                                  language_index,
 634                                                  offset, &len,
 635                                                  feature_indices);
 636 
 637       for (unsigned int i = 0; i < len; i++)
 638         _hb_ot_layout_collect_lookups_lookups (face,
 639                                                table_tag,
 640                                                feature_indices[i],
 641                                                lookup_indexes);
 642 
 643       offset += len;
 644     } while (len == ARRAY_LENGTH (feature_indices));
 645   }
 646   else
 647   {
 648     for (; *features; features++)
 649     {
 650       unsigned int feature_index;
 651       if (hb_ot_layout_language_find_feature (face,
 652                                               table_tag,
 653                                               script_index,
 654                                               language_index,
 655                                               *features,
 656                                               &feature_index))
 657         _hb_ot_layout_collect_lookups_lookups (face,
 658                                                table_tag,
 659                                                feature_index,
 660                                                lookup_indexes);
 661     }
 662   }
 663 }
 664 
 665 static void
 666 _hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
 667                                          hb_tag_t        table_tag,
 668                                          unsigned int    script_index,
 669                                          const hb_tag_t *languages,
 670                                          const hb_tag_t *features,
 671                                          hb_set_t       *lookup_indexes /* OUT */)
 672 {
 673   _hb_ot_layout_collect_lookups_features (face,
 674                                           table_tag,
 675                                           script_index,
 676                                           HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
 677                                           features,
 678                                           lookup_indexes);
 679 
 680   if (!languages)
 681   {
 682     /* All languages */
 683     unsigned int count = hb_ot_layout_script_get_language_tags (face,
 684                                                                 table_tag,
 685                                                                 script_index,
 686                                                                 0, NULL, NULL);
 687     for (unsigned int language_index = 0; language_index < count; language_index++)
 688       _hb_ot_layout_collect_lookups_features (face,
 689                                               table_tag,
 690                                               script_index,
 691                                               language_index,
 692                                               features,
 693                                               lookup_indexes);
 694   }
 695   else
 696   {
 697     for (; *languages; languages++)
 698     {
 699       unsigned int language_index;
 700       if (hb_ot_layout_script_find_language (face,
 701                                              table_tag,
 702                                              script_index,
 703                                              *languages,
 704                                              &language_index))
 705         _hb_ot_layout_collect_lookups_features (face,
 706                                                 table_tag,
 707                                                 script_index,
 708                                                 language_index,
 709                                                 features,
 710                                                 lookup_indexes);
 711     }
 712   }
 713 }
 714 
 715 /**
 716  * hb_ot_layout_collect_lookups:
 717  *
 718  * Since: 0.9.8
 719  **/
 720 void
 721 hb_ot_layout_collect_lookups (hb_face_t      *face,
 722                               hb_tag_t        table_tag,
 723                               const hb_tag_t *scripts,
 724                               const hb_tag_t *languages,
 725                               const hb_tag_t *features,
 726                               hb_set_t       *lookup_indexes /* OUT */)
 727 {
 728   if (!scripts)
 729   {
 730     /* All scripts */
 731     unsigned int count = hb_ot_layout_table_get_script_tags (face,
 732                                                              table_tag,
 733                                                              0, NULL, NULL);
 734     for (unsigned int script_index = 0; script_index < count; script_index++)
 735       _hb_ot_layout_collect_lookups_languages (face,
 736                                                table_tag,
 737                                                script_index,
 738                                                languages,
 739                                                features,
 740                                                lookup_indexes);
 741   }
 742   else
 743   {
 744     for (; *scripts; scripts++)
 745     {
 746       unsigned int script_index;
 747       if (hb_ot_layout_table_find_script (face,
 748                                           table_tag,
 749                                           *scripts,
 750                                           &script_index))
 751         _hb_ot_layout_collect_lookups_languages (face,
 752                                                  table_tag,
 753                                                  script_index,
 754                                                  languages,
 755                                                  features,
 756                                                  lookup_indexes);
 757     }
 758   }
 759 }
 760 
 761 /**
 762  * hb_ot_layout_lookup_collect_glyphs:
 763  *
 764  * Since: 0.9.7
 765  **/
 766 void
 767 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
 768                                     hb_tag_t      table_tag,
 769                                     unsigned int  lookup_index,
 770                                     hb_set_t     *glyphs_before, /* OUT. May be NULL */
 771                                     hb_set_t     *glyphs_input,  /* OUT. May be NULL */
 772                                     hb_set_t     *glyphs_after,  /* OUT. May be NULL */
 773                                     hb_set_t     *glyphs_output  /* OUT. May be NULL */)
 774 {
 775   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
 776 
 777   OT::hb_collect_glyphs_context_t c (face,
 778                                      glyphs_before,
 779                                      glyphs_input,
 780                                      glyphs_after,
 781                                      glyphs_output);
 782 
 783   switch (table_tag)
 784   {
 785     case HB_OT_TAG_GSUB:
 786     {
 787       const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
 788       l.collect_glyphs (&c);
 789       return;
 790     }
 791     case HB_OT_TAG_GPOS:
 792     {
 793       const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
 794       l.collect_glyphs (&c);
 795       return;
 796     }
 797   }
 798 }
 799 
 800 
 801 /*
 802  * OT::GSUB
 803  */
 804 
 805 hb_bool_t
 806 hb_ot_layout_has_substitution (hb_face_t *face)
 807 {
 808   return &_get_gsub (face) != &OT::Null(OT::GSUB);
 809 }
 810 
 811 /**
 812  * hb_ot_layout_lookup_would_substitute:
 813  *
 814  * Since: 0.9.7
 815  **/
 816 hb_bool_t
 817 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
 818                                       unsigned int          lookup_index,
 819                                       const hb_codepoint_t *glyphs,
 820                                       unsigned int          glyphs_length,
 821                                       hb_bool_t             zero_context)
 822 {
 823   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
 824   return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
 825 }
 826 
 827 hb_bool_t
 828 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 829                                            unsigned int          lookup_index,
 830                                            const hb_codepoint_t *glyphs,
 831                                            unsigned int          glyphs_length,
 832                                            hb_bool_t             zero_context)
 833 {
 834   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
 835   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
 836 
 837   const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
 838 
 839   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
 840 }
 841 
 842 void
 843 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 844 {
 845   OT::GSUB::substitute_start (font, buffer);
 846 }
 847 
 848 /**
 849  * hb_ot_layout_lookup_substitute_closure:
 850  *
 851  * Since: 0.9.7
 852  **/
 853 void
 854 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 855                                         unsigned int  lookup_index,
 856                                         hb_set_t     *glyphs)
 857 {
 858   OT::hb_closure_context_t c (face, glyphs);
 859 
 860   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
 861 
 862   l.closure (&c);
 863 }
 864 
 865 /*
 866  * OT::GPOS
 867  */
 868 
 869 hb_bool_t
 870 hb_ot_layout_has_positioning (hb_face_t *face)
 871 {
 872   return &_get_gpos (face) != &OT::Null(OT::GPOS);
 873 }
 874 
 875 void
 876 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 877 {
 878   OT::GPOS::position_start (font, buffer);
 879 }
 880 
 881 void
 882 hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
 883 {
 884   OT::GPOS::position_finish_advances (font, buffer);
 885 }
 886 
 887 void
 888 hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
 889 {
 890   OT::GPOS::position_finish_offsets (font, buffer);
 891 }
 892 
 893 /**
 894  * hb_ot_layout_get_size_params:
 895  *
 896  * Since: 0.9.10
 897  **/
 898 hb_bool_t
 899 hb_ot_layout_get_size_params (hb_face_t    *face,
 900                               unsigned int *design_size,       /* OUT.  May be NULL */
 901                               unsigned int *subfamily_id,      /* OUT.  May be NULL */
 902                               unsigned int *subfamily_name_id, /* OUT.  May be NULL */
 903                               unsigned int *range_start,       /* OUT.  May be NULL */
 904                               unsigned int *range_end          /* OUT.  May be NULL */)
 905 {
 906   const OT::GPOS &gpos = _get_gpos (face);
 907   const hb_tag_t tag = HB_TAG ('s','i','z','e');
 908 
 909   unsigned int num_features = gpos.get_feature_count ();
 910   for (unsigned int i = 0; i < num_features; i++)
 911   {
 912     if (tag == gpos.get_feature_tag (i))
 913     {
 914       const OT::Feature &f = gpos.get_feature (i);
 915       const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
 916 
 917       if (params.designSize)
 918       {
 919 #define PARAM(a, A) if (a) *a = params.A
 920         PARAM (design_size, designSize);
 921         PARAM (subfamily_id, subfamilyID);
 922         PARAM (subfamily_name_id, subfamilyNameID);
 923         PARAM (range_start, rangeStart);
 924         PARAM (range_end, rangeEnd);
 925 #undef PARAM
 926 
 927         return true;
 928       }
 929     }
 930   }
 931 
 932 #define PARAM(a, A) if (a) *a = 0
 933   PARAM (design_size, designSize);
 934   PARAM (subfamily_id, subfamilyID);
 935   PARAM (subfamily_name_id, subfamilyNameID);
 936   PARAM (range_start, rangeStart);
 937   PARAM (range_end, rangeEnd);
 938 #undef PARAM
 939 
 940   return false;
 941 }
 942 
 943 
 944 /*
 945  * Parts of different types are implemented here such that they have direct
 946  * access to GSUB/GPOS lookups.
 947  */
 948 
 949 
 950 struct GSUBProxy
 951 {
 952   static const unsigned int table_index = 0;
 953   static const bool inplace = false;
 954   typedef OT::SubstLookup Lookup;
 955 
 956   GSUBProxy (hb_face_t *face) :
 957     table (*hb_ot_layout_from_face (face)->gsub),
 958     accels (hb_ot_layout_from_face (face)->gsub_accels) {}
 959 
 960   const OT::GSUB &table;
 961   const hb_ot_layout_lookup_accelerator_t *accels;
 962 };
 963 
 964 struct GPOSProxy
 965 {
 966   static const unsigned int table_index = 1;
 967   static const bool inplace = true;
 968   typedef OT::PosLookup Lookup;
 969 
 970   GPOSProxy (hb_face_t *face) :
 971     table (*hb_ot_layout_from_face (face)->gpos),
 972     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
 973 
 974   const OT::GPOS &table;
 975   const hb_ot_layout_lookup_accelerator_t *accels;
 976 };
 977 
 978 
 979 struct hb_get_subtables_context_t :
 980        OT::hb_dispatch_context_t<hb_get_subtables_context_t, hb_void_t, HB_DEBUG_APPLY>
 981 {
 982   template <typename Type>
 983   static inline bool apply_to (const void *obj, OT::hb_apply_context_t *c)
 984   {
 985     const Type *typed_obj = (const Type *) obj;
 986     return typed_obj->apply (c);
 987   }
 988 
 989   typedef bool (*hb_apply_func_t) (const void *obj, OT::hb_apply_context_t *c);
 990 
 991   struct hb_applicable_t
 992   {
 993     inline void init (const void *obj_, hb_apply_func_t apply_func_)
 994     {
 995       obj = obj_;
 996       apply_func = apply_func_;
 997     }
 998 
 999     inline bool apply (OT::hb_apply_context_t *c) const { return apply_func (obj, c); }
1000 
1001     private:
1002     const void *obj;
1003     hb_apply_func_t apply_func;
1004   };
1005 
1006   typedef hb_auto_array_t<hb_applicable_t> array_t;
1007 
1008   /* Dispatch interface. */
1009   inline const char *get_name (void) { return "GET_SUBTABLES"; }
1010   template <typename T>
1011   inline return_t dispatch (const T &obj)
1012   {
1013     hb_applicable_t *entry = array.push();
1014     if (likely (entry))
1015       entry->init (&obj, apply_to<T>);
1016     return HB_VOID;
1017   }
1018   static return_t default_return_value (void) { return HB_VOID; }
1019   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
1020 
1021   hb_get_subtables_context_t (array_t &array_) :
1022                               array (array_),
1023                               debug_depth (0) {}
1024 
1025   array_t &array;
1026   unsigned int debug_depth;
1027 };
1028 
1029 static inline bool
1030 apply_forward (OT::hb_apply_context_t *c,
1031                const hb_ot_layout_lookup_accelerator_t &accel,
1032                const hb_get_subtables_context_t::array_t &subtables)
1033 {
1034   bool ret = false;
1035   hb_buffer_t *buffer = c->buffer;
1036   while (buffer->idx < buffer->len && !buffer->in_error)
1037   {
1038     bool applied = false;
1039     if (accel.may_have (buffer->cur().codepoint) &&
1040         (buffer->cur().mask & c->lookup_mask) &&
1041         c->check_glyph_property (&buffer->cur(), c->lookup_props))
1042      {
1043        for (unsigned int i = 0; i < subtables.len; i++)
1044          if (subtables[i].apply (c))
1045          {
1046            applied = true;
1047            break;
1048          }
1049      }
1050 
1051     if (applied)
1052       ret = true;
1053     else
1054       buffer->next_glyph ();
1055   }
1056   return ret;
1057 }
1058 
1059 static inline bool
1060 apply_backward (OT::hb_apply_context_t *c,
1061                const hb_ot_layout_lookup_accelerator_t &accel,
1062                const hb_get_subtables_context_t::array_t &subtables)
1063 {
1064   bool ret = false;
1065   hb_buffer_t *buffer = c->buffer;
1066   do
1067   {
1068     if (accel.may_have (buffer->cur().codepoint) &&
1069         (buffer->cur().mask & c->lookup_mask) &&
1070         c->check_glyph_property (&buffer->cur(), c->lookup_props))
1071     {
1072      for (unsigned int i = 0; i < subtables.len; i++)
1073        if (subtables[i].apply (c))
1074        {
1075          ret = true;
1076          break;
1077        }
1078     }
1079     /* The reverse lookup doesn't "advance" cursor (for good reason). */
1080     buffer->idx--;
1081 
1082   }
1083   while ((int) buffer->idx >= 0);
1084   return ret;
1085 }
1086 
1087 template <typename Proxy>
1088 static inline void
1089 apply_string (OT::hb_apply_context_t *c,
1090               const typename Proxy::Lookup &lookup,
1091               const hb_ot_layout_lookup_accelerator_t &accel)
1092 {
1093   hb_buffer_t *buffer = c->buffer;
1094 
1095   if (unlikely (!buffer->len || !c->lookup_mask))
1096     return;
1097 
1098   c->set_lookup_props (lookup.get_props ());
1099 
1100   hb_get_subtables_context_t::array_t subtables;
1101   hb_get_subtables_context_t c_get_subtables (subtables);
1102   lookup.dispatch (&c_get_subtables);
1103 
1104   if (likely (!lookup.is_reverse ()))
1105   {
1106     /* in/out forward substitution/positioning */
1107     if (Proxy::table_index == 0)
1108       buffer->clear_output ();
1109     buffer->idx = 0;
1110 
1111     bool ret;
1112     ret = apply_forward (c, accel, subtables);
1113     if (ret)
1114     {
1115       if (!Proxy::inplace)
1116         buffer->swap_buffers ();
1117       else
1118         assert (!buffer->has_separate_output ());
1119     }
1120   }
1121   else
1122   {
1123     /* in-place backward substitution/positioning */
1124     if (Proxy::table_index == 0)
1125       buffer->remove_output ();
1126     buffer->idx = buffer->len - 1;
1127 
1128     apply_backward (c, accel, subtables);
1129   }
1130 }
1131 
1132 template <typename Proxy>
1133 inline void hb_ot_map_t::apply (const Proxy &proxy,
1134                                 const hb_ot_shape_plan_t *plan,
1135                                 hb_font_t *font,
1136                                 hb_buffer_t *buffer) const
1137 {
1138   const unsigned int table_index = proxy.table_index;
1139   unsigned int i = 0;
1140   OT::hb_apply_context_t c (table_index, font, buffer);
1141   c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
1142 
1143   for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
1144     const stage_map_t *stage = &stages[table_index][stage_index];
1145     for (; i < stage->last_lookup; i++)
1146     {
1147       unsigned int lookup_index = lookups[table_index][i].index;
1148       if (!buffer->message (font, "start lookup %d", lookup_index)) continue;
1149       c.set_lookup_index (lookup_index);
1150       c.set_lookup_mask (lookups[table_index][i].mask);
1151       c.set_auto_zwj (lookups[table_index][i].auto_zwj);
1152       apply_string<Proxy> (&c,
1153                            proxy.table.get_lookup (lookup_index),
1154                            proxy.accels[lookup_index]);
1155       (void) buffer->message (font, "end lookup %d", lookup_index);
1156     }
1157 
1158     if (stage->pause_func)
1159     {
1160       buffer->clear_output ();
1161       stage->pause_func (plan, font, buffer);
1162     }
1163   }
1164 }
1165 
1166 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1167 {
1168   GSUBProxy proxy (font->face);
1169   apply (proxy, plan, font, buffer);
1170 }
1171 
1172 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1173 {
1174   GPOSProxy proxy (font->face);
1175   apply (proxy, plan, font, buffer);
1176 }
1177 
1178 HB_INTERNAL void
1179 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
1180                                 const OT::SubstLookup &lookup,
1181                                 const hb_ot_layout_lookup_accelerator_t &accel)
1182 {
1183   apply_string<GSUBProxy> (c, lookup, accel);
1184 }