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   layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
  64   layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
  65 
  66   layout->gsub_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
  67   layout->gpos_accels = (hb_ot_layout_lookup_accelerator_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_ot_layout_lookup_accelerator_t));
  68 
  69   if (unlikely ((layout->gsub_lookup_count && !layout->gsub_accels) ||
  70                 (layout->gpos_lookup_count && !layout->gpos_accels)))
  71   {
  72     _hb_ot_layout_destroy (layout);
  73     return NULL;
  74   }
  75 
  76   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
  77     layout->gsub_accels[i].init (layout->gsub->get_lookup (i));
  78   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
  79     layout->gpos_accels[i].init (layout->gpos->get_lookup (i));
  80 
  81   return layout;
  82 }
  83 
  84 void
  85 _hb_ot_layout_destroy (hb_ot_layout_t *layout)
  86 {
  87   for (unsigned int i = 0; i < layout->gsub_lookup_count; i++)
  88     layout->gsub_accels[i].fini ();
  89   for (unsigned int i = 0; i < layout->gpos_lookup_count; i++)
  90     layout->gpos_accels[i].fini ();
  91 
  92   free (layout->gsub_accels);
  93   free (layout->gpos_accels);
  94 
  95   hb_blob_destroy (layout->gdef_blob);
  96   hb_blob_destroy (layout->gsub_blob);
  97   hb_blob_destroy (layout->gpos_blob);
  98 
  99   free (layout);
 100 }
 101 
 102 static inline const OT::GDEF&
 103 _get_gdef (hb_face_t *face)
 104 {
 105   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
 106   return *hb_ot_layout_from_face (face)->gdef;
 107 }
 108 static inline const OT::GSUB&
 109 _get_gsub (hb_face_t *face)
 110 {
 111   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
 112   return *hb_ot_layout_from_face (face)->gsub;
 113 }
 114 static inline const OT::GPOS&
 115 _get_gpos (hb_face_t *face)
 116 {
 117   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
 118   return *hb_ot_layout_from_face (face)->gpos;
 119 }
 120 
 121 
 122 /*
 123  * GDEF
 124  */
 125 
 126 hb_bool_t
 127 hb_ot_layout_has_glyph_classes (hb_face_t *face)
 128 {
 129   return _get_gdef (face).has_glyph_classes ();
 130 }
 131 
 132 /**
 133  * Since: 0.9.7
 134  **/
 135 hb_ot_layout_glyph_class_t
 136 hb_ot_layout_get_glyph_class (hb_face_t      *face,
 137                               hb_codepoint_t  glyph)
 138 {
 139   return (hb_ot_layout_glyph_class_t) _get_gdef (face).get_glyph_class (glyph);
 140 }
 141 
 142 /**
 143  * Since: 0.9.7
 144  **/
 145 void
 146 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
 147                                   hb_ot_layout_glyph_class_t  klass,
 148                                   hb_set_t                   *glyphs /* OUT */)
 149 {
 150   return _get_gdef (face).get_glyphs_in_class (klass, glyphs);
 151 }
 152 
 153 unsigned int
 154 hb_ot_layout_get_attach_points (hb_face_t      *face,
 155                                 hb_codepoint_t  glyph,
 156                                 unsigned int    start_offset,
 157                                 unsigned int   *point_count /* IN/OUT */,
 158                                 unsigned int   *point_array /* OUT */)
 159 {
 160   return _get_gdef (face).get_attach_points (glyph, start_offset, point_count, point_array);
 161 }
 162 
 163 unsigned int
 164 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
 165                                   hb_direction_t  direction,
 166                                   hb_codepoint_t  glyph,
 167                                   unsigned int    start_offset,
 168                                   unsigned int   *caret_count /* IN/OUT */,
 169                                   int            *caret_array /* OUT */)
 170 {
 171   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
 172 }
 173 
 174 
 175 /*
 176  * GSUB/GPOS
 177  */
 178 
 179 static const OT::GSUBGPOS&
 180 get_gsubgpos_table (hb_face_t *face,
 181                     hb_tag_t   table_tag)
 182 {
 183   switch (table_tag) {
 184     case HB_OT_TAG_GSUB: return _get_gsub (face);
 185     case HB_OT_TAG_GPOS: return _get_gpos (face);
 186     default:             return OT::Null(OT::GSUBGPOS);
 187   }
 188 }
 189 
 190 
 191 unsigned int
 192 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
 193                                     hb_tag_t      table_tag,
 194                                     unsigned int  start_offset,
 195                                     unsigned int *script_count /* IN/OUT */,
 196                                     hb_tag_t     *script_tags /* OUT */)
 197 {
 198   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 199 
 200   return g.get_script_tags (start_offset, script_count, script_tags);
 201 }
 202 
 203 #define HB_OT_TAG_LATIN_SCRIPT          HB_TAG ('l', 'a', 't', 'n')
 204 
 205 hb_bool_t
 206 hb_ot_layout_table_find_script (hb_face_t    *face,
 207                                 hb_tag_t      table_tag,
 208                                 hb_tag_t      script_tag,
 209                                 unsigned int *script_index)
 210 {
 211   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
 212   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 213 
 214   if (g.find_script_index (script_tag, script_index))
 215     return true;
 216 
 217   /* try finding 'DFLT' */
 218   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
 219     return false;
 220 
 221   /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
 222    * including many versions of DejaVu Sans Mono! */
 223   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
 224     return false;
 225 
 226   /* try with 'latn'; some old fonts put their features there even though
 227      they're really trying to support Thai, for example :( */
 228   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
 229     return false;
 230 
 231   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
 232   return false;
 233 }
 234 
 235 hb_bool_t
 236 hb_ot_layout_table_choose_script (hb_face_t      *face,
 237                                   hb_tag_t        table_tag,
 238                                   const hb_tag_t *script_tags,
 239                                   unsigned int   *script_index,
 240                                   hb_tag_t       *chosen_script)
 241 {
 242   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
 243   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 244 
 245   while (*script_tags)
 246   {
 247     if (g.find_script_index (*script_tags, script_index)) {
 248       if (chosen_script)
 249         *chosen_script = *script_tags;
 250       return true;
 251     }
 252     script_tags++;
 253   }
 254 
 255   /* try finding 'DFLT' */
 256   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
 257     if (chosen_script)
 258       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
 259     return false;
 260   }
 261 
 262   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
 263   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
 264     if (chosen_script)
 265       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
 266     return false;
 267   }
 268 
 269   /* try with 'latn'; some old fonts put their features there even though
 270      they're really trying to support Thai, for example :( */
 271   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
 272     if (chosen_script)
 273       *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
 274     return false;
 275   }
 276 
 277   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
 278   if (chosen_script)
 279     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
 280   return false;
 281 }
 282 
 283 unsigned int
 284 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
 285                                      hb_tag_t      table_tag,
 286                                      unsigned int  start_offset,
 287                                      unsigned int *feature_count /* IN/OUT */,
 288                                      hb_tag_t     *feature_tags /* OUT */)
 289 {
 290   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 291 
 292   return g.get_feature_tags (start_offset, feature_count, feature_tags);
 293 }
 294 
 295 hb_bool_t
 296 hb_ot_layout_table_find_feature (hb_face_t    *face,
 297                                  hb_tag_t      table_tag,
 298                                  hb_tag_t      feature_tag,
 299                                  unsigned int *feature_index)
 300 {
 301   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
 302   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 303 
 304   unsigned int num_features = g.get_feature_count ();
 305   for (unsigned int i = 0; i < num_features; i++)
 306   {
 307     if (feature_tag == g.get_feature_tag (i)) {
 308       if (feature_index) *feature_index = i;
 309       return true;
 310     }
 311   }
 312 
 313   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
 314   return false;
 315 }
 316 
 317 
 318 unsigned int
 319 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
 320                                        hb_tag_t      table_tag,
 321                                        unsigned int  script_index,
 322                                        unsigned int  start_offset,
 323                                        unsigned int *language_count /* IN/OUT */,
 324                                        hb_tag_t     *language_tags /* OUT */)
 325 {
 326   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 327 
 328   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
 329 }
 330 
 331 hb_bool_t
 332 hb_ot_layout_script_find_language (hb_face_t    *face,
 333                                    hb_tag_t      table_tag,
 334                                    unsigned int  script_index,
 335                                    hb_tag_t      language_tag,
 336                                    unsigned int *language_index)
 337 {
 338   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
 339   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 340 
 341   if (s.find_lang_sys_index (language_tag, language_index))
 342     return true;
 343 
 344   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
 345   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
 346     return false;
 347 
 348   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
 349   return false;
 350 }
 351 
 352 hb_bool_t
 353 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
 354                                                   hb_tag_t      table_tag,
 355                                                   unsigned int  script_index,
 356                                                   unsigned int  language_index,
 357                                                   unsigned int *feature_index)
 358 {
 359   return hb_ot_layout_language_get_required_feature (face,
 360                                                      table_tag,
 361                                                      script_index,
 362                                                      language_index,
 363                                                      feature_index,
 364                                                      NULL);
 365 }
 366 
 367 /**
 368  * Since: 0.9.30
 369  **/
 370 hb_bool_t
 371 hb_ot_layout_language_get_required_feature (hb_face_t    *face,
 372                                             hb_tag_t      table_tag,
 373                                             unsigned int  script_index,
 374                                             unsigned int  language_index,
 375                                             unsigned int *feature_index,
 376                                             hb_tag_t     *feature_tag)
 377 {
 378   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 379   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 380 
 381   unsigned int index = l.get_required_feature_index ();
 382   if (feature_index) *feature_index = index;
 383   if (feature_tag) *feature_tag = g.get_feature_tag (index);
 384 
 385   return l.has_required_feature ();
 386 }
 387 
 388 unsigned int
 389 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
 390                                            hb_tag_t      table_tag,
 391                                            unsigned int  script_index,
 392                                            unsigned int  language_index,
 393                                            unsigned int  start_offset,
 394                                            unsigned int *feature_count /* IN/OUT */,
 395                                            unsigned int *feature_indexes /* OUT */)
 396 {
 397   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 398   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 399 
 400   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
 401 }
 402 
 403 unsigned int
 404 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
 405                                         hb_tag_t      table_tag,
 406                                         unsigned int  script_index,
 407                                         unsigned int  language_index,
 408                                         unsigned int  start_offset,
 409                                         unsigned int *feature_count /* IN/OUT */,
 410                                         hb_tag_t     *feature_tags /* OUT */)
 411 {
 412   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 413   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 414 
 415   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
 416   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
 417 
 418   if (feature_tags) {
 419     unsigned int count = *feature_count;
 420     for (unsigned int i = 0; i < count; i++)
 421       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
 422   }
 423 
 424   return ret;
 425 }
 426 
 427 
 428 hb_bool_t
 429 hb_ot_layout_language_find_feature (hb_face_t    *face,
 430                                     hb_tag_t      table_tag,
 431                                     unsigned int  script_index,
 432                                     unsigned int  language_index,
 433                                     hb_tag_t      feature_tag,
 434                                     unsigned int *feature_index)
 435 {
 436   ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
 437   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 438   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 439 
 440   unsigned int num_features = l.get_feature_count ();
 441   for (unsigned int i = 0; i < num_features; i++) {
 442     unsigned int f_index = l.get_feature_index (i);
 443 
 444     if (feature_tag == g.get_feature_tag (f_index)) {
 445       if (feature_index) *feature_index = f_index;
 446       return true;
 447     }
 448   }
 449 
 450   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
 451   return false;
 452 }
 453 
 454 /**
 455  * Since: 0.9.7
 456  **/
 457 unsigned int
 458 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
 459                                   hb_tag_t      table_tag,
 460                                   unsigned int  feature_index,
 461                                   unsigned int  start_offset,
 462                                   unsigned int *lookup_count /* IN/OUT */,
 463                                   unsigned int *lookup_indexes /* OUT */)
 464 {
 465   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 466   const OT::Feature &f = g.get_feature (feature_index);
 467 
 468   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
 469 }
 470 
 471 /**
 472  * Since: 0.9.22
 473  **/
 474 unsigned int
 475 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
 476                                      hb_tag_t      table_tag)
 477 {
 478   switch (table_tag)
 479   {
 480     case HB_OT_TAG_GSUB:
 481     {
 482       return hb_ot_layout_from_face (face)->gsub_lookup_count;
 483     }
 484     case HB_OT_TAG_GPOS:
 485     {
 486       return hb_ot_layout_from_face (face)->gpos_lookup_count;
 487     }
 488   }
 489   return 0;
 490 }
 491 
 492 static void
 493 _hb_ot_layout_collect_lookups_lookups (hb_face_t      *face,
 494                                        hb_tag_t        table_tag,
 495                                        unsigned int    feature_index,
 496                                        hb_set_t       *lookup_indexes /* OUT */)
 497 {
 498   unsigned int lookup_indices[32];
 499   unsigned int offset, len;
 500 
 501   offset = 0;
 502   do {
 503     len = ARRAY_LENGTH (lookup_indices);
 504     hb_ot_layout_feature_get_lookups (face,
 505                                       table_tag,
 506                                       feature_index,
 507                                       offset, &len,
 508                                       lookup_indices);
 509 
 510     for (unsigned int i = 0; i < len; i++)
 511       lookup_indexes->add (lookup_indices[i]);
 512 
 513     offset += len;
 514   } while (len == ARRAY_LENGTH (lookup_indices));
 515 }
 516 
 517 static void
 518 _hb_ot_layout_collect_lookups_features (hb_face_t      *face,
 519                                         hb_tag_t        table_tag,
 520                                         unsigned int    script_index,
 521                                         unsigned int    language_index,
 522                                         const hb_tag_t *features,
 523                                         hb_set_t       *lookup_indexes /* OUT */)
 524 {
 525   if (!features)
 526   {
 527     unsigned int required_feature_index;
 528     if (hb_ot_layout_language_get_required_feature (face,
 529                                                     table_tag,
 530                                                     script_index,
 531                                                     language_index,
 532                                                     &required_feature_index,
 533                                                     NULL))
 534       _hb_ot_layout_collect_lookups_lookups (face,
 535                                              table_tag,
 536                                              required_feature_index,
 537                                              lookup_indexes);
 538 
 539     /* All features */
 540     unsigned int feature_indices[32];
 541     unsigned int offset, len;
 542 
 543     offset = 0;
 544     do {
 545       len = ARRAY_LENGTH (feature_indices);
 546       hb_ot_layout_language_get_feature_indexes (face,
 547                                                  table_tag,
 548                                                  script_index,
 549                                                  language_index,
 550                                                  offset, &len,
 551                                                  feature_indices);
 552 
 553       for (unsigned int i = 0; i < len; i++)
 554         _hb_ot_layout_collect_lookups_lookups (face,
 555                                                table_tag,
 556                                                feature_indices[i],
 557                                                lookup_indexes);
 558 
 559       offset += len;
 560     } while (len == ARRAY_LENGTH (feature_indices));
 561   }
 562   else
 563   {
 564     for (; *features; features++)
 565     {
 566       unsigned int feature_index;
 567       if (hb_ot_layout_language_find_feature (face,
 568                                               table_tag,
 569                                               script_index,
 570                                               language_index,
 571                                               *features,
 572                                               &feature_index))
 573         _hb_ot_layout_collect_lookups_lookups (face,
 574                                                table_tag,
 575                                                feature_index,
 576                                                lookup_indexes);
 577     }
 578   }
 579 }
 580 
 581 static void
 582 _hb_ot_layout_collect_lookups_languages (hb_face_t      *face,
 583                                          hb_tag_t        table_tag,
 584                                          unsigned int    script_index,
 585                                          const hb_tag_t *languages,
 586                                          const hb_tag_t *features,
 587                                          hb_set_t       *lookup_indexes /* OUT */)
 588 {
 589   _hb_ot_layout_collect_lookups_features (face,
 590                                           table_tag,
 591                                           script_index,
 592                                           HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
 593                                           features,
 594                                           lookup_indexes);
 595 
 596   if (!languages)
 597   {
 598     /* All languages */
 599     unsigned int count = hb_ot_layout_script_get_language_tags (face,
 600                                                                 table_tag,
 601                                                                 script_index,
 602                                                                 0, NULL, NULL);
 603     for (unsigned int language_index = 0; language_index < count; language_index++)
 604       _hb_ot_layout_collect_lookups_features (face,
 605                                               table_tag,
 606                                               script_index,
 607                                               language_index,
 608                                               features,
 609                                               lookup_indexes);
 610   }
 611   else
 612   {
 613     for (; *languages; languages++)
 614     {
 615       unsigned int language_index;
 616       if (hb_ot_layout_script_find_language (face,
 617                                              table_tag,
 618                                              script_index,
 619                                              *languages,
 620                                              &language_index))
 621         _hb_ot_layout_collect_lookups_features (face,
 622                                                 table_tag,
 623                                                 script_index,
 624                                                 language_index,
 625                                                 features,
 626                                                 lookup_indexes);
 627     }
 628   }
 629 }
 630 
 631 /**
 632  * Since: 0.9.8
 633  **/
 634 void
 635 hb_ot_layout_collect_lookups (hb_face_t      *face,
 636                               hb_tag_t        table_tag,
 637                               const hb_tag_t *scripts,
 638                               const hb_tag_t *languages,
 639                               const hb_tag_t *features,
 640                               hb_set_t       *lookup_indexes /* OUT */)
 641 {
 642   if (!scripts)
 643   {
 644     /* All scripts */
 645     unsigned int count = hb_ot_layout_table_get_script_tags (face,
 646                                                              table_tag,
 647                                                              0, NULL, NULL);
 648     for (unsigned int script_index = 0; script_index < count; script_index++)
 649       _hb_ot_layout_collect_lookups_languages (face,
 650                                                table_tag,
 651                                                script_index,
 652                                                languages,
 653                                                features,
 654                                                lookup_indexes);
 655   }
 656   else
 657   {
 658     for (; *scripts; scripts++)
 659     {
 660       unsigned int script_index;
 661       if (hb_ot_layout_table_find_script (face,
 662                                           table_tag,
 663                                           *scripts,
 664                                           &script_index))
 665         _hb_ot_layout_collect_lookups_languages (face,
 666                                                  table_tag,
 667                                                  script_index,
 668                                                  languages,
 669                                                  features,
 670                                                  lookup_indexes);
 671     }
 672   }
 673 }
 674 
 675 /**
 676  * Since: 0.9.7
 677  **/
 678 void
 679 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
 680                                     hb_tag_t      table_tag,
 681                                     unsigned int  lookup_index,
 682                                     hb_set_t     *glyphs_before, /* OUT. May be NULL */
 683                                     hb_set_t     *glyphs_input,  /* OUT. May be NULL */
 684                                     hb_set_t     *glyphs_after,  /* OUT. May be NULL */
 685                                     hb_set_t     *glyphs_output  /* OUT. May be NULL */)
 686 {
 687   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return;
 688 
 689   OT::hb_collect_glyphs_context_t c (face,
 690                                      glyphs_before,
 691                                      glyphs_input,
 692                                      glyphs_after,
 693                                      glyphs_output);
 694 
 695   switch (table_tag)
 696   {
 697     case HB_OT_TAG_GSUB:
 698     {
 699       const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
 700       l.collect_glyphs (&c);
 701       return;
 702     }
 703     case HB_OT_TAG_GPOS:
 704     {
 705       const OT::PosLookup& l = hb_ot_layout_from_face (face)->gpos->get_lookup (lookup_index);
 706       l.collect_glyphs (&c);
 707       return;
 708     }
 709   }
 710 }
 711 
 712 
 713 /*
 714  * OT::GSUB
 715  */
 716 
 717 hb_bool_t
 718 hb_ot_layout_has_substitution (hb_face_t *face)
 719 {
 720   return &_get_gsub (face) != &OT::Null(OT::GSUB);
 721 }
 722 
 723 /**
 724  * Since: 0.9.7
 725  **/
 726 hb_bool_t
 727 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
 728                                       unsigned int          lookup_index,
 729                                       const hb_codepoint_t *glyphs,
 730                                       unsigned int          glyphs_length,
 731                                       hb_bool_t             zero_context)
 732 {
 733   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
 734   return hb_ot_layout_lookup_would_substitute_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
 735 }
 736 
 737 hb_bool_t
 738 hb_ot_layout_lookup_would_substitute_fast (hb_face_t            *face,
 739                                            unsigned int          lookup_index,
 740                                            const hb_codepoint_t *glyphs,
 741                                            unsigned int          glyphs_length,
 742                                            hb_bool_t             zero_context)
 743 {
 744   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
 745   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
 746 
 747   const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
 748 
 749   return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_accels[lookup_index]);
 750 }
 751 
 752 void
 753 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 754 {
 755   OT::GSUB::substitute_start (font, buffer);
 756 }
 757 
 758 void
 759 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
 760 {
 761   OT::GSUB::substitute_finish (font, buffer);
 762 }
 763 
 764 /**
 765  * Since: 0.9.7
 766  **/
 767 void
 768 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
 769                                         unsigned int  lookup_index,
 770                                         hb_set_t     *glyphs)
 771 {
 772   OT::hb_closure_context_t c (face, glyphs);
 773 
 774   const OT::SubstLookup& l = _get_gsub (face).get_lookup (lookup_index);
 775 
 776   l.closure (&c);
 777 }
 778 
 779 /*
 780  * OT::GPOS
 781  */
 782 
 783 hb_bool_t
 784 hb_ot_layout_has_positioning (hb_face_t *face)
 785 {
 786   return &_get_gpos (face) != &OT::Null(OT::GPOS);
 787 }
 788 
 789 void
 790 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 791 {
 792   OT::GPOS::position_start (font, buffer);
 793 }
 794 
 795 void
 796 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
 797 {
 798   OT::GPOS::position_finish (font, buffer);
 799 }
 800 
 801 /**
 802  * Since: 0.9.10
 803  **/
 804 hb_bool_t
 805 hb_ot_layout_get_size_params (hb_face_t    *face,
 806                               unsigned int *design_size,       /* OUT.  May be NULL */
 807                               unsigned int *subfamily_id,      /* OUT.  May be NULL */
 808                               unsigned int *subfamily_name_id, /* OUT.  May be NULL */
 809                               unsigned int *range_start,       /* OUT.  May be NULL */
 810                               unsigned int *range_end          /* OUT.  May be NULL */)
 811 {
 812   const OT::GPOS &gpos = _get_gpos (face);
 813   const hb_tag_t tag = HB_TAG ('s','i','z','e');
 814 
 815   unsigned int num_features = gpos.get_feature_count ();
 816   for (unsigned int i = 0; i < num_features; i++)
 817   {
 818     if (tag == gpos.get_feature_tag (i))
 819     {
 820       const OT::Feature &f = gpos.get_feature (i);
 821       const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
 822 
 823       if (params.designSize)
 824       {
 825 #define PARAM(a, A) if (a) *a = params.A
 826         PARAM (design_size, designSize);
 827         PARAM (subfamily_id, subfamilyID);
 828         PARAM (subfamily_name_id, subfamilyNameID);
 829         PARAM (range_start, rangeStart);
 830         PARAM (range_end, rangeEnd);
 831 #undef PARAM
 832 
 833         return true;
 834       }
 835     }
 836   }
 837 
 838 #define PARAM(a, A) if (a) *a = 0
 839   PARAM (design_size, designSize);
 840   PARAM (subfamily_id, subfamilyID);
 841   PARAM (subfamily_name_id, subfamilyNameID);
 842   PARAM (range_start, rangeStart);
 843   PARAM (range_end, rangeEnd);
 844 #undef PARAM
 845 
 846   return false;
 847 }
 848 
 849 
 850 /*
 851  * Parts of different types are implemented here such that they have direct
 852  * access to GSUB/GPOS lookups.
 853  */
 854 
 855 
 856 struct GSUBProxy
 857 {
 858   static const unsigned int table_index = 0;
 859   static const bool inplace = false;
 860   typedef OT::SubstLookup Lookup;
 861 
 862   GSUBProxy (hb_face_t *face) :
 863     table (*hb_ot_layout_from_face (face)->gsub),
 864     accels (hb_ot_layout_from_face (face)->gsub_accels) {}
 865 
 866   const OT::GSUB &table;
 867   const hb_ot_layout_lookup_accelerator_t *accels;
 868 };
 869 
 870 struct GPOSProxy
 871 {
 872   static const unsigned int table_index = 1;
 873   static const bool inplace = true;
 874   typedef OT::PosLookup Lookup;
 875 
 876   GPOSProxy (hb_face_t *face) :
 877     table (*hb_ot_layout_from_face (face)->gpos),
 878     accels (hb_ot_layout_from_face (face)->gpos_accels) {}
 879 
 880   const OT::GPOS &table;
 881   const hb_ot_layout_lookup_accelerator_t *accels;
 882 };
 883 
 884 
 885 template <typename Obj>
 886 static inline bool
 887 apply_forward (OT::hb_apply_context_t *c,
 888                const Obj &obj,
 889                const hb_ot_layout_lookup_accelerator_t &accel)
 890 {
 891   bool ret = false;
 892   hb_buffer_t *buffer = c->buffer;
 893   while (buffer->idx < buffer->len)
 894   {
 895     if (accel.may_have (buffer->cur().codepoint) &&
 896         (buffer->cur().mask & c->lookup_mask) &&
 897         c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
 898         obj.apply (c))
 899       ret = true;
 900     else
 901       buffer->next_glyph ();
 902   }
 903   return ret;
 904 }
 905 
 906 template <typename Obj>
 907 static inline bool
 908 apply_backward (OT::hb_apply_context_t *c,
 909                 const Obj &obj,
 910                 const hb_ot_layout_lookup_accelerator_t &accel)
 911 {
 912   bool ret = false;
 913   hb_buffer_t *buffer = c->buffer;
 914   do
 915   {
 916     if (accel.may_have (buffer->cur().codepoint) &&
 917         (buffer->cur().mask & c->lookup_mask) &&
 918         c->check_glyph_property (&buffer->cur(), c->lookup_props) &&
 919         obj.apply (c))
 920       ret = true;
 921     /* The reverse lookup doesn't "advance" cursor (for good reason). */
 922     buffer->idx--;
 923 
 924   }
 925   while ((int) buffer->idx >= 0);
 926   return ret;
 927 }
 928 
 929 struct hb_apply_forward_context_t :
 930        OT::hb_dispatch_context_t<hb_apply_forward_context_t, bool, HB_DEBUG_APPLY>
 931 {
 932   inline const char *get_name (void) { return "APPLY_FWD"; }
 933   template <typename T>
 934   inline return_t dispatch (const T &obj) { return apply_forward (c, obj, accel); }
 935   static return_t default_return_value (void) { return false; }
 936   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return true; }
 937 
 938   hb_apply_forward_context_t (OT::hb_apply_context_t *c_,
 939                               const hb_ot_layout_lookup_accelerator_t &accel_) :
 940                                 c (c_),
 941                                 accel (accel_),
 942                                 debug_depth (0) {}
 943 
 944   OT::hb_apply_context_t *c;
 945   const hb_ot_layout_lookup_accelerator_t &accel;
 946   unsigned int debug_depth;
 947 };
 948 
 949 template <typename Proxy>
 950 static inline void
 951 apply_string (OT::hb_apply_context_t *c,
 952               const typename Proxy::Lookup &lookup,
 953               const hb_ot_layout_lookup_accelerator_t &accel)
 954 {
 955   hb_buffer_t *buffer = c->buffer;
 956 
 957   if (unlikely (!buffer->len || !c->lookup_mask))
 958     return;
 959 
 960   c->set_lookup_props (lookup.get_props ());
 961 
 962   if (likely (!lookup.is_reverse ()))
 963   {
 964     /* in/out forward substitution/positioning */
 965     if (Proxy::table_index == 0)
 966       buffer->clear_output ();
 967     buffer->idx = 0;
 968 
 969     bool ret;
 970     if (lookup.get_subtable_count () == 1)
 971     {
 972       hb_apply_forward_context_t c_forward (c, accel);
 973       ret = lookup.dispatch (&c_forward);
 974     }
 975     else
 976       ret = apply_forward (c, lookup, accel);
 977     if (ret)
 978     {
 979       if (!Proxy::inplace)
 980         buffer->swap_buffers ();
 981       else
 982         assert (!buffer->has_separate_output ());
 983     }
 984   }
 985   else
 986   {
 987     /* in-place backward substitution/positioning */
 988     if (Proxy::table_index == 0)
 989       buffer->remove_output ();
 990     buffer->idx = buffer->len - 1;
 991 
 992     apply_backward (c, lookup, accel);
 993   }
 994 }
 995 
 996 template <typename Proxy>
 997 inline void hb_ot_map_t::apply (const Proxy &proxy,
 998                                 const hb_ot_shape_plan_t *plan,
 999                                 hb_font_t *font,
1000                                 hb_buffer_t *buffer) const
1001 {
1002   const unsigned int table_index = proxy.table_index;
1003   unsigned int i = 0;
1004   OT::hb_apply_context_t c (table_index, font, buffer);
1005   c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
1006 
1007   for (unsigned int stage_index = 0; stage_index < stages[table_index].len; stage_index++) {
1008     const stage_map_t *stage = &stages[table_index][stage_index];
1009     for (; i < stage->last_lookup; i++)
1010     {
1011 #if 0
1012       char buf[4096];
1013       hb_buffer_serialize_glyphs (buffer, 0, buffer->len,
1014                                   buf, sizeof (buf), NULL,
1015                                   font,
1016                                   HB_BUFFER_SERIALIZE_FORMAT_TEXT,
1017                                   Proxy::table_index == 0 ?
1018                                   HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS :
1019                                   HB_BUFFER_SERIALIZE_FLAG_DEFAULT);
1020       printf ("buf: [%s]\n", buf);
1021 #endif
1022 
1023       unsigned int lookup_index = lookups[table_index][i].index;
1024       c.set_lookup_index (lookup_index);
1025       c.set_lookup_mask (lookups[table_index][i].mask);
1026       c.set_auto_zwj (lookups[table_index][i].auto_zwj);
1027       apply_string<Proxy> (&c,
1028                            proxy.table.get_lookup (lookup_index),
1029                            proxy.accels[lookup_index]);
1030     }
1031 
1032     if (stage->pause_func)
1033     {
1034       buffer->clear_output ();
1035       stage->pause_func (plan, font, buffer);
1036     }
1037   }
1038 }
1039 
1040 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1041 {
1042   GSUBProxy proxy (font->face);
1043   apply (proxy, plan, font, buffer);
1044 }
1045 
1046 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1047 {
1048   GPOSProxy proxy (font->face);
1049   apply (proxy, plan, font, buffer);
1050 }
1051 
1052 HB_INTERNAL void
1053 hb_ot_layout_substitute_lookup (OT::hb_apply_context_t *c,
1054                                 const OT::SubstLookup &lookup,
1055                                 const hb_ot_layout_lookup_accelerator_t &accel)
1056 {
1057   apply_string<GSUBProxy> (c, lookup, accel);
1058 }