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