Coverage Report

Created: 2026-10-14 09:33

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