tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-ot-layout.cc (93143B)


      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.hh"
     32 
     33 #ifndef HB_NO_OT_LAYOUT
     34 
     35 #ifdef HB_NO_OT_TAG
     36 #error "Cannot compile hb-ot-layout.cc with HB_NO_OT_TAG."
     37 #endif
     38 
     39 #include "hb-open-type.hh"
     40 #include "hb-ot-layout.hh"
     41 #include "hb-ot-face.hh"
     42 #include "hb-ot-map.hh"
     43 #include "hb-map.hh"
     44 
     45 #include "hb-ot-kern-table.hh"
     46 #include "hb-ot-layout-gdef-table.hh"
     47 #include "hb-ot-layout-gsub-table.hh"
     48 #include "hb-ot-layout-gpos-table.hh"
     49 #include "hb-ot-layout-base-table.hh"
     50 #include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise.
     51 #include "hb-ot-name-table.hh"
     52 #include "hb-ot-os2-table.hh"
     53 
     54 #include "hb-aat-layout-morx-table.hh"
     55 #include "hb-aat-layout-opbd-table.hh" // Just so we compile it; unused otherwise.
     56 
     57 using OT::Layout::GSUB;
     58 using OT::Layout::GPOS;
     59 
     60 /**
     61 * SECTION:hb-ot-layout
     62 * @title: hb-ot-layout
     63 * @short_description: OpenType Layout
     64 * @include: hb-ot.h
     65 *
     66 * Functions for querying OpenType Layout features in the font face.
     67 * See the [OpenType specification](http://www.microsoft.com/typography/otspec/)
     68 * for details.
     69 **/
     70 
     71 
     72 /*
     73 * kern
     74 */
     75 
     76 #ifndef HB_NO_OT_KERN
     77 /**
     78 * hb_ot_layout_has_kerning:
     79 * @face: The #hb_face_t to work on
     80 *
     81 * Tests whether a face includes any kerning data in the 'kern' table.
     82 * Does NOT test for kerning lookups in the GPOS table.
     83 *
     84 * Return value: `true` if data found, `false` otherwise
     85 *
     86 **/
     87 bool
     88 hb_ot_layout_has_kerning (hb_face_t *face)
     89 {
     90  return face->table.kern->table->has_data ();
     91 }
     92 
     93 /**
     94 * hb_ot_layout_has_machine_kerning:
     95 * @face: The #hb_face_t to work on
     96 *
     97 * Tests whether a face includes any state-machine kerning in the 'kern' table.
     98 * Does NOT examine the GPOS table.
     99 *
    100 * Return value: `true` if data found, `false` otherwise
    101 *
    102 **/
    103 bool
    104 hb_ot_layout_has_machine_kerning (hb_face_t *face)
    105 {
    106  return face->table.kern->table->has_state_machine ();
    107 }
    108 
    109 /**
    110 * hb_ot_layout_has_cross_kerning:
    111 * @face: The #hb_face_t to work on
    112 *
    113 * Tests whether a face has any cross-stream kerning (i.e., kerns
    114 * that make adjustments perpendicular to the direction of the text
    115 * flow: Y adjustments in horizontal text or X adjustments in
    116 * vertical text) in the 'kern' table.
    117 *
    118 * Does NOT examine the GPOS table.
    119 *
    120 * Return value: `true` is data found, `false` otherwise
    121 *
    122 **/
    123 bool
    124 hb_ot_layout_has_cross_kerning (hb_face_t *face)
    125 {
    126  return face->table.kern->table->has_cross_stream ();
    127 }
    128 
    129 void
    130 hb_ot_layout_kern (const hb_ot_shape_plan_t *plan,
    131 	   hb_font_t *font,
    132 	   hb_buffer_t  *buffer)
    133 {
    134  auto &accel = *font->face->table.kern;
    135  hb_blob_t *blob = accel.get_blob ();
    136 
    137  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob);
    138 
    139  if (!buffer->message (font, "start table kern")) return;
    140  c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set ();
    141  accel.apply (&c);
    142  accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set);
    143  (void) buffer->message (font, "end table kern");
    144 }
    145 #endif
    146 
    147 
    148 /*
    149 * GDEF
    150 */
    151 
    152 bool
    153 OT::GDEF::is_blocklisted (hb_blob_t *blob,
    154 		  hb_face_t *face) const
    155 {
    156 #ifdef HB_NO_OT_LAYOUT_BLOCKLIST
    157  return false;
    158 #endif
    159  /* The ugly business of blocklisting individual fonts' tables happen here!
    160   * See this thread for why we finally had to bend in and do this:
    161   * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
    162   *
    163   * In certain versions of Times New Roman Italic and Bold Italic,
    164   * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark)
    165   * in GDEF.  Many versions of Tahoma have bad GDEF tables that
    166   * incorrectly classify some spacing marks such as certain IPA
    167   * symbols as glyph class 3. So do older versions of Microsoft
    168   * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04.
    169   *
    170   * Nuke the GDEF tables of to avoid unwanted width-zeroing.
    171   *
    172   * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
    173   *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
    174   *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
    175   */
    176  switch HB_CODEPOINT_ENCODE3(blob->length,
    177 		      face->table.GSUB->table.get_length (),
    178 		      face->table.GPOS->table.get_length ())
    179  {
    180    /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
    181    case HB_CODEPOINT_ENCODE3 (442, 2874, 42038):
    182    /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
    183    case HB_CODEPOINT_ENCODE3 (430, 2874, 40662):
    184    /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
    185    case HB_CODEPOINT_ENCODE3 (442, 2874, 39116):
    186    /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
    187    case HB_CODEPOINT_ENCODE3 (430, 2874, 39374):
    188    /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
    189    case HB_CODEPOINT_ENCODE3 (490, 3046, 41638):
    190    /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
    191    case HB_CODEPOINT_ENCODE3 (478, 3046, 41902):
    192    /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
    193    case HB_CODEPOINT_ENCODE3 (898, 12554, 46470):
    194    /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
    195    case HB_CODEPOINT_ENCODE3 (910, 12566, 47732):
    196    /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
    197    case HB_CODEPOINT_ENCODE3 (928, 23298, 59332):
    198    /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
    199    case HB_CODEPOINT_ENCODE3 (940, 23310, 60732):
    200    /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
    201    case HB_CODEPOINT_ENCODE3 (964, 23836, 60072):
    202    /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
    203    case HB_CODEPOINT_ENCODE3 (976, 23832, 61456):
    204    /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
    205    case HB_CODEPOINT_ENCODE3 (994, 24474, 60336):
    206    /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
    207    case HB_CODEPOINT_ENCODE3 (1006, 24470, 61740):
    208    /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
    209    case HB_CODEPOINT_ENCODE3 (1006, 24576, 61346):
    210    /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
    211    case HB_CODEPOINT_ENCODE3 (1018, 24572, 62828):
    212    /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
    213    case HB_CODEPOINT_ENCODE3 (1006, 24576, 61352):
    214    /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
    215    case HB_CODEPOINT_ENCODE3 (1018, 24572, 62834):
    216    /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
    217    case HB_CODEPOINT_ENCODE3 (832, 7324, 47162):
    218    /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
    219    case HB_CODEPOINT_ENCODE3 (844, 7302, 45474):
    220    /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc  himalaya.ttf from Windows 7 */
    221    case HB_CODEPOINT_ENCODE3 (180, 13054, 7254):
    222    /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
    223    case HB_CODEPOINT_ENCODE3 (192, 12638, 7254):
    224    /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
    225    case HB_CODEPOINT_ENCODE3 (192, 12690, 7254):
    226    /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
    227    /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
    228    case HB_CODEPOINT_ENCODE3 (188, 248, 3852):
    229    /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
    230    /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
    231    case HB_CODEPOINT_ENCODE3 (188, 264, 3426):
    232    /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
    233    case HB_CODEPOINT_ENCODE3 (1058, 47032, 11818):
    234    /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
    235    case HB_CODEPOINT_ENCODE3 (1046, 47030, 12600):
    236    /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
    237    case HB_CODEPOINT_ENCODE3 (1058, 71796, 16770):
    238    /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
    239    case HB_CODEPOINT_ENCODE3 (1046, 71790, 17862):
    240    /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
    241    case HB_CODEPOINT_ENCODE3 (1046, 71788, 17112):
    242    /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
    243    case HB_CODEPOINT_ENCODE3 (1058, 71794, 17514):
    244    /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
    245    case HB_CODEPOINT_ENCODE3 (1330, 109904, 57938):
    246    /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
    247    case HB_CODEPOINT_ENCODE3 (1330, 109904, 58972):
    248    /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
    249     *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
    250    case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836):
    251    /* 88d2006ca084f04af2df1954ed714a8c71e8400f  Courier New.ttf from macOS 15 */
    252    case HB_CODEPOINT_ENCODE3 (588, 5078, 14418):
    253    /* 608e3ebb6dd1aee521cff08eb07d500a2c59df68  Courier New Bold.ttf from macOS 15 */
    254    case HB_CODEPOINT_ENCODE3 (588, 5078, 14238):
    255    /* d13221044ff054efd78f1cd8631b853c3ce85676  cour.ttf from Windows 10 */
    256    case HB_CODEPOINT_ENCODE3 (894, 17162, 33960):
    257    /* 68ed4a22d8067fcf1622ac6f6e2f4d3a2e3ec394  courbd.ttf from Windows 10 */
    258    case HB_CODEPOINT_ENCODE3 (894, 17154, 34472):
    259    /* 4cdb0259c96b7fd7c103821bb8f08f7cc6b211d7  cour.ttf from Windows 8.1 */
    260    case HB_CODEPOINT_ENCODE3 (816, 7868, 17052):
    261    /* 920483d8a8ed37f7f0afdabbe7f679aece7c75d8  courbd.ttf from Windows 8.1 */
    262    case HB_CODEPOINT_ENCODE3 (816, 7868, 17138):
    263      return true;
    264  }
    265  return false;
    266 }
    267 
    268 static void
    269 _hb_ot_layout_set_glyph_props (hb_font_t *font,
    270 		       hb_buffer_t *buffer)
    271 {
    272  _hb_buffer_assert_gsubgpos_vars (buffer);
    273 
    274  const auto &gdef = *font->face->table.GDEF;
    275  unsigned int count = buffer->len;
    276  hb_glyph_info_t *info = buffer->info;
    277  for (unsigned int i = 0; i < count; i++)
    278  {
    279    _hb_glyph_info_set_glyph_props (&info[i], gdef.get_glyph_props (info[i].codepoint));
    280    _hb_glyph_info_clear_lig_props (&info[i]);
    281  }
    282 }
    283 
    284 /* Public API */
    285 
    286 /**
    287 * hb_ot_layout_has_glyph_classes:
    288 * @face: #hb_face_t to work upon
    289 *
    290 * Tests whether a face has any glyph classes defined in its GDEF table.
    291 *
    292 * Return value: `true` if data found, `false` otherwise
    293 *
    294 **/
    295 hb_bool_t
    296 hb_ot_layout_has_glyph_classes (hb_face_t *face)
    297 {
    298  return face->table.GDEF->table->has_glyph_classes ();
    299 }
    300 
    301 /**
    302 * hb_ot_layout_get_glyph_class:
    303 * @face: The #hb_face_t to work on
    304 * @glyph: The #hb_codepoint_t code point to query
    305 *
    306 * Fetches the GDEF class of the requested glyph in the specified face.
    307 *
    308 * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code
    309 * point in the GDEF table of the face.
    310 *
    311 * Since: 0.9.7
    312 **/
    313 hb_ot_layout_glyph_class_t
    314 hb_ot_layout_get_glyph_class (hb_face_t      *face,
    315 		      hb_codepoint_t  glyph)
    316 {
    317  return (hb_ot_layout_glyph_class_t) face->table.GDEF->table->get_glyph_class (glyph);
    318 }
    319 
    320 /**
    321 * hb_ot_layout_get_glyphs_in_class:
    322 * @face: The #hb_face_t to work on
    323 * @klass: The #hb_ot_layout_glyph_class_t GDEF class to retrieve
    324 * @glyphs: (out): The #hb_set_t set of all glyphs belonging to the requested
    325 *          class.
    326 *
    327 * Retrieves the set of all glyphs from the face that belong to the requested
    328 * glyph class in the face's GDEF table.
    329 *
    330 * Since: 0.9.7
    331 **/
    332 void
    333 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
    334 			  hb_ot_layout_glyph_class_t  klass,
    335 			  hb_set_t                   *glyphs /* OUT */)
    336 {
    337  return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs);
    338 }
    339 
    340 #ifndef HB_NO_LAYOUT_UNUSED
    341 /**
    342 * hb_ot_layout_get_attach_points:
    343 * @face: The #hb_face_t to work on
    344 * @glyph: The #hb_codepoint_t code point to query
    345 * @start_offset: offset of the first attachment point to retrieve
    346 * @point_count: (inout) (nullable): Input = the maximum number of attachment points to return;
    347 *               Output = the actual number of attachment points returned (may be zero)
    348 * @point_array: (out) (array length=point_count): The array of attachment points found for the query
    349 *
    350 * Fetches a list of all attachment points for the specified glyph in the GDEF
    351 * table of the face. The list returned will begin at the offset provided.
    352 *
    353 * Useful if the client program wishes to cache the list.
    354 *
    355 * Return value: Total number of attachment points for @glyph.
    356 *
    357 **/
    358 unsigned int
    359 hb_ot_layout_get_attach_points (hb_face_t      *face,
    360 			hb_codepoint_t  glyph,
    361 			unsigned int    start_offset,
    362 			unsigned int   *point_count /* IN/OUT */,
    363 			unsigned int   *point_array /* OUT */)
    364 {
    365  return face->table.GDEF->table->get_attach_points (glyph,
    366 					     start_offset,
    367 					     point_count,
    368 					     point_array);
    369 }
    370 /**
    371 * hb_ot_layout_get_ligature_carets:
    372 * @font: The #hb_font_t to work on
    373 * @direction: The #hb_direction_t text direction to use
    374 * @glyph: The #hb_codepoint_t code point to query
    375 * @start_offset: offset of the first caret position to retrieve
    376 * @caret_count: (inout) (nullable): Input = the maximum number of caret positions to return;
    377 *               Output = the actual number of caret positions returned (may be zero)
    378 * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query
    379 *
    380 * Fetches a list of the caret positions defined for a ligature glyph in the GDEF
    381 * table of the font. The list returned will begin at the offset provided.
    382 *
    383 * Note that a ligature that is formed from n characters will have n-1
    384 * caret positions. The first character is not represented in the array,
    385 * since its caret position is the glyph position.
    386 *
    387 * The positions returned by this function are 'unshaped', and will have to
    388 * be fixed up for kerning that may be applied to the ligature glyph.
    389 *
    390 * Return value: Total number of ligature caret positions for @glyph.
    391 *
    392 **/
    393 unsigned int
    394 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
    395 			  hb_direction_t  direction,
    396 			  hb_codepoint_t  glyph,
    397 			  unsigned int    start_offset,
    398 			  unsigned int   *caret_count /* IN/OUT */,
    399 			  hb_position_t  *caret_array /* OUT */)
    400 {
    401  return font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
    402 }
    403 #endif
    404 
    405 
    406 /*
    407 * GSUB/GPOS
    408 */
    409 
    410 bool
    411 GSUB::is_blocklisted (hb_blob_t *blob HB_UNUSED,
    412 		  hb_face_t *face) const
    413 {
    414 #ifdef HB_NO_OT_LAYOUT_BLOCKLIST
    415  return false;
    416 #endif
    417  return false;
    418 }
    419 
    420 bool
    421 GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED,
    422 		  hb_face_t *face HB_UNUSED) const
    423 {
    424 #ifdef HB_NO_OT_LAYOUT_BLOCKLIST
    425  return false;
    426 #endif
    427  return false;
    428 }
    429 
    430 static const OT::GSUBGPOS&
    431 get_gsubgpos_table (hb_face_t *face,
    432 	    hb_tag_t   table_tag)
    433 {
    434  switch (table_tag) {
    435    case HB_OT_TAG_GSUB: return *face->table.GSUB->table;
    436    case HB_OT_TAG_GPOS: return *face->table.GPOS->table;
    437    default:             return Null (OT::GSUBGPOS);
    438  }
    439 }
    440 
    441 
    442 /**
    443 * hb_ot_layout_table_get_script_tags:
    444 * @face: #hb_face_t to work upon
    445 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    446 * @start_offset: offset of the first script tag to retrieve
    447 * @script_count: (inout) (nullable): Input = the maximum number of script tags to return;
    448 *                Output = the actual number of script tags returned (may be zero)
    449 * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query
    450 *
    451 * Fetches a list of all scripts enumerated in the specified face's GSUB table
    452 * or GPOS table. The list returned will begin at the offset provided.
    453 *
    454 * Return value: Total number of script tags.
    455 *
    456 **/
    457 unsigned int
    458 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
    459 			    hb_tag_t      table_tag,
    460 			    unsigned int  start_offset,
    461 			    unsigned int *script_count /* IN/OUT */,
    462 			    hb_tag_t     *script_tags  /* OUT */)
    463 {
    464  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    465 
    466  return g.get_script_tags (start_offset, script_count, script_tags);
    467 }
    468 
    469 #define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
    470 
    471 /**
    472 * hb_ot_layout_table_find_script:
    473 * @face: #hb_face_t to work upon
    474 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    475 * @script_tag: #hb_tag_t of the script tag requested
    476 * @script_index: (out): The index of the requested script tag
    477 *
    478 * Fetches the index if a given script tag in the specified face's GSUB table
    479 * or GPOS table.
    480 *
    481 * Return value: `true` if the script is found, `false` otherwise
    482 *
    483 **/
    484 hb_bool_t
    485 hb_ot_layout_table_find_script (hb_face_t    *face,
    486 			hb_tag_t      table_tag,
    487 			hb_tag_t      script_tag,
    488 			unsigned int *script_index /* OUT */)
    489 {
    490  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
    491  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    492 
    493  if (g.find_script_index (script_tag, script_index))
    494    return true;
    495 
    496  /* try finding 'DFLT' */
    497  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
    498    return false;
    499 
    500  /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
    501   * including many versions of DejaVu Sans Mono! */
    502  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
    503    return false;
    504 
    505  /* try with 'latn'; some old fonts put their features there even though
    506     they're really trying to support Thai, for example :( */
    507  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
    508    return false;
    509 
    510  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
    511  return false;
    512 }
    513 
    514 #ifndef HB_DISABLE_DEPRECATED
    515 /**
    516 * hb_ot_layout_table_choose_script:
    517 * @face: #hb_face_t to work upon
    518 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    519 * @script_tags: Array of #hb_tag_t script tags
    520 * @script_index: (out): The index of the chosen script
    521 * @chosen_script: (out): #hb_tag_t of the chosen script
    522 *
    523 * Deprecated since 2.0.0
    524 **/
    525 hb_bool_t
    526 hb_ot_layout_table_choose_script (hb_face_t      *face,
    527 			  hb_tag_t        table_tag,
    528 			  const hb_tag_t *script_tags,
    529 			  unsigned int   *script_index  /* OUT */,
    530 			  hb_tag_t       *chosen_script /* OUT */)
    531 {
    532  const hb_tag_t *t;
    533  for (t = script_tags; *t; t++);
    534  return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script);
    535 }
    536 #endif
    537 
    538 /**
    539 * hb_ot_layout_table_select_script:
    540 * @face: #hb_face_t to work upon
    541 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    542 * @script_count: Number of script tags in the array
    543 * @script_tags: Array of #hb_tag_t script tags
    544 * @script_index: (out) (nullable): The index of the requested script
    545 * @chosen_script: (out) (nullable): #hb_tag_t of the requested script
    546 *
    547 * Selects an OpenType script for @table_tag from the @script_tags array.
    548 *
    549 * If the table does not have any of the requested scripts, then `DFLT`,
    550 * `dflt`, and `latn` tags are tried in that order. If the table still does not
    551 * have any of these scripts, @script_index is set to
    552 * #HB_OT_LAYOUT_NO_SCRIPT_INDEX and @chosen_script is set to #HB_TAG_NONE.
    553 *
    554 * Return value:
    555 * `true` if one of the requested scripts is selected, `false` if a fallback
    556 * script is selected or if no scripts are selected.
    557 *
    558 * Since: 2.0.0
    559 **/
    560 hb_bool_t
    561 hb_ot_layout_table_select_script (hb_face_t      *face,
    562 			  hb_tag_t        table_tag,
    563 			  unsigned int    script_count,
    564 			  const hb_tag_t *script_tags,
    565 			  unsigned int   *script_index  /* OUT */,
    566 			  hb_tag_t       *chosen_script /* OUT */)
    567 {
    568  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
    569  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    570  unsigned int i;
    571 
    572  for (i = 0; i < script_count; i++)
    573  {
    574    if (g.find_script_index (script_tags[i], script_index))
    575    {
    576      if (chosen_script)
    577 *chosen_script = script_tags[i];
    578      return true;
    579    }
    580  }
    581 
    582  /* try finding 'DFLT' */
    583  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
    584    if (chosen_script)
    585      *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
    586    return false;
    587  }
    588 
    589  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
    590  if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
    591    if (chosen_script)
    592      *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
    593    return false;
    594  }
    595 
    596  /* try with 'latn'; some old fonts put their features there even though
    597     they're really trying to support Thai, for example :( */
    598  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
    599    if (chosen_script)
    600      *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
    601    return false;
    602  }
    603 
    604  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
    605  if (chosen_script)
    606    *chosen_script = HB_TAG_NONE;
    607  return false;
    608 }
    609 
    610 
    611 /**
    612 * hb_ot_layout_table_get_feature_tags:
    613 * @face: #hb_face_t to work upon
    614 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    615 * @start_offset: offset of the first feature tag to retrieve
    616 * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
    617 *                 Output = the actual number of feature tags returned (may be zero)
    618 * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table
    619 *
    620 * Fetches a list of all feature tags in the given face's GSUB or GPOS table.
    621 * Note that there might be duplicate feature tags, belonging to different
    622 * script/language-system pairs of the table.
    623 *
    624 * Return value: Total number of feature tags.
    625 *
    626 * Since: 0.6.0
    627 *
    628 **/
    629 unsigned int
    630 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
    631 			     hb_tag_t      table_tag,
    632 			     unsigned int  start_offset,
    633 			     unsigned int *feature_count /* IN/OUT */,
    634 			     hb_tag_t     *feature_tags  /* OUT */)
    635 {
    636  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    637 
    638  return g.get_feature_tags (start_offset, feature_count, feature_tags);
    639 }
    640 
    641 
    642 /**
    643 * hb_ot_layout_table_find_feature:
    644 * @face: #hb_face_t to work upon
    645 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    646 * @feature_tag: The #hb_tag_t of the requested feature tag
    647 * @feature_index: (out): The index of the requested feature
    648 *
    649 * Fetches the index for a given feature tag in the specified face's GSUB table
    650 * or GPOS table.
    651 *
    652 * Return value: `true` if the feature is found, `false` otherwise
    653 *
    654 * Since: 0.6.0
    655 *
    656 **/
    657 bool
    658 hb_ot_layout_table_find_feature (hb_face_t    *face,
    659 			 hb_tag_t      table_tag,
    660 			 hb_tag_t      feature_tag,
    661 			 unsigned int *feature_index /* OUT */)
    662 {
    663  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
    664  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    665 
    666  unsigned int num_features = g.get_feature_count ();
    667  for (unsigned int i = 0; i < num_features; i++)
    668  {
    669    if (feature_tag == g.get_feature_tag (i)) {
    670      if (feature_index) *feature_index = i;
    671      return true;
    672    }
    673  }
    674 
    675  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
    676  return false;
    677 }
    678 
    679 
    680 /**
    681 * hb_ot_layout_script_get_language_tags:
    682 * @face: #hb_face_t to work upon
    683 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    684 * @script_index: The index of the requested script tag
    685 * @start_offset: offset of the first language tag to retrieve
    686 * @language_count: (inout) (nullable): Input = the maximum number of language tags to return;
    687 *                  Output = the actual number of language tags returned (may be zero)
    688 * @language_tags: (out) (array length=language_count): Array of language tags found in the table
    689 *
    690 * Fetches a list of language tags in the given face's GSUB or GPOS table, underneath
    691 * the specified script index. The list returned will begin at the offset provided.
    692 *
    693 * Return value: Total number of language tags.
    694 *
    695 * Since: 0.6.0
    696 *
    697 **/
    698 unsigned int
    699 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
    700 			       hb_tag_t      table_tag,
    701 			       unsigned int  script_index,
    702 			       unsigned int  start_offset,
    703 			       unsigned int *language_count /* IN/OUT */,
    704 			       hb_tag_t     *language_tags  /* OUT */)
    705 {
    706  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
    707 
    708  return s.get_lang_sys_tags (start_offset, language_count, language_tags);
    709 }
    710 
    711 
    712 #ifndef HB_DISABLE_DEPRECATED
    713 /**
    714 * hb_ot_layout_script_find_language:
    715 * @face: #hb_face_t to work upon
    716 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    717 * @script_index: The index of the requested script tag
    718 * @language_tag: The #hb_tag_t of the requested language
    719 * @language_index: The index of the requested language
    720 *
    721 * Fetches the index of a given language tag in the specified face's GSUB table
    722 * or GPOS table, underneath the specified script tag.
    723 *
    724 * Return value: `true` if the language tag is found, `false` otherwise
    725 *
    726 * Since: 0.6.0
    727 * Deprecated: 2.0.0
    728 **/
    729 hb_bool_t
    730 hb_ot_layout_script_find_language (hb_face_t    *face,
    731 			   hb_tag_t      table_tag,
    732 			   unsigned int  script_index,
    733 			   hb_tag_t      language_tag,
    734 			   unsigned int *language_index)
    735 {
    736  return hb_ot_layout_script_select_language (face,
    737 				      table_tag,
    738 				      script_index,
    739 				      1,
    740 				      &language_tag,
    741 				      language_index);
    742 }
    743 #endif
    744 
    745 
    746 /**
    747 * hb_ot_layout_script_select_language2:
    748 * @face: #hb_face_t to work upon
    749 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    750 * @script_index: The index of the requested script tag
    751 * @language_count: The number of languages in the specified script
    752 * @language_tags: The array of language tags
    753 * @language_index: (out): The index of the chosen language
    754 * @chosen_language: (out): #hb_tag_t of the chosen language
    755 *
    756 * Fetches the index of the first language tag fom @language_tags that is present
    757 * in the specified face's GSUB or GPOS table, underneath the specified script
    758 * index.
    759 *
    760 * If none of the given language tags is found, `false` is returned and
    761 * @language_index is set to #HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX and
    762 * @chosen_language is set to #HB_TAG_NONE.
    763 *
    764 * Return value: `true` if one of the given language tags is found, `false` otherwise
    765 *
    766 * Since: 7.0.0
    767 **/
    768 hb_bool_t
    769 hb_ot_layout_script_select_language2 (hb_face_t      *face,
    770 			     hb_tag_t        table_tag,
    771 			     unsigned int    script_index,
    772 			     unsigned int    language_count,
    773 			     const hb_tag_t *language_tags,
    774 			     unsigned int   *language_index /* OUT */,
    775 			     hb_tag_t       *chosen_language /* OUT */)
    776 {
    777  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX), "");
    778  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
    779  unsigned int i;
    780 
    781  for (i = 0; i < language_count; i++)
    782  {
    783    if (s.find_lang_sys_index (language_tags[i], language_index))
    784    {
    785      if (chosen_language)
    786        *chosen_language = language_tags[i];
    787      return true;
    788    }
    789  }
    790 
    791  /* try finding 'dflt' */
    792  if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
    793  {
    794    if (chosen_language)
    795      *chosen_language = HB_OT_TAG_DEFAULT_LANGUAGE;
    796    return false;
    797  }
    798 
    799  if (language_index)
    800    *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
    801  if (chosen_language)
    802    *chosen_language = HB_TAG_NONE;
    803  return false;
    804 }
    805 
    806 /**
    807 * hb_ot_layout_script_select_language:
    808 * @face: #hb_face_t to work upon
    809 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    810 * @script_index: The index of the requested script tag
    811 * @language_count: The number of languages in the specified script
    812 * @language_tags: The array of language tags
    813 * @language_index: (out): The index of the requested language
    814 *
    815 * Fetches the index of the first language tag fom @language_tags that is present
    816 * in the specified face's GSUB or GPOS table, underneath the specified script
    817 * index.
    818 *
    819 * If none of the given language tags is found, `false` is returned and
    820 * @language_index is set to the default language index.
    821 *
    822 * Return value: `true` if one of the given language tags is found, `false` otherwise
    823 *
    824 * Since: 2.0.0
    825 **/
    826 hb_bool_t
    827 hb_ot_layout_script_select_language (hb_face_t      *face,
    828 			     hb_tag_t        table_tag,
    829 			     unsigned int    script_index,
    830 			     unsigned int    language_count,
    831 			     const hb_tag_t *language_tags,
    832 			     unsigned int   *language_index /* OUT */)
    833 {
    834  return hb_ot_layout_script_select_language2 (face, table_tag,
    835 				       script_index,
    836 				       language_count, language_tags,
    837 				       language_index, nullptr);
    838 }
    839 
    840 /**
    841 * hb_ot_layout_language_get_required_feature_index:
    842 * @face: #hb_face_t to work upon
    843 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    844 * @script_index: The index of the requested script tag
    845 * @language_index: The index of the requested language tag
    846 * @feature_index: (out): The index of the requested feature
    847 *
    848 * Fetches the index of a requested feature in the given face's GSUB or GPOS table,
    849 * underneath the specified script and language.
    850 *
    851 * Return value: `true` if the feature is found, `false` otherwise
    852 *
    853 * Since: 0.6.0
    854 *
    855 **/
    856 hb_bool_t
    857 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
    858 					  hb_tag_t      table_tag,
    859 					  unsigned int  script_index,
    860 					  unsigned int  language_index,
    861 					  unsigned int *feature_index /* OUT */)
    862 {
    863  return hb_ot_layout_language_get_required_feature (face,
    864 					     table_tag,
    865 					     script_index,
    866 					     language_index,
    867 					     feature_index,
    868 					     nullptr);
    869 }
    870 
    871 
    872 /**
    873 * hb_ot_layout_language_get_required_feature:
    874 * @face: #hb_face_t to work upon
    875 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    876 * @script_index: The index of the requested script tag
    877 * @language_index: The index of the requested language tag
    878 * @feature_index: (out): The index of the requested feature
    879 * @feature_tag: (out): The #hb_tag_t of the requested feature
    880 *
    881 * Fetches the tag of a requested feature index in the given face's GSUB or GPOS table,
    882 * underneath the specified script and language.
    883 *
    884 * Return value: `true` if the feature is found, `false` otherwise
    885 *
    886 * Since: 0.9.30
    887 **/
    888 hb_bool_t
    889 hb_ot_layout_language_get_required_feature (hb_face_t    *face,
    890 				    hb_tag_t      table_tag,
    891 				    unsigned int  script_index,
    892 				    unsigned int  language_index,
    893 				    unsigned int *feature_index /* OUT */,
    894 				    hb_tag_t     *feature_tag   /* OUT */)
    895 {
    896  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    897  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    898 
    899  unsigned int index = l.get_required_feature_index ();
    900  if (feature_index) *feature_index = index;
    901  if (feature_tag) *feature_tag = g.get_feature_tag (index);
    902 
    903  return l.has_required_feature ();
    904 }
    905 
    906 
    907 /**
    908 * hb_ot_layout_language_get_feature_indexes:
    909 * @face: #hb_face_t to work upon
    910 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    911 * @script_index: The index of the requested script tag
    912 * @language_index: The index of the requested language tag
    913 * @start_offset: offset of the first feature tag to retrieve
    914 * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
    915 *                 Output: the actual number of feature tags returned (may be zero)
    916 * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query
    917 *
    918 * Fetches a list of all features in the specified face's GSUB table
    919 * or GPOS table, underneath the specified script and language. The list
    920 * returned will begin at the offset provided.
    921 *
    922 * Return value: Total number of features.
    923 *
    924 * Since: 0.6.0
    925 *
    926 **/
    927 unsigned int
    928 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
    929 				   hb_tag_t      table_tag,
    930 				   unsigned int  script_index,
    931 				   unsigned int  language_index,
    932 				   unsigned int  start_offset,
    933 				   unsigned int *feature_count   /* IN/OUT */,
    934 				   unsigned int *feature_indexes /* OUT */)
    935 {
    936  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    937  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    938 
    939  return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
    940 }
    941 
    942 
    943 /**
    944 * hb_ot_layout_language_get_feature_tags:
    945 * @face: #hb_face_t to work upon
    946 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    947 * @script_index: The index of the requested script tag
    948 * @language_index: The index of the requested language tag
    949 * @start_offset: offset of the first feature tag to retrieve
    950 * @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
    951 *                 Output = the actual number of feature tags returned (may be zero)
    952 * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query
    953 *
    954 * Fetches a list of all features in the specified face's GSUB table
    955 * or GPOS table, underneath the specified script and language. The list
    956 * returned will begin at the offset provided.
    957 *
    958 * Return value: Total number of feature tags.
    959 *
    960 * Since: 0.6.0
    961 *
    962 **/
    963 unsigned int
    964 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
    965 				hb_tag_t      table_tag,
    966 				unsigned int  script_index,
    967 				unsigned int  language_index,
    968 				unsigned int  start_offset,
    969 				unsigned int *feature_count /* IN/OUT */,
    970 				hb_tag_t     *feature_tags  /* OUT */)
    971 {
    972  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
    973  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
    974 
    975  static_assert ((sizeof (unsigned int) == sizeof (hb_tag_t)), "");
    976  unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
    977 
    978  if (feature_tags) {
    979    unsigned int count = *feature_count;
    980    for (unsigned int i = 0; i < count; i++)
    981      feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
    982  }
    983 
    984  return ret;
    985 }
    986 
    987 
    988 /**
    989 * hb_ot_layout_language_find_feature:
    990 * @face: #hb_face_t to work upon
    991 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
    992 * @script_index: The index of the requested script tag
    993 * @language_index: The index of the requested language tag
    994 * @feature_tag: #hb_tag_t of the feature tag requested
    995 * @feature_index: (out): The index of the requested feature
    996 *
    997 * Fetches the index of a given feature tag in the specified face's GSUB table
    998 * or GPOS table, underneath the specified script and language.
    999 *
   1000 * Return value: `true` if the feature is found, `false` otherwise
   1001 *
   1002 * Since: 0.6.0
   1003 *
   1004 **/
   1005 hb_bool_t
   1006 hb_ot_layout_language_find_feature (hb_face_t    *face,
   1007 			    hb_tag_t      table_tag,
   1008 			    unsigned int  script_index,
   1009 			    unsigned int  language_index,
   1010 			    hb_tag_t      feature_tag,
   1011 			    unsigned int *feature_index /* OUT */)
   1012 {
   1013  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
   1014  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   1015  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
   1016 
   1017  unsigned int num_features = l.get_feature_count ();
   1018  for (unsigned int i = 0; i < num_features; i++) {
   1019    unsigned int f_index = l.get_feature_index (i);
   1020 
   1021    if (feature_tag == g.get_feature_tag (f_index)) {
   1022      if (feature_index) *feature_index = f_index;
   1023      return true;
   1024    }
   1025  }
   1026 
   1027  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
   1028  return false;
   1029 }
   1030 
   1031 
   1032 /**
   1033 * hb_ot_layout_feature_get_lookups:
   1034 * @face: #hb_face_t to work upon
   1035 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1036 * @feature_index: The index of the requested feature
   1037 * @start_offset: offset of the first lookup to retrieve
   1038 * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return;
   1039 *                Output = the actual number of lookups returned (may be zero)
   1040 * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query
   1041 *
   1042 * Fetches a list of all lookups enumerated for the specified feature, in
   1043 * the specified face's GSUB table or GPOS table. The list returned will
   1044 * begin at the offset provided.
   1045 *
   1046 * Return value: Total number of lookups.
   1047 *
   1048 * Since: 0.9.7
   1049 **/
   1050 unsigned int
   1051 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
   1052 			  hb_tag_t      table_tag,
   1053 			  unsigned int  feature_index,
   1054 			  unsigned int  start_offset,
   1055 			  unsigned int *lookup_count   /* IN/OUT */,
   1056 			  unsigned int *lookup_indexes /* OUT */)
   1057 {
   1058  return hb_ot_layout_feature_with_variations_get_lookups (face,
   1059 						   table_tag,
   1060 						   feature_index,
   1061 						   HB_OT_LAYOUT_NO_VARIATIONS_INDEX,
   1062 						   start_offset,
   1063 						   lookup_count,
   1064 						   lookup_indexes);
   1065 }
   1066 
   1067 
   1068 /**
   1069 * hb_ot_layout_table_get_lookup_count:
   1070 * @face: #hb_face_t to work upon
   1071 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1072 *
   1073 * Fetches the total number of lookups enumerated in the specified
   1074 * face's GSUB table or GPOS table.
   1075 *
   1076 * Return value: Total number of lookups.
   1077 *
   1078 * Since: 0.9.22
   1079 **/
   1080 unsigned int
   1081 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
   1082 			     hb_tag_t      table_tag)
   1083 {
   1084  return get_gsubgpos_table (face, table_tag).get_lookup_count ();
   1085 }
   1086 
   1087 
   1088 struct hb_collect_features_context_t
   1089 {
   1090  hb_collect_features_context_t (hb_face_t *face,
   1091 			 hb_tag_t   table_tag,
   1092 			 hb_set_t  *feature_indices_,
   1093 			 const hb_tag_t *features)
   1094 
   1095    : g (get_gsubgpos_table (face, table_tag)),
   1096      feature_indices (feature_indices_),
   1097      has_feature_filter (false),
   1098      script_count (0),langsys_count (0), feature_index_count (0)
   1099  {
   1100    compute_feature_filter (features);
   1101  }
   1102 
   1103  void compute_feature_filter (const hb_tag_t *features)
   1104  {
   1105    if (features == nullptr)
   1106    {
   1107      has_feature_filter = false;
   1108      return;
   1109    }
   1110 
   1111    has_feature_filter = true;
   1112    hb_set_t features_set;
   1113    for (; *features; features++)
   1114      features_set.add (*features);
   1115 
   1116    for (unsigned i = 0; i < g.get_feature_count (); i++)
   1117    {
   1118      hb_tag_t tag = g.get_feature_tag (i);
   1119      if (features_set.has (tag))
   1120 feature_indices_filter.add(i);
   1121    }
   1122  }
   1123 
   1124  bool visited (const OT::Script &s)
   1125  {
   1126    /* We might have Null() object here.  Don't want to involve
   1127     * that in the memoize.  So, detect empty objects and return. */
   1128    if (unlikely (!s.has_default_lang_sys () &&
   1129 	  !s.get_lang_sys_count ()))
   1130      return true;
   1131 
   1132    if (script_count++ > HB_MAX_SCRIPTS)
   1133      return true;
   1134 
   1135    return visited (s, visited_script);
   1136  }
   1137  bool visited (const OT::LangSys &l)
   1138  {
   1139    /* We might have Null() object here.  Don't want to involve
   1140     * that in the memoize.  So, detect empty objects and return. */
   1141    if (unlikely (!l.has_required_feature () &&
   1142 	  !l.get_feature_count ()))
   1143      return true;
   1144 
   1145    if (langsys_count++ > HB_MAX_LANGSYS)
   1146      return true;
   1147 
   1148    return visited (l, visited_langsys);
   1149  }
   1150 
   1151  bool visited_feature_indices (unsigned count)
   1152  {
   1153    feature_index_count += count;
   1154    return feature_index_count > HB_MAX_FEATURE_INDICES;
   1155  }
   1156 
   1157  private:
   1158  template <typename T>
   1159  bool visited (const T &p, hb_set_t &visited_set)
   1160  {
   1161    hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g);
   1162     if (visited_set.has (delta))
   1163      return true;
   1164 
   1165    visited_set.add (delta);
   1166    return false;
   1167  }
   1168 
   1169  public:
   1170  const OT::GSUBGPOS &g;
   1171  hb_set_t *feature_indices;
   1172  hb_set_t  feature_indices_filter;
   1173  bool has_feature_filter;
   1174 
   1175  private:
   1176  hb_set_t visited_script;
   1177  hb_set_t visited_langsys;
   1178  unsigned int script_count;
   1179  unsigned int langsys_count;
   1180  unsigned int feature_index_count;
   1181 };
   1182 
   1183 static void
   1184 langsys_collect_features (hb_collect_features_context_t *c,
   1185 		  const OT::LangSys  &l)
   1186 {
   1187  if (c->visited (l)) return;
   1188 
   1189  if (!c->has_feature_filter)
   1190  {
   1191    /* All features. */
   1192    if (l.has_required_feature () && !c->visited_feature_indices (1))
   1193      c->feature_indices->add (l.get_required_feature_index ());
   1194 
   1195    // TODO(garretrieger): filter out indices >= feature count?
   1196    if (!c->visited_feature_indices (l.featureIndex.len))
   1197      l.add_feature_indexes_to (c->feature_indices);
   1198  }
   1199  else
   1200  {
   1201    if (c->feature_indices_filter.is_empty()) return;
   1202    unsigned int num_features = l.get_feature_count ();
   1203    for (unsigned int i = 0; i < num_features; i++)
   1204    {
   1205      unsigned int feature_index = l.get_feature_index (i);
   1206      if (!c->feature_indices_filter.has (feature_index)) continue;
   1207 
   1208      c->feature_indices->add (feature_index);
   1209      c->feature_indices_filter.del (feature_index);
   1210    }
   1211  }
   1212 }
   1213 
   1214 static void
   1215 script_collect_features (hb_collect_features_context_t *c,
   1216 		 const OT::Script   &s,
   1217 		 const hb_tag_t *languages)
   1218 {
   1219  if (c->visited (s)) return;
   1220 
   1221  if (!languages)
   1222  {
   1223    /* All languages. */
   1224    if (s.has_default_lang_sys ())
   1225      langsys_collect_features (c,
   1226 			s.get_default_lang_sys ());
   1227 
   1228 
   1229    unsigned int count = s.get_lang_sys_count ();
   1230    for (unsigned int language_index = 0; language_index < count; language_index++)
   1231      langsys_collect_features (c,
   1232 			s.get_lang_sys (language_index));
   1233  }
   1234  else
   1235  {
   1236    for (; *languages; languages++)
   1237    {
   1238      unsigned int language_index;
   1239      if (s.find_lang_sys_index (*languages, &language_index))
   1240 langsys_collect_features (c,
   1241 			  s.get_lang_sys (language_index));
   1242 
   1243    }
   1244  }
   1245 }
   1246 
   1247 
   1248 /**
   1249 * hb_ot_layout_collect_features:
   1250 * @face: #hb_face_t to work upon
   1251 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1252 * @scripts: (nullable) (array zero-terminated=1): The array of scripts to collect features for,
   1253 *   terminated by %HB_TAG_NONE
   1254 * @languages: (nullable) (array zero-terminated=1): The array of languages to collect features for,
   1255 *   terminated by %HB_TAG_NONE
   1256 * @features: (nullable) (array zero-terminated=1): The array of features to collect,
   1257 *   terminated by %HB_TAG_NONE
   1258 * @feature_indexes: (out): The set of feature indexes found for the query
   1259 *
   1260 * Fetches a list of all feature indexes in the specified face's GSUB table
   1261 * or GPOS table, underneath the specified scripts, languages, and features.
   1262 * If no list of scripts is provided, all scripts will be queried. If no list
   1263 * of languages is provided, all languages will be queried. If no list of
   1264 * features is provided, all features will be queried.
   1265 *
   1266 * Since: 1.8.5
   1267 **/
   1268 void
   1269 hb_ot_layout_collect_features (hb_face_t      *face,
   1270 		       hb_tag_t        table_tag,
   1271 		       const hb_tag_t *scripts,
   1272 		       const hb_tag_t *languages,
   1273 		       const hb_tag_t *features,
   1274 		       hb_set_t       *feature_indexes /* OUT */)
   1275 {
   1276  hb_collect_features_context_t c (face, table_tag, feature_indexes, features);
   1277  if (!scripts)
   1278  {
   1279    /* All scripts. */
   1280    unsigned int count = c.g.get_script_count ();
   1281    for (unsigned int script_index = 0; script_index < count; script_index++)
   1282      script_collect_features (&c,
   1283 		       c.g.get_script (script_index),
   1284 		       languages);
   1285  }
   1286  else
   1287  {
   1288    for (; *scripts; scripts++)
   1289    {
   1290      unsigned int script_index;
   1291      if (c.g.find_script_index (*scripts, &script_index))
   1292 script_collect_features (&c,
   1293 			 c.g.get_script (script_index),
   1294 			 languages);
   1295    }
   1296  }
   1297 }
   1298 
   1299 /**
   1300 * hb_ot_layout_collect_features_map:
   1301 * @face: #hb_face_t to work upon
   1302 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1303 * @script_index: The index of the requested script tag
   1304 * @language_index: The index of the requested language tag
   1305 * @feature_map: (out): The map of feature tag to feature index.
   1306 *
   1307 * Fetches the mapping from feature tags to feature indexes for
   1308 * the specified script and language.
   1309 *
   1310 * Since: 8.1.0
   1311 **/
   1312 void
   1313 hb_ot_layout_collect_features_map (hb_face_t      *face,
   1314 			   hb_tag_t        table_tag,
   1315 			   unsigned        script_index,
   1316 			   unsigned        language_index,
   1317 			   hb_map_t       *feature_map /* OUT */)
   1318 {
   1319  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   1320  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
   1321 
   1322  unsigned int count = l.get_feature_indexes (0, nullptr, nullptr);
   1323  feature_map->alloc (count);
   1324 
   1325  /* Loop in reverse, such that earlier entries win. That emulates
   1326   * a linear search, which seems to be what other implementations do.
   1327   * We found that with arialuni_t.ttf, the "ur" language system has
   1328   * duplicate features, and the earlier ones work but not later ones.
   1329   */
   1330  for (unsigned int i = count; i; i--)
   1331  {
   1332    unsigned feature_index = 0;
   1333    unsigned feature_count = 1;
   1334    l.get_feature_indexes (i - 1, &feature_count, &feature_index);
   1335    if (!feature_count)
   1336      break;
   1337    hb_tag_t feature_tag = g.get_feature_tag (feature_index);
   1338    feature_map->set (feature_tag, feature_index);
   1339  }
   1340 }
   1341 
   1342 
   1343 /**
   1344 * hb_ot_layout_collect_lookups:
   1345 * @face: #hb_face_t to work upon
   1346 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1347 * @scripts: (nullable) (array zero-terminated=1): The array of scripts to collect lookups for,
   1348 *   terminated by %HB_TAG_NONE
   1349 * @languages: (nullable) (array zero-terminated=1): The array of languages to collect lookups for,
   1350 *   terminated by %HB_TAG_NONE
   1351 * @features: (nullable) (array zero-terminated=1): The array of features to collect lookups for,
   1352 *   terminated by %HB_TAG_NONE
   1353 * @lookup_indexes: (out): The array of lookup indexes found for the query
   1354 *
   1355 * Fetches a list of all feature-lookup indexes in the specified face's GSUB
   1356 * table or GPOS table, underneath the specified scripts, languages, and
   1357 * features. If no list of scripts is provided, all scripts will be queried.
   1358 * If no list of languages is provided, all languages will be queried. If no
   1359 * list of features is provided, all features will be queried.
   1360 *
   1361 * Since: 0.9.8
   1362 **/
   1363 void
   1364 hb_ot_layout_collect_lookups (hb_face_t      *face,
   1365 		      hb_tag_t        table_tag,
   1366 		      const hb_tag_t *scripts,
   1367 		      const hb_tag_t *languages,
   1368 		      const hb_tag_t *features,
   1369 		      hb_set_t       *lookup_indexes /* OUT */)
   1370 {
   1371  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   1372 
   1373  hb_set_t feature_indexes;
   1374  hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
   1375 
   1376  for (auto feature_index : feature_indexes)
   1377    g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
   1378 
   1379  g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes);
   1380 }
   1381 
   1382 
   1383 #ifndef HB_NO_LAYOUT_COLLECT_GLYPHS
   1384 /**
   1385 * hb_ot_layout_lookup_collect_glyphs:
   1386 * @face: #hb_face_t to work upon
   1387 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1388 * @lookup_index: The index of the feature lookup to query
   1389 * @glyphs_before: (out) (nullable): Array of glyphs preceding the substitution range
   1390 * @glyphs_input: (out) (nullable): Array of input glyphs that would be substituted by the lookup
   1391 * @glyphs_after: (out) (nullable): Array of glyphs following the substitution range
   1392 * @glyphs_output: (out) (nullable): Array of glyphs that would be the substituted output of the lookup
   1393 *
   1394 * Fetches a list of all glyphs affected by the specified lookup in the
   1395 * specified face's GSUB table or GPOS table.
   1396 *
   1397 * Since: 0.9.7
   1398 **/
   1399 void
   1400 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
   1401 			    hb_tag_t      table_tag,
   1402 			    unsigned int  lookup_index,
   1403 			    hb_set_t     *glyphs_before, /* OUT.  May be NULL */
   1404 			    hb_set_t     *glyphs_input,  /* OUT.  May be NULL */
   1405 			    hb_set_t     *glyphs_after,  /* OUT.  May be NULL */
   1406 			    hb_set_t     *glyphs_output  /* OUT.  May be NULL */)
   1407 {
   1408  OT::hb_collect_glyphs_context_t c (face,
   1409 			     glyphs_before,
   1410 			     glyphs_input,
   1411 			     glyphs_after,
   1412 			     glyphs_output);
   1413 
   1414  switch (table_tag)
   1415  {
   1416    case HB_OT_TAG_GSUB:
   1417    {
   1418      const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
   1419      l.collect_glyphs (&c);
   1420      return;
   1421    }
   1422    case HB_OT_TAG_GPOS:
   1423    {
   1424      const OT::PosLookup& l = face->table.GPOS->table->get_lookup (lookup_index);
   1425      l.collect_glyphs (&c);
   1426      return;
   1427    }
   1428  }
   1429 }
   1430 #endif
   1431 
   1432 
   1433 /* Variations support */
   1434 
   1435 
   1436 /**
   1437 * hb_ot_layout_table_find_feature_variations:
   1438 * @face: #hb_face_t to work upon
   1439 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1440 * @coords: The variation coordinates to query
   1441 * @num_coords: The number of variation coordinates
   1442 * @variations_index: (out): The array of feature variations found for the query
   1443 *
   1444 * Fetches a list of feature variations in the specified face's GSUB table
   1445 * or GPOS table, at the specified variation coordinates.
   1446 *
   1447 * Return value: `true` if feature variations were found, `false` otherwise.
   1448 *
   1449 * Since: 1.4.0
   1450 *
   1451 **/
   1452 hb_bool_t
   1453 hb_ot_layout_table_find_feature_variations (hb_face_t    *face,
   1454 				    hb_tag_t      table_tag,
   1455 				    const int    *coords,
   1456 				    unsigned int  num_coords,
   1457 				    unsigned int *variations_index /* out */)
   1458 {
   1459  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   1460  const OT::GDEF &gdef = *face->table.GDEF->table;
   1461 
   1462  auto instancer = OT::ItemVarStoreInstancer(&gdef.get_var_store(), nullptr,
   1463 				     hb_array (coords, num_coords));
   1464 
   1465  return g.find_variations_index (coords, num_coords, variations_index, &instancer);
   1466 }
   1467 
   1468 
   1469 /**
   1470 * hb_ot_layout_feature_with_variations_get_lookups:
   1471 * @face: #hb_face_t to work upon
   1472 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
   1473 * @feature_index: The index of the feature to query
   1474 * @variations_index: The index of the feature variation to query
   1475 * @start_offset: offset of the first lookup to retrieve
   1476 * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return;
   1477 *                Output = the actual number of lookups returned (may be zero)
   1478 * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query
   1479 *
   1480 * Fetches a list of all lookups enumerated for the specified feature, in
   1481 * the specified face's GSUB table or GPOS table, enabled at the specified
   1482 * variations index. The list returned will begin at the offset provided.
   1483 *
   1484 * Return value: Total number of lookups.
   1485 *
   1486 * Since: 1.4.0
   1487 *
   1488 **/
   1489 unsigned int
   1490 hb_ot_layout_feature_with_variations_get_lookups (hb_face_t    *face,
   1491 					  hb_tag_t      table_tag,
   1492 					  unsigned int  feature_index,
   1493 					  unsigned int  variations_index,
   1494 					  unsigned int  start_offset,
   1495 					  unsigned int *lookup_count /* IN/OUT */,
   1496 					  unsigned int *lookup_indexes /* OUT */)
   1497 {
   1498  static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), "");
   1499  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   1500 
   1501  const OT::Feature &f = g.get_feature_variation (feature_index, variations_index);
   1502 
   1503  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
   1504 }
   1505 
   1506 
   1507 /*
   1508 * OT::GSUB
   1509 */
   1510 
   1511 
   1512 /**
   1513 * hb_ot_layout_has_substitution:
   1514 * @face: #hb_face_t to work upon
   1515 *
   1516 * Tests whether the specified face includes any GSUB substitutions.
   1517 *
   1518 * Return value: `true` if data found, `false` otherwise
   1519 *
   1520 * Since: 0.6.0
   1521 *
   1522 **/
   1523 hb_bool_t
   1524 hb_ot_layout_has_substitution (hb_face_t *face)
   1525 {
   1526  return face->table.GSUB->table->has_data ();
   1527 }
   1528 
   1529 
   1530 /**
   1531 * hb_ot_layout_lookup_would_substitute:
   1532 * @face: #hb_face_t to work upon
   1533 * @lookup_index: The index of the lookup to query
   1534 * @glyphs: The sequence of glyphs to query for substitution
   1535 * @glyphs_length: The length of the glyph sequence
   1536 * @zero_context: #hb_bool_t indicating whether pre-/post-context are disallowed
   1537 * in substitutions
   1538 *
   1539 * Tests whether a specified lookup in the specified face would
   1540 * trigger a substitution on the given glyph sequence.
   1541 *
   1542 * Return value: `true` if a substitution would be triggered, `false` otherwise
   1543 *
   1544 * Since: 0.9.7
   1545 **/
   1546 hb_bool_t
   1547 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
   1548 			      unsigned int          lookup_index,
   1549 			      const hb_codepoint_t *glyphs,
   1550 			      unsigned int          glyphs_length,
   1551 			      hb_bool_t             zero_context)
   1552 {
   1553  auto &gsub = face->table.GSUB;
   1554  if (unlikely (lookup_index >= gsub->lookup_count)) return false;
   1555  OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
   1556 
   1557  const OT::SubstLookup& l = gsub->table->get_lookup (lookup_index);
   1558  auto *accel = gsub->get_accel (lookup_index);
   1559  return accel && l.would_apply (&c, accel);
   1560 }
   1561 
   1562 
   1563 /**
   1564 * hb_ot_layout_substitute_start:
   1565 * @font: #hb_font_t to use
   1566 * @buffer: #hb_buffer_t buffer to work upon
   1567 *
   1568 * Called before substitution lookups are performed, to ensure that glyph
   1569 * class and other properties are set on the glyphs in the buffer.
   1570 *
   1571 **/
   1572 void
   1573 hb_ot_layout_substitute_start (hb_font_t    *font,
   1574 		       hb_buffer_t  *buffer)
   1575 {
   1576  _hb_ot_layout_set_glyph_props (font, buffer);
   1577 }
   1578 
   1579 /**
   1580 * hb_ot_layout_lookup_substitute_closure:
   1581 * @face: #hb_face_t to work upon
   1582 * @lookup_index: index of the feature lookup to query
   1583 * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookup
   1584 *
   1585 * Compute the transitive closure of glyphs needed for a
   1586 * specified lookup.
   1587 *
   1588 * Since: 0.9.7
   1589 **/
   1590 void
   1591 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
   1592 				unsigned int  lookup_index,
   1593 				hb_set_t     *glyphs /* OUT */)
   1594 {
   1595  hb_map_t done_lookups_glyph_count;
   1596  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set;
   1597  OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
   1598 
   1599  const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
   1600 
   1601  l.closure (&c, lookup_index);
   1602 }
   1603 
   1604 /**
   1605 * hb_ot_layout_lookups_substitute_closure:
   1606 * @face: #hb_face_t to work upon
   1607 * @lookups: The set of lookups to query
   1608 * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookups
   1609 *
   1610 * Compute the transitive closure of glyphs needed for all of the
   1611 * provided lookups.
   1612 *
   1613 * Since: 1.8.1
   1614 **/
   1615 void
   1616 hb_ot_layout_lookups_substitute_closure (hb_face_t      *face,
   1617 				 const hb_set_t *lookups,
   1618 				 hb_set_t       *glyphs /* OUT */)
   1619 {
   1620  hb_map_t done_lookups_glyph_count;
   1621  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set;
   1622  OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
   1623  const GSUB& gsub = *face->table.GSUB->table;
   1624 
   1625  unsigned int iteration_count = 0;
   1626  unsigned int glyphs_length;
   1627  do
   1628  {
   1629    c.reset_lookup_visit_count ();
   1630    glyphs_length = glyphs->get_population ();
   1631    if (lookups)
   1632    {
   1633      for (auto lookup_index : *lookups)
   1634 gsub.get_lookup (lookup_index).closure (&c, lookup_index);
   1635    }
   1636    else
   1637    {
   1638      for (unsigned int i = 0; i < gsub.get_lookup_count (); i++)
   1639 gsub.get_lookup (i).closure (&c, i);
   1640    }
   1641  } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES &&
   1642    glyphs_length != glyphs->get_population ());
   1643 }
   1644 
   1645 /*
   1646 * GPOS
   1647 */
   1648 
   1649 
   1650 /**
   1651 * hb_ot_layout_has_positioning:
   1652 * @face: #hb_face_t to work upon
   1653 *
   1654 * Tests whether the specified face includes any GPOS positioning.
   1655 *
   1656 * Return value: `true` if the face has GPOS data, `false` otherwise
   1657 *
   1658 **/
   1659 hb_bool_t
   1660 hb_ot_layout_has_positioning (hb_face_t *face)
   1661 {
   1662  return face->table.GPOS->table->has_data ();
   1663 }
   1664 
   1665 /**
   1666 * hb_ot_layout_position_start:
   1667 * @font: #hb_font_t to use
   1668 * @buffer: #hb_buffer_t buffer to work upon
   1669 *
   1670 * Called before positioning lookups are performed, to ensure that glyph
   1671 * attachment types and glyph-attachment chains are set for the glyphs in the buffer.
   1672 *
   1673 **/
   1674 void
   1675 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
   1676 {
   1677  GPOS::position_start (font, buffer);
   1678 }
   1679 
   1680 
   1681 /**
   1682 * hb_ot_layout_position_finish_advances:
   1683 * @font: #hb_font_t to use
   1684 * @buffer: #hb_buffer_t buffer to work upon
   1685 *
   1686 * Called after positioning lookups are performed, to finish glyph advances.
   1687 *
   1688 **/
   1689 void
   1690 hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
   1691 {
   1692  GPOS::position_finish_advances (font, buffer);
   1693 }
   1694 
   1695 /**
   1696 * hb_ot_layout_position_finish_offsets:
   1697 * @font: #hb_font_t to use
   1698 * @buffer: #hb_buffer_t buffer to work upon
   1699 *
   1700 * Called after positioning lookups are performed, to finish glyph offsets.
   1701 *
   1702 **/
   1703 void
   1704 hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
   1705 {
   1706  GPOS::position_finish_offsets (font, buffer);
   1707 }
   1708 
   1709 
   1710 #ifndef HB_NO_LAYOUT_FEATURE_PARAMS
   1711 /**
   1712 * hb_ot_layout_get_size_params:
   1713 * @face: #hb_face_t to work upon
   1714 * @design_size: (out): The design size of the face
   1715 * @subfamily_id: (out): The identifier of the face within the font subfamily
   1716 * @subfamily_name_id: (out): The ‘name’ table name ID of the face within the font subfamily
   1717 * @range_start: (out): The minimum size of the recommended size range for the face
   1718 * @range_end: (out): The maximum size of the recommended size range for the face
   1719 *
   1720 * Fetches optical-size feature data (i.e., the `size` feature from GPOS). Note that
   1721 * the subfamily_id and the subfamily name string (accessible via the subfamily_name_id)
   1722 * as used here are defined as pertaining only to fonts within a font family that differ
   1723 * specifically in their respective size ranges; other ways to differentiate fonts within
   1724 * a subfamily are not covered by the `size` feature.
   1725 *
   1726 * For more information on this distinction, see the [`size` feature documentation](
   1727 * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size).
   1728 *
   1729 * Return value: `true` if data found, `false` otherwise
   1730 *
   1731 * Since: 0.9.10
   1732 **/
   1733 hb_bool_t
   1734 hb_ot_layout_get_size_params (hb_face_t       *face,
   1735 		      unsigned int    *design_size,       /* OUT.  May be NULL */
   1736 		      unsigned int    *subfamily_id,      /* OUT.  May be NULL */
   1737 		      hb_ot_name_id_t *subfamily_name_id, /* OUT.  May be NULL */
   1738 		      unsigned int    *range_start,       /* OUT.  May be NULL */
   1739 		      unsigned int    *range_end          /* OUT.  May be NULL */)
   1740 {
   1741  const GPOS &gpos = *face->table.GPOS->table;
   1742  const hb_tag_t tag = HB_TAG ('s','i','z','e');
   1743 
   1744  unsigned int num_features = gpos.get_feature_count ();
   1745  for (unsigned int i = 0; i < num_features; i++)
   1746  {
   1747    if (tag == gpos.get_feature_tag (i))
   1748    {
   1749      const OT::Feature &f = gpos.get_feature (i);
   1750      const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
   1751 
   1752      if (params.designSize)
   1753      {
   1754 if (design_size) *design_size = params.designSize;
   1755 if (subfamily_id) *subfamily_id = params.subfamilyID;
   1756 if (subfamily_name_id) *subfamily_name_id = params.subfamilyNameID;
   1757 if (range_start) *range_start = params.rangeStart;
   1758 if (range_end) *range_end = params.rangeEnd;
   1759 
   1760 return true;
   1761      }
   1762    }
   1763  }
   1764 
   1765  if (design_size) *design_size = 0;
   1766  if (subfamily_id) *subfamily_id = 0;
   1767  if (subfamily_name_id) *subfamily_name_id = HB_OT_NAME_ID_INVALID;
   1768  if (range_start) *range_start = 0;
   1769  if (range_end) *range_end = 0;
   1770 
   1771  return false;
   1772 }
   1773 
   1774 
   1775 /**
   1776 * hb_ot_layout_feature_get_name_ids:
   1777 * @face: #hb_face_t to work upon
   1778 * @table_tag: table tag to query, "GSUB" or "GPOS".
   1779 * @feature_index: index of feature to query.
   1780 * @label_id: (out) (nullable): The ‘name’ table name ID that specifies a string
   1781 *            for a user-interface label for this feature.
   1782 * @tooltip_id: (out) (nullable): The ‘name’ table name ID that specifies a string
   1783 *              that an application can use for tooltip text for this
   1784 *              feature.
   1785 * @sample_id: (out) (nullable): The ‘name’ table name ID that specifies sample text
   1786 *             that illustrates the effect of this feature.
   1787 * @num_named_parameters: (out) (nullable):  Number of named parameters.
   1788 * @first_param_id: (out) (nullable): The first ‘name’ table name ID used to specify
   1789 *                  strings for user-interface labels for the feature
   1790 *                  parameters. (Must be zero if numParameters is zero.)
   1791 *
   1792 * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or
   1793 * "Character Variant" ('cvXX') features.
   1794 *
   1795 * Return value: `true` if data found, `false` otherwise
   1796 *
   1797 * Since: 2.0.0
   1798 **/
   1799 hb_bool_t
   1800 hb_ot_layout_feature_get_name_ids (hb_face_t       *face,
   1801 			   hb_tag_t         table_tag,
   1802 			   unsigned int     feature_index,
   1803 			   hb_ot_name_id_t *label_id,             /* OUT.  May be NULL */
   1804 			   hb_ot_name_id_t *tooltip_id,           /* OUT.  May be NULL */
   1805 			   hb_ot_name_id_t *sample_id,            /* OUT.  May be NULL */
   1806 			   unsigned int    *num_named_parameters, /* OUT.  May be NULL */
   1807 			   hb_ot_name_id_t *first_param_id        /* OUT.  May be NULL */)
   1808 {
   1809  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   1810 
   1811  hb_tag_t feature_tag = g.get_feature_tag (feature_index);
   1812  const OT::Feature &f = g.get_feature (feature_index);
   1813 
   1814  const OT::FeatureParams &feature_params = f.get_feature_params ();
   1815  if (&feature_params != &Null (OT::FeatureParams))
   1816  {
   1817    const OT::FeatureParamsStylisticSet& ss_params =
   1818      feature_params.get_stylistic_set_params (feature_tag);
   1819    if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */
   1820    {
   1821      if (label_id) *label_id = ss_params.uiNameID;
   1822      // ssXX features don't have the rest
   1823      if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
   1824      if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
   1825      if (num_named_parameters) *num_named_parameters = 0;
   1826      if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
   1827      return true;
   1828    }
   1829    const OT::FeatureParamsCharacterVariants& cv_params =
   1830      feature_params.get_character_variants_params (feature_tag);
   1831    if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */
   1832    {
   1833      if (label_id) *label_id = cv_params.featUILableNameID;
   1834      if (tooltip_id) *tooltip_id = cv_params.featUITooltipTextNameID;
   1835      if (sample_id) *sample_id = cv_params.sampleTextNameID;
   1836      if (num_named_parameters) *num_named_parameters = cv_params.numNamedParameters;
   1837      if (first_param_id) *first_param_id = cv_params.firstParamUILabelNameID;
   1838      return true;
   1839    }
   1840  }
   1841 
   1842  if (label_id) *label_id = HB_OT_NAME_ID_INVALID;
   1843  if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
   1844  if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
   1845  if (num_named_parameters) *num_named_parameters = 0;
   1846  if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
   1847  return false;
   1848 }
   1849 /**
   1850 * hb_ot_layout_feature_get_characters:
   1851 * @face: #hb_face_t to work upon
   1852 * @table_tag: table tag to query, "GSUB" or "GPOS".
   1853 * @feature_index: index of feature to query.
   1854 * @start_offset: offset of the first character to retrieve
   1855 * @char_count: (inout) (nullable): Input = the maximum number of characters to return;
   1856 *              Output = the actual number of characters returned (may be zero)
   1857 * @characters: (out caller-allocates) (array length=char_count): A buffer pointer.
   1858 *              The Unicode codepoints of the characters for which this feature provides
   1859 *               glyph variants.
   1860 *
   1861 * Fetches a list of the characters defined as having a variant under the specified
   1862 * "Character Variant" ("cvXX") feature tag.
   1863 *
   1864 * Return value: Number of total sample characters in the cvXX feature.
   1865 *
   1866 * Since: 2.0.0
   1867 **/
   1868 unsigned int
   1869 hb_ot_layout_feature_get_characters (hb_face_t      *face,
   1870 			     hb_tag_t        table_tag,
   1871 			     unsigned int    feature_index,
   1872 			     unsigned int    start_offset,
   1873 			     unsigned int   *char_count, /* IN/OUT.  May be NULL */
   1874 			     hb_codepoint_t *characters  /* OUT.     May be NULL */)
   1875 {
   1876  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
   1877  return g.get_feature (feature_index)
   1878   .get_feature_params ()
   1879   .get_character_variants_params(g.get_feature_tag (feature_index))
   1880   .get_characters (start_offset, char_count, characters);
   1881 }
   1882 #endif
   1883 
   1884 
   1885 /*
   1886 * Parts of different types are implemented here such that they have direct
   1887 * access to GSUB/GPOS lookups.
   1888 */
   1889 
   1890 
   1891 struct GSUBProxy
   1892 {
   1893  static constexpr unsigned table_index = 0u;
   1894  static constexpr bool always_inplace = false;
   1895  typedef OT::SubstLookup Lookup;
   1896 
   1897  GSUBProxy (hb_face_t *face) :
   1898    accel (*face->table.GSUB) {}
   1899 
   1900  const GSUB::accelerator_t &accel;
   1901 };
   1902 
   1903 struct GPOSProxy
   1904 {
   1905  static constexpr unsigned table_index = 1u;
   1906  static constexpr bool always_inplace = true;
   1907  typedef OT::PosLookup Lookup;
   1908 
   1909  GPOSProxy (hb_face_t *face) :
   1910    accel (*face->table.GPOS) {}
   1911 
   1912  const GPOS::accelerator_t &accel;
   1913 };
   1914 
   1915 
   1916 static inline bool
   1917 apply_forward (OT::hb_ot_apply_context_t *c,
   1918        const OT::hb_ot_layout_lookup_accelerator_t &accel)
   1919 {
   1920  bool use_hot_subtable_cache = accel.cache_enter (c);
   1921 
   1922  bool ret = false;
   1923  hb_buffer_t *buffer = c->buffer;
   1924  while (buffer->successful)
   1925  {
   1926    hb_glyph_info_t *info = buffer->info;
   1927    unsigned j = buffer->idx;
   1928    while (j < buffer->len &&
   1929    !(accel.digest.may_have (info[j].codepoint) &&
   1930      (info[j].mask & c->lookup_mask) &&
   1931      c->check_glyph_property (&info[j], c->lookup_props)))
   1932      j++;
   1933    if (unlikely (j > buffer->idx && !buffer->next_glyphs (j - buffer->idx)))
   1934      break;
   1935    if (buffer->idx >= buffer->len)
   1936      break;
   1937 
   1938    if (accel.apply (c, use_hot_subtable_cache))
   1939      ret = true;
   1940    else
   1941      (void) buffer->next_glyph ();
   1942  }
   1943 
   1944  if (use_hot_subtable_cache)
   1945    accel.cache_leave (c);
   1946 
   1947  return ret;
   1948 }
   1949 
   1950 static inline bool
   1951 apply_backward (OT::hb_ot_apply_context_t *c,
   1952        const OT::hb_ot_layout_lookup_accelerator_t &accel)
   1953 {
   1954  bool ret = false;
   1955  hb_buffer_t *buffer = c->buffer;
   1956  do
   1957  {
   1958    auto &cur = buffer->cur();
   1959    if (accel.digest.may_have (cur.codepoint) &&
   1960 (cur.mask & c->lookup_mask) &&
   1961 c->check_glyph_property (&cur, c->lookup_props))
   1962      ret |= accel.apply (c, false);
   1963 
   1964    /* The reverse lookup doesn't "advance" cursor (for good reason). */
   1965    buffer->idx--;
   1966  }
   1967  while ((int) buffer->idx >= 0);
   1968  return ret;
   1969 }
   1970 
   1971 template <typename Proxy>
   1972 static inline bool
   1973 apply_string (OT::hb_ot_apply_context_t *c,
   1974       const typename Proxy::Lookup &lookup,
   1975       const OT::hb_ot_layout_lookup_accelerator_t &accel)
   1976 {
   1977  hb_buffer_t *buffer = c->buffer;
   1978 
   1979  if (unlikely (!buffer->len || !c->lookup_mask))
   1980    return false;
   1981 
   1982  bool ret = false;
   1983 
   1984  c->set_lookup_props (lookup.get_props ());
   1985 
   1986  if (likely (!lookup.is_reverse ()))
   1987  {
   1988    /* in/out forward substitution/positioning */
   1989    if (!Proxy::always_inplace)
   1990      buffer->clear_output ();
   1991 
   1992    buffer->idx = 0;
   1993    ret = apply_forward (c, accel);
   1994 
   1995    if (!Proxy::always_inplace)
   1996      buffer->sync ();
   1997  }
   1998  else
   1999  {
   2000    /* in-place backward substitution/positioning */
   2001    assert (!buffer->have_output);
   2002    buffer->idx = buffer->len - 1;
   2003    ret = apply_backward (c, accel);
   2004  }
   2005 
   2006  return ret;
   2007 }
   2008 
   2009 template <typename Proxy>
   2010 inline void hb_ot_map_t::apply (const Proxy &proxy,
   2011 			const hb_ot_shape_plan_t *plan,
   2012 			hb_font_t *font,
   2013 			hb_buffer_t *buffer) const
   2014 {
   2015  const unsigned int table_index = proxy.table_index;
   2016  unsigned int i = 0;
   2017 
   2018  auto *font_data = font->data.ot.get ();
   2019  auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data;
   2020 
   2021  OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache);
   2022  c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
   2023 
   2024  for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
   2025  {
   2026    const stage_map_t *stage = &stages[table_index][stage_index];
   2027    for (; i < stage->last_lookup; i++)
   2028    {
   2029      auto &lookup = lookups[table_index][i];
   2030 
   2031      unsigned int lookup_index = lookup.index;
   2032 
   2033      auto *accel = proxy.accel.get_accel (lookup_index);
   2034      if (unlikely (!accel)) continue;
   2035 
   2036      if (buffer->messaging () &&
   2037   !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue;
   2038 
   2039      /* Only try applying the lookup if there is any overlap. */
   2040      if (accel->digest.may_intersect (buffer->digest))
   2041      {
   2042 c.set_lookup_index (lookup_index);
   2043 c.set_lookup_mask (lookup.mask, false);
   2044 c.set_auto_zwj (lookup.auto_zwj, false);
   2045 c.set_auto_zwnj (lookup.auto_zwnj, false);
   2046 c.set_random (lookup.random);
   2047 c.set_per_syllable (lookup.per_syllable, false);
   2048 /* apply_string's set_lookup_props initializes the iterators. */
   2049 
   2050 apply_string<Proxy> (&c,
   2051 		     proxy.accel.table->get_lookup (lookup_index),
   2052 		     *accel);
   2053      }
   2054      else if (buffer->messaging ())
   2055 (void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag));
   2056 
   2057      if (buffer->messaging ())
   2058 (void) buffer->message (font, "end lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag));
   2059    }
   2060 
   2061    if (stage->pause_func)
   2062    {
   2063      if (stage->pause_func (plan, font, buffer))
   2064      {
   2065 /* Refresh working buffer digest since buffer changed. */
   2066 buffer->update_digest ();
   2067      }
   2068    }
   2069  }
   2070 }
   2071 
   2072 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
   2073 {
   2074  GSUBProxy proxy (font->face);
   2075  if (buffer->messaging () &&
   2076      !buffer->message (font, "start table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]))) return;
   2077  apply (proxy, plan, font, buffer);
   2078  if (buffer->messaging ())
   2079    (void) buffer->message (font, "end table GSUB script tag '%c%c%c%c'", HB_UNTAG (chosen_script[0]));
   2080 }
   2081 
   2082 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
   2083 {
   2084  GPOSProxy proxy (font->face);
   2085  if (buffer->messaging () &&
   2086      !buffer->message (font, "start table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]))) return;
   2087  apply (proxy, plan, font, buffer);
   2088  if (buffer->messaging ())
   2089    (void) buffer->message (font, "end table GPOS script tag '%c%c%c%c'", HB_UNTAG (chosen_script[1]));
   2090 }
   2091 
   2092 void
   2093 hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
   2094 			const OT::SubstLookup &lookup,
   2095 			const OT::hb_ot_layout_lookup_accelerator_t &accel)
   2096 {
   2097  apply_string<GSUBProxy> (c, lookup, accel);
   2098 }
   2099 
   2100 #ifndef HB_NO_BASE
   2101 
   2102 static void
   2103 choose_base_tags (hb_script_t    script,
   2104 	  hb_language_t  language,
   2105 	  hb_tag_t      *script_tag,
   2106 	  hb_tag_t      *language_tag)
   2107 {
   2108  hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
   2109  unsigned script_count = ARRAY_LENGTH (script_tags);
   2110 
   2111  hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
   2112  unsigned language_count = ARRAY_LENGTH (language_tags);
   2113 
   2114  hb_ot_tags_from_script_and_language (script, language,
   2115 			       &script_count, script_tags,
   2116 			       &language_count, language_tags);
   2117 
   2118  *script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT;
   2119  *language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE;
   2120 }
   2121 
   2122 /**
   2123 * hb_ot_layout_get_font_extents:
   2124 * @font: a font
   2125 * @direction: text direction.
   2126 * @script_tag:  script tag.
   2127 * @language_tag: language tag.
   2128 * @extents: (out) (nullable): font extents if found.
   2129 *
   2130 * Fetches script/language-specific font extents.  These values are
   2131 * looked up in the `BASE` table's `MinMax` records.
   2132 *
   2133 * If no such extents are found, the default extents for the font are
   2134 * fetched. As such, the return value of this function can for the
   2135 * most part be ignored.  Note that the per-script/language extents
   2136 * do not have a line-gap value, and the line-gap is set to zero in
   2137 * that case.
   2138 *
   2139 * Return value: `true` if found script/language-specific font extents.
   2140 *
   2141 * Since: 8.0.0
   2142 **/
   2143 hb_bool_t
   2144 hb_ot_layout_get_font_extents (hb_font_t         *font,
   2145 		       hb_direction_t     direction,
   2146 		       hb_tag_t           script_tag,
   2147 		       hb_tag_t           language_tag,
   2148 		       hb_font_extents_t *extents)
   2149 {
   2150  hb_position_t min = 0, max = 0;
   2151  if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE,
   2152 				   &min, &max))
   2153  {
   2154    if (extents)
   2155    {
   2156      extents->ascender  = max;
   2157      extents->descender = min;
   2158      extents->line_gap  = 0;
   2159    }
   2160    return true;
   2161  }
   2162 
   2163  hb_font_get_extents_for_direction (font, direction, extents);
   2164  return false;
   2165 }
   2166 
   2167 /**
   2168 * hb_ot_layout_get_font_extents2:
   2169 * @font: a font
   2170 * @direction: text direction.
   2171 * @script:  script.
   2172 * @language: (nullable): language.
   2173 * @extents: (out) (nullable): font extents if found.
   2174 *
   2175 * Fetches script/language-specific font extents.  These values are
   2176 * looked up in the `BASE` table's `MinMax` records.
   2177 *
   2178 * If no such extents are found, the default extents for the font are
   2179 * fetched. As such, the return value of this function can for the
   2180 * most part be ignored.  Note that the per-script/language extents
   2181 * do not have a line-gap value, and the line-gap is set to zero in
   2182 * that case.
   2183 *
   2184 * This function is like hb_ot_layout_get_font_extents() but takes
   2185 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
   2186 *
   2187 * Return value: `true` if found script/language-specific font extents.
   2188 *
   2189 * Since: 8.0.0
   2190 **/
   2191 hb_bool_t
   2192 hb_ot_layout_get_font_extents2 (hb_font_t         *font,
   2193 			hb_direction_t     direction,
   2194 			hb_script_t        script,
   2195 			hb_language_t      language,
   2196 			hb_font_extents_t *extents)
   2197 {
   2198  hb_tag_t script_tag, language_tag;
   2199  choose_base_tags (script, language, &script_tag, &language_tag);
   2200  return hb_ot_layout_get_font_extents (font,
   2201 				direction,
   2202 				script_tag,
   2203 				language_tag,
   2204 				extents);
   2205 }
   2206 
   2207 /**
   2208 * hb_ot_layout_get_horizontal_baseline_tag_for_script:
   2209 * @script: a script tag.
   2210 *
   2211 * Fetches the dominant horizontal baseline tag used by @script.
   2212 *
   2213 * Return value: dominant baseline tag for the @script.
   2214 *
   2215 * Since: 4.0.0
   2216 **/
   2217 hb_ot_layout_baseline_tag_t
   2218 hb_ot_layout_get_horizontal_baseline_tag_for_script (hb_script_t script)
   2219 {
   2220  /* Keep in sync with hb_ot_layout_get_baseline_with_fallback */
   2221  switch ((int) script)
   2222  {
   2223    /* Unicode-1.1 additions */
   2224    case HB_SCRIPT_BENGALI:
   2225    case HB_SCRIPT_DEVANAGARI:
   2226    case HB_SCRIPT_GUJARATI:
   2227    case HB_SCRIPT_GURMUKHI:
   2228    /* Unicode-2.0 additions */
   2229    case HB_SCRIPT_TIBETAN:
   2230    /* Unicode-4.0 additions */
   2231    case HB_SCRIPT_LIMBU:
   2232    /* Unicode-4.1 additions */
   2233    case HB_SCRIPT_SYLOTI_NAGRI:
   2234    /* Unicode-5.0 additions */
   2235    case HB_SCRIPT_PHAGS_PA:
   2236    /* Unicode-5.2 additions */
   2237    case HB_SCRIPT_MEETEI_MAYEK:
   2238    /* Unicode-6.1 additions */
   2239    case HB_SCRIPT_SHARADA:
   2240    case HB_SCRIPT_TAKRI:
   2241    /* Unicode-7.0 additions */
   2242    case HB_SCRIPT_MODI:
   2243    case HB_SCRIPT_SIDDHAM:
   2244    case HB_SCRIPT_TIRHUTA:
   2245    /* Unicode-9.0 additions */
   2246    case HB_SCRIPT_MARCHEN:
   2247    case HB_SCRIPT_NEWA:
   2248    /* Unicode-10.0 additions */
   2249    case HB_SCRIPT_SOYOMBO:
   2250    case HB_SCRIPT_ZANABAZAR_SQUARE:
   2251    /* Unicode-11.0 additions */
   2252    case HB_SCRIPT_DOGRA:
   2253    case HB_SCRIPT_GUNJALA_GONDI:
   2254    /* Unicode-12.0 additions */
   2255    case HB_SCRIPT_NANDINAGARI:
   2256      return HB_OT_LAYOUT_BASELINE_TAG_HANGING;
   2257 
   2258    /* Unicode-1.1 additions */
   2259    case HB_SCRIPT_HANGUL:
   2260    case HB_SCRIPT_HAN:
   2261    case HB_SCRIPT_HIRAGANA:
   2262    case HB_SCRIPT_KATAKANA:
   2263    /* Unicode-3.0 additions */
   2264    case HB_SCRIPT_BOPOMOFO:
   2265    /* Unicode-9.0 additions */
   2266    case HB_SCRIPT_TANGUT:
   2267    /* Unicode-10.0 additions */
   2268    case HB_SCRIPT_NUSHU:
   2269    /* Unicode-13.0 additions */
   2270    case HB_SCRIPT_KHITAN_SMALL_SCRIPT:
   2271      return HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT;
   2272 
   2273    default:
   2274      return HB_OT_LAYOUT_BASELINE_TAG_ROMAN;
   2275  }
   2276 }
   2277 
   2278 /**
   2279 * hb_ot_layout_get_baseline:
   2280 * @font: a font
   2281 * @baseline_tag: a baseline tag
   2282 * @direction: text direction.
   2283 * @script_tag:  script tag.
   2284 * @language_tag: language tag, currently unused.
   2285 * @coord: (out) (nullable): baseline value if found.
   2286 *
   2287 * Fetches a baseline value from the face.
   2288 *
   2289 * Return value: `true` if found baseline value in the font.
   2290 *
   2291 * Since: 2.6.0
   2292 **/
   2293 hb_bool_t
   2294 hb_ot_layout_get_baseline (hb_font_t                   *font,
   2295 		   hb_ot_layout_baseline_tag_t  baseline_tag,
   2296 		   hb_direction_t               direction,
   2297 		   hb_tag_t                     script_tag,
   2298 		   hb_tag_t                     language_tag,
   2299 		   hb_position_t               *coord        /* OUT.  May be NULL. */)
   2300 {
   2301  return font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord);
   2302 }
   2303 
   2304 /**
   2305 * hb_ot_layout_get_baseline2:
   2306 * @font: a font
   2307 * @baseline_tag: a baseline tag
   2308 * @direction: text direction.
   2309 * @script:  script.
   2310 * @language: (nullable): language, currently unused.
   2311 * @coord: (out) (nullable): baseline value if found.
   2312 *
   2313 * Fetches a baseline value from the face.
   2314 *
   2315 * This function is like hb_ot_layout_get_baseline() but takes
   2316 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
   2317 *
   2318 * Return value: `true` if found baseline value in the font.
   2319 *
   2320 * Since: 8.0.0
   2321 **/
   2322 hb_bool_t
   2323 hb_ot_layout_get_baseline2 (hb_font_t                   *font,
   2324 		    hb_ot_layout_baseline_tag_t  baseline_tag,
   2325 		    hb_direction_t               direction,
   2326 		    hb_script_t                  script,
   2327 		    hb_language_t                language,
   2328 		    hb_position_t               *coord        /* OUT.  May be NULL. */)
   2329 {
   2330  hb_tag_t script_tag, language_tag;
   2331  choose_base_tags (script, language, &script_tag, &language_tag);
   2332  return hb_ot_layout_get_baseline (font,
   2333 			    baseline_tag,
   2334 			    direction,
   2335 			    script_tag,
   2336 			    language_tag,
   2337 			    coord);
   2338 }
   2339 
   2340 /**
   2341 * hb_ot_layout_get_baseline_with_fallback:
   2342 * @font: a font
   2343 * @baseline_tag: a baseline tag
   2344 * @direction: text direction.
   2345 * @script_tag:  script tag.
   2346 * @language_tag: language tag, currently unused.
   2347 * @coord: (out): baseline value if found.
   2348 *
   2349 * Fetches a baseline value from the face, and synthesizes
   2350 * it if the font does not have it.
   2351 *
   2352 * Since: 4.0.0
   2353 **/
   2354 void
   2355 hb_ot_layout_get_baseline_with_fallback (hb_font_t                   *font,
   2356 				 hb_ot_layout_baseline_tag_t  baseline_tag,
   2357 				 hb_direction_t               direction,
   2358 				 hb_tag_t                     script_tag,
   2359 				 hb_tag_t                     language_tag,
   2360 				 hb_position_t               *coord /* OUT */)
   2361 {
   2362  if (hb_ot_layout_get_baseline (font,
   2363 			 baseline_tag,
   2364 			 direction,
   2365 			 script_tag,
   2366 			 language_tag,
   2367 			 coord))
   2368    return;
   2369 
   2370  /* Synthesize missing baselines.
   2371   * See https://www.w3.org/TR/css-inline-3/#baseline-synthesis-fonts
   2372   */
   2373  switch (baseline_tag)
   2374  {
   2375  case HB_OT_LAYOUT_BASELINE_TAG_ROMAN:
   2376    *coord = 0; // FIXME origin ?
   2377    break;
   2378 
   2379  case HB_OT_LAYOUT_BASELINE_TAG_MATH:
   2380    {
   2381      hb_codepoint_t glyph;
   2382      hb_glyph_extents_t extents;
   2383      if (HB_DIRECTION_IS_HORIZONTAL (direction) &&
   2384   (hb_font_get_nominal_glyph (font, 0x2212u, &glyph) ||
   2385    hb_font_get_nominal_glyph (font, '-', &glyph)) &&
   2386   hb_font_get_glyph_extents (font, glyph, &extents))
   2387      {
   2388 *coord = extents.y_bearing + extents.height / 2;
   2389      }
   2390      else
   2391      {
   2392 hb_position_t x_height = font->y_scale / 2;
   2393 #ifndef HB_NO_METRICS
   2394 hb_ot_metrics_get_position_with_fallback (font, HB_OT_METRICS_TAG_X_HEIGHT, &x_height);
   2395 #endif
   2396 *coord = x_height / 2;
   2397      }
   2398    }
   2399    break;
   2400 
   2401  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT:
   2402  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT:
   2403    {
   2404      hb_position_t embox_top, embox_bottom;
   2405 
   2406      hb_ot_layout_get_baseline_with_fallback (font,
   2407 				       HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
   2408 				       direction,
   2409 				       script_tag,
   2410 				       language_tag,
   2411 				       &embox_top);
   2412      hb_ot_layout_get_baseline_with_fallback (font,
   2413 				       HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
   2414 				       direction,
   2415 				       script_tag,
   2416 				       language_tag,
   2417 				       &embox_bottom);
   2418 
   2419      if (baseline_tag == HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT)
   2420 *coord = embox_top + (embox_bottom - embox_top) / 10;
   2421      else
   2422 *coord = embox_bottom + (embox_top - embox_bottom) / 10;
   2423    }
   2424    break;
   2425 
   2426  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT:
   2427    if (hb_ot_layout_get_baseline (font,
   2428 			   HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
   2429 			   direction,
   2430 			   script_tag,
   2431 			   language_tag,
   2432 			   coord))
   2433      *coord += HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale;
   2434    else
   2435    {
   2436      hb_font_extents_t font_extents;
   2437      hb_font_get_extents_for_direction (font, direction, &font_extents);
   2438      *coord = font_extents.ascender;
   2439    }
   2440    break;
   2441 
   2442  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT:
   2443    if (hb_ot_layout_get_baseline (font,
   2444 			   HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
   2445 			   direction,
   2446 			   script_tag,
   2447 			   language_tag,
   2448 			   coord))
   2449      *coord -= HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale;
   2450    else
   2451    {
   2452      hb_font_extents_t font_extents;
   2453      hb_font_get_extents_for_direction (font, direction, &font_extents);
   2454      *coord = font_extents.descender;
   2455    }
   2456    break;
   2457 
   2458  case HB_OT_LAYOUT_BASELINE_TAG_HANGING:
   2459    if (HB_DIRECTION_IS_HORIZONTAL (direction))
   2460    {
   2461      hb_codepoint_t ch;
   2462      hb_codepoint_t glyph;
   2463      hb_glyph_extents_t extents;
   2464 
   2465      /* Keep in sync with hb_ot_layout_get_horizontal_baseline_for_script */
   2466      switch ((int) script_tag)
   2467      {
   2468      /* Unicode-1.1 additions */
   2469      case HB_SCRIPT_BENGALI:          ch = 0x0995u; break;
   2470      case HB_SCRIPT_DEVANAGARI:       ch = 0x0915u; break;
   2471      case HB_SCRIPT_GUJARATI:         ch = 0x0a95u; break;
   2472      case HB_SCRIPT_GURMUKHI:         ch = 0x0a15u; break;
   2473      /* Unicode-2.0 additions */
   2474      case HB_SCRIPT_TIBETAN:          ch = 0x0f40u; break;
   2475      /* Unicode-4.0 additions */
   2476      case HB_SCRIPT_LIMBU:            ch = 0x1901u; break;
   2477      /* Unicode-4.1 additions */
   2478      case HB_SCRIPT_SYLOTI_NAGRI:     ch = 0xa807u; break;
   2479      /* Unicode-5.0 additions */
   2480      case HB_SCRIPT_PHAGS_PA:         ch = 0xa840u; break;
   2481      /* Unicode-5.2 additions */
   2482      case HB_SCRIPT_MEETEI_MAYEK:     ch = 0xabc0u; break;
   2483      /* Unicode-6.1 additions */
   2484      case HB_SCRIPT_SHARADA:          ch = 0x11191u; break;
   2485      case HB_SCRIPT_TAKRI:            ch = 0x1168cu; break;
   2486      /* Unicode-7.0 additions */
   2487      case HB_SCRIPT_MODI:             ch = 0x1160eu;break;
   2488      case HB_SCRIPT_SIDDHAM:          ch = 0x11590u; break;
   2489      case HB_SCRIPT_TIRHUTA:          ch = 0x1148fu; break;
   2490      /* Unicode-9.0 additions */
   2491      case HB_SCRIPT_MARCHEN:          ch = 0x11c72u; break;
   2492      case HB_SCRIPT_NEWA:             ch = 0x1140eu; break;
   2493      /* Unicode-10.0 additions */
   2494      case HB_SCRIPT_SOYOMBO:          ch = 0x11a5cu; break;
   2495      case HB_SCRIPT_ZANABAZAR_SQUARE: ch = 0x11a0bu; break;
   2496      /* Unicode-11.0 additions */
   2497      case HB_SCRIPT_DOGRA:            ch = 0x1180au; break;
   2498      case HB_SCRIPT_GUNJALA_GONDI:    ch = 0x11d6cu; break;
   2499      /* Unicode-12.0 additions */
   2500      case HB_SCRIPT_NANDINAGARI:      ch = 0x119b0u; break;
   2501      default:                         ch = 0;        break;
   2502      }
   2503 
   2504      if (ch &&
   2505   hb_font_get_nominal_glyph (font, ch, &glyph) &&
   2506   hb_font_get_glyph_extents (font, glyph, &extents))
   2507 *coord = extents.y_bearing;
   2508      else
   2509 *coord = font->y_scale * 6 / 10; // FIXME makes assumptions about origin
   2510    }
   2511    else
   2512      *coord = font->x_scale * 6 / 10; // FIXME makes assumptions about origin
   2513    break;
   2514 
   2515  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL:
   2516    {
   2517      hb_position_t top, bottom;
   2518      hb_ot_layout_get_baseline_with_fallback (font,
   2519 				       HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
   2520 				       direction,
   2521 				       script_tag,
   2522 				       language_tag,
   2523 				       &top);
   2524      hb_ot_layout_get_baseline_with_fallback (font,
   2525 				       HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
   2526 				       direction,
   2527 				       script_tag,
   2528 				       language_tag,
   2529 				       &bottom);
   2530      *coord = (top + bottom) / 2;
   2531 
   2532    }
   2533    break;
   2534 
   2535  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL:
   2536    {
   2537      hb_position_t top, bottom;
   2538      hb_ot_layout_get_baseline_with_fallback (font,
   2539 				       HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT,
   2540 				       direction,
   2541 				       script_tag,
   2542 				       language_tag,
   2543 				       &top);
   2544      hb_ot_layout_get_baseline_with_fallback (font,
   2545 				       HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT,
   2546 				       direction,
   2547 				       script_tag,
   2548 				       language_tag,
   2549 				       &bottom);
   2550      *coord = (top + bottom) / 2;
   2551 
   2552    }
   2553    break;
   2554 
   2555  case _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE:
   2556  default:
   2557    *coord = 0;
   2558    break;
   2559  }
   2560 }
   2561 
   2562 /**
   2563 * hb_ot_layout_get_baseline_with_fallback2:
   2564 * @font: a font
   2565 * @baseline_tag: a baseline tag
   2566 * @direction: text direction.
   2567 * @script:  script.
   2568 * @language: (nullable): language, currently unused.
   2569 * @coord: (out): baseline value if found.
   2570 *
   2571 * Fetches a baseline value from the face, and synthesizes
   2572 * it if the font does not have it.
   2573 *
   2574 * This function is like hb_ot_layout_get_baseline_with_fallback() but takes
   2575 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
   2576 *
   2577 * Since: 8.0.0
   2578 **/
   2579 void
   2580 hb_ot_layout_get_baseline_with_fallback2 (hb_font_t                   *font,
   2581 				  hb_ot_layout_baseline_tag_t  baseline_tag,
   2582 				  hb_direction_t               direction,
   2583 				  hb_script_t                  script,
   2584 				  hb_language_t                language,
   2585 				  hb_position_t               *coord        /* OUT */)
   2586 {
   2587  hb_tag_t script_tag, language_tag;
   2588  choose_base_tags (script, language, &script_tag, &language_tag);
   2589  hb_ot_layout_get_baseline_with_fallback (font,
   2590 				   baseline_tag,
   2591 				   direction,
   2592 				   script_tag,
   2593 				   language_tag,
   2594 				   coord);
   2595 }
   2596 
   2597 #endif
   2598 
   2599 
   2600 #ifndef HB_NO_LAYOUT_RARELY_USED
   2601 struct hb_get_glyph_alternates_dispatch_t :
   2602       hb_dispatch_context_t<hb_get_glyph_alternates_dispatch_t, unsigned>
   2603 {
   2604  static return_t default_return_value () { return 0; }
   2605  bool stop_sublookup_iteration (return_t r) const { return r; }
   2606 
   2607  private:
   2608  template <typename T, typename ...Ts> auto
   2609  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
   2610  ( obj.get_glyph_alternates (std::forward<Ts> (ds)...) )
   2611  template <typename T, typename ...Ts> auto
   2612  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
   2613  ( default_return_value () )
   2614  public:
   2615  template <typename T, typename ...Ts> auto
   2616  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
   2617  ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
   2618 };
   2619 
   2620 /**
   2621 * hb_ot_layout_lookup_get_glyph_alternates:
   2622 * @face: a face.
   2623 * @lookup_index: index of the feature lookup to query.
   2624 * @glyph: a glyph id.
   2625 * @start_offset: starting offset.
   2626 * @alternate_count: (inout) (nullable): Input = the maximum number of alternate glyphs to return;
   2627 *                   Output = the actual number of alternate glyphs returned (may be zero).
   2628 * @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer.
   2629 *                    Alternate glyphs associated with the glyph id.
   2630 *
   2631 * Fetches alternates of a glyph from a given GSUB lookup index. Note that for one-to-one GSUB
   2632 * glyph substitutions, this function fetches the substituted glyph.
   2633 *
   2634 * Return value: Total number of alternates found in the specific lookup index for the given glyph id.
   2635 *
   2636 * Since: 2.6.8
   2637 **/
   2638 HB_EXTERN unsigned
   2639 hb_ot_layout_lookup_get_glyph_alternates (hb_face_t      *face,
   2640 				  unsigned        lookup_index,
   2641 				  hb_codepoint_t  glyph,
   2642 				  unsigned        start_offset,
   2643 				  unsigned       *alternate_count  /* IN/OUT.  May be NULL. */,
   2644 				  hb_codepoint_t *alternate_glyphs /* OUT.     May be NULL. */)
   2645 {
   2646  hb_get_glyph_alternates_dispatch_t c;
   2647  const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index);
   2648  auto ret = lookup.dispatch (&c, glyph, start_offset, alternate_count, alternate_glyphs);
   2649  if (!ret && alternate_count) *alternate_count = 0;
   2650  return ret;
   2651 }
   2652 
   2653 struct hb_collect_glyph_alternates_dispatch_t :
   2654       hb_dispatch_context_t<hb_collect_glyph_alternates_dispatch_t, bool>
   2655 {
   2656  static return_t default_return_value () { return false; }
   2657  bool stop_sublookup_iteration (return_t r) const { return false; }
   2658 
   2659  private:
   2660  template <typename T, typename ...Ts> auto
   2661  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
   2662  ( (obj.collect_glyph_alternates (std::forward<Ts> (ds)...), true) )
   2663  template <typename T, typename ...Ts> auto
   2664  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
   2665  ( default_return_value () )
   2666  public:
   2667  template <typename T, typename ...Ts> auto
   2668  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
   2669  ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
   2670 };
   2671 
   2672 /**
   2673 * hb_ot_layout_lookup_collect_glyph_alternates:
   2674 * @face: a face.
   2675 * @lookup_index: index of the feature lookup to query.
   2676 * @alternate_count: (inout): mapping from glyph index to number of alternates for that glyph.
   2677 * @alternate_glyphs: (inout): mapping from encoded glyph index and alternate index, to alternate glyph ids.
   2678 *
   2679 * Collects alternates of glyphs from a given GSUB lookup index.
   2680 *
   2681 * For one-to-one GSUB glyph substitutions, this function collects the
   2682 * substituted glyph.
   2683 *
   2684 * For lookups that assign multiple alternates to a glyph, all alternate glyphs are collected.
   2685 *
   2686 * For other lookup types, nothing is performed and `false` is returned.
   2687 *
   2688 * The `alternate_count` mapping will contain the number of alternates for each glyph id.
   2689 * Upon entry, this mapping should contain the glyph ids as keys, and the number of alternates
   2690 * currently known for each glyph id as values.
   2691 *
   2692 * The `alternate_glyphs` mapping will contain the alternate glyph ids for each glyph id.
   2693 * The mapping is encoded in the following way, upon entry and after processing:
   2694 * If G is the glyph id, and A0, A1, ..., A(n-1) are the alternate glyph ids,
   2695 * the mapping will contain the following entries: (G + (i << 24)) -> A(i)
   2696 * for i = 0, 1, ..., n-1 where n is the number of alternates for G as per `alternate_count`.
   2697 *
   2698 * Return value: `true` if alternates were collected, `false` otherwise.
   2699 * Since: 12.1.0
   2700 */
   2701 HB_EXTERN hb_bool_t
   2702 hb_ot_layout_lookup_collect_glyph_alternates (hb_face_t *face,
   2703 				      unsigned   lookup_index,
   2704 				      hb_map_t  *alternate_count /* IN/OUT */,
   2705 				      hb_map_t  *alternate_glyphs /* IN/OUT */)
   2706 {
   2707  hb_collect_glyph_alternates_dispatch_t c;
   2708  const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index);
   2709  return lookup.dispatch (&c, alternate_count, alternate_glyphs);
   2710 }
   2711 
   2712 struct hb_position_single_dispatch_t :
   2713       hb_dispatch_context_t<hb_position_single_dispatch_t, bool>
   2714 {
   2715  static return_t default_return_value () { return false; }
   2716  bool stop_sublookup_iteration (return_t r) const { return r; }
   2717 
   2718  private:
   2719  template <typename T, typename ...Ts> auto
   2720  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
   2721  ( obj.position_single (std::forward<Ts> (ds)...) )
   2722  template <typename T, typename ...Ts> auto
   2723  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
   2724  ( default_return_value () )
   2725  public:
   2726  template <typename T, typename ...Ts> auto
   2727  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
   2728  ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
   2729 };
   2730 
   2731 /**
   2732 * hb_ot_layout_lookup_get_optical_bound:
   2733 * @font: a font.
   2734 * @lookup_index: index of the feature lookup to query.
   2735 * @direction: edge of the glyph to query.
   2736 * @glyph: a glyph id.
   2737 *
   2738 * Fetches the optical bound of a glyph positioned at the margin of text.
   2739 * The direction identifies which edge of the glyph to query.
   2740 *
   2741 * Return value: Adjustment value. Negative values mean the glyph will stick out of the margin.
   2742 *
   2743 * Since: 5.3.0
   2744 **/
   2745 hb_position_t
   2746 hb_ot_layout_lookup_get_optical_bound (hb_font_t      *font,
   2747 			       unsigned        lookup_index,
   2748 			       hb_direction_t  direction,
   2749 			       hb_codepoint_t  glyph)
   2750 {
   2751  const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index);
   2752  hb_blob_t *blob = font->face->table.GPOS->get_blob ();
   2753  hb_glyph_position_t pos = {0};
   2754  hb_position_single_dispatch_t c;
   2755  lookup.dispatch (&c, font, blob, direction, glyph, pos);
   2756  hb_position_t ret = 0;
   2757  switch (direction)
   2758  {
   2759    case HB_DIRECTION_LTR:
   2760      ret = pos.x_offset;
   2761      break;
   2762    case HB_DIRECTION_RTL:
   2763      ret = pos.x_advance - pos.x_offset;
   2764      break;
   2765    case HB_DIRECTION_TTB:
   2766      ret = pos.y_offset;
   2767      break;
   2768    case HB_DIRECTION_BTT:
   2769      ret = pos.y_advance - pos.y_offset;
   2770      break;
   2771    case HB_DIRECTION_INVALID:
   2772    default:
   2773      break;
   2774  }
   2775  return ret;
   2776 }
   2777 #endif
   2778 
   2779 
   2780 #endif