Coverage Report

Created: 2026-10-17 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/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
1.72k
{
90
1.72k
  return face->table.kern->table->has_data ();
91
1.72k
}
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
20
{
106
20
  return face->table.kern->table->has_state_machine ();
107
20
}
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
448
{
134
448
  auto &accel = *font->face->table.kern;
135
448
  hb_blob_t *blob = accel.get_blob ();
136
137
448
  AAT::hb_aat_apply_context_t c (plan, font, buffer, blob);
138
139
448
  if (!buffer->message (font, "start table kern")) return;
140
448
  c.buffer_glyph_set = accel.scratch.create_buffer_glyph_set ();
141
448
  accel.apply (&c);
142
448
  accel.scratch.destroy_buffer_glyph_set (c.buffer_glyph_set);
143
448
  (void) buffer->message (font, "end table kern");
144
448
}
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
1.47k
{
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
1.47k
  switch HB_CODEPOINT_ENCODE3(blob->length,
177
1.47k
            face->table.GSUB->table.get_length (),
178
1.47k
            face->table.GPOS->table.get_length ())
179
1.47k
  {
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
1.47k
  }
265
1.47k
  return false;
266
1.47k
}
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
78.1k
{
273
78.1k
  _hb_buffer_assert_gsubgpos_vars (buffer);
274
275
78.1k
  const auto &gdef = *font->face->table.GDEF;
276
78.1k
  unsigned int count = buffer->len;
277
78.1k
  hb_glyph_info_t *info = buffer->info;
278
454k
  for (unsigned int i = 0; i < count; i++)
279
376k
  {
280
376k
    if (SetDigest)
281
376k
      buffer->digest.add (info[i].codepoint);
282
376k
    _hb_glyph_info_set_glyph_props (&info[i], gdef.get_glyph_props (info[i].codepoint));
283
376k
    _hb_glyph_info_clear_lig_props (&info[i]);
284
376k
  }
285
78.1k
}
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
78.1k
{
273
78.1k
  _hb_buffer_assert_gsubgpos_vars (buffer);
274
275
78.1k
  const auto &gdef = *font->face->table.GDEF;
276
78.1k
  unsigned int count = buffer->len;
277
78.1k
  hb_glyph_info_t *info = buffer->info;
278
454k
  for (unsigned int i = 0; i < count; i++)
279
376k
  {
280
376k
    if (SetDigest)
281
376k
      buffer->digest.add (info[i].codepoint);
282
376k
    _hb_glyph_info_set_glyph_props (&info[i], gdef.get_glyph_props (info[i].codepoint));
283
376k
    _hb_glyph_info_clear_lig_props (&info[i]);
284
376k
  }
285
78.1k
}
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
1.72k
{
301
1.72k
  return face->table.GDEF->table->has_glyph_classes ();
302
1.72k
}
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
0
{
404
0
  return font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
405
0
}
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
1.47k
{
417
#ifdef HB_NO_OT_LAYOUT_BLOCKLIST
418
  return false;
419
#endif
420
1.47k
  return false;
421
1.47k
}
422
423
bool
424
GPOS::is_blocklisted (hb_blob_t *blob HB_UNUSED,
425
        hb_face_t *face HB_UNUSED) const
426
1.47k
{
427
#ifdef HB_NO_OT_LAYOUT_BLOCKLIST
428
  return false;
429
#endif
430
1.47k
  return false;
431
1.47k
}
432
433
static const OT::GSUBGPOS&
434
get_gsubgpos_table (hb_face_t *face,
435
        hb_tag_t   table_tag)
436
173k
{
437
173k
  switch (table_tag) {
438
86.9k
    case HB_OT_TAG_GSUB: return *face->table.GSUB->table;
439
86.9k
    case HB_OT_TAG_GPOS: return *face->table.GPOS->table;
440
0
    default:             return Null (OT::GSUBGPOS);
441
173k
  }
442
173k
}
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
3.41k
#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
3.45k
{
571
3.45k
  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
572
3.45k
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
573
3.45k
  unsigned int i;
574
575
6.87k
  for (i = 0; i < script_count; i++)
576
3.45k
  {
577
3.45k
    if (g.find_script_index (script_tags[i], script_index))
578
28
    {
579
28
      if (chosen_script)
580
28
  *chosen_script = script_tags[i];
581
28
      return true;
582
28
    }
583
3.45k
  }
584
585
  /* try finding 'DFLT' */
586
3.42k
  if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
587
12
    if (chosen_script)
588
12
      *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
589
12
    return false;
590
12
  }
591
592
  /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
593
3.41k
  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
3.41k
  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
3.41k
  if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
608
3.41k
  if (chosen_script)
609
3.41k
    *chosen_script = HB_TAG_NONE;
610
3.41k
  return false;
611
3.41k
}
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
0
{
666
0
  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
667
0
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
668
669
0
  unsigned int num_features = g.get_feature_count ();
670
0
  for (unsigned int i = 0; i < num_features; i++)
671
0
  {
672
0
    if (feature_tag == g.get_feature_tag (i)) {
673
0
      if (feature_index) *feature_index = i;
674
0
      return true;
675
0
    }
676
0
  }
677
678
0
  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
679
0
  return false;
680
0
}
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
3.45k
{
780
3.45k
  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX), "");
781
3.45k
  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
782
3.45k
  unsigned int i;
783
784
5.35k
  for (i = 0; i < language_count; i++)
785
1.90k
  {
786
1.90k
    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
1.90k
  }
793
794
  /* try finding 'dflt' */
795
3.45k
  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
3.45k
  if (language_index)
803
3.45k
    *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
804
3.45k
  if (chosen_language)
805
0
    *chosen_language = HB_TAG_NONE;
806
3.45k
  return false;
807
3.45k
}
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
3.45k
{
837
3.45k
  return hb_ot_layout_script_select_language2 (face, table_tag,
838
3.45k
                 script_index,
839
3.45k
                 language_count, language_tags,
840
3.45k
                 language_index, nullptr);
841
3.45k
}
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
3.45k
{
899
3.45k
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
900
3.45k
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
901
902
3.45k
  unsigned int index = l.get_required_feature_index ();
903
3.45k
  if (feature_index) *feature_index = index;
904
3.45k
  if (feature_tag) *feature_tag = g.get_feature_tag (index);
905
906
3.45k
  return l.has_required_feature ();
907
3.45k
}
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
0
{
1016
0
  static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
1017
0
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1018
0
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
1019
1020
0
  unsigned int num_features = l.get_feature_count ();
1021
0
  for (unsigned int i = 0; i < num_features; i++) {
1022
0
    unsigned int f_index = l.get_feature_index (i);
1023
1024
0
    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
0
  }
1029
1030
0
  if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
1031
0
  return false;
1032
0
}
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
124
{
1087
124
  return get_gsubgpos_table (face, table_tag).get_lookup_count ();
1088
124
}
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
0
    : g (get_gsubgpos_table (face, table_tag)),
1099
0
      feature_indices (feature_indices_),
1100
0
      has_feature_filter (false),
1101
0
      script_count (0),langsys_count (0), feature_index_count (0)
1102
0
  {
1103
0
    compute_feature_filter (features);
1104
0
  }
1105
1106
  void compute_feature_filter (const hb_tag_t *features)
1107
0
  {
1108
0
    if (features == nullptr)
1109
0
    {
1110
0
      has_feature_filter = false;
1111
0
      return;
1112
0
    }
1113
1114
0
    has_feature_filter = true;
1115
0
    hb_set_t features_set;
1116
0
    for (; *features; features++)
1117
0
      features_set.add (*features);
1118
1119
0
    for (unsigned i = 0; i < g.get_feature_count (); i++)
1120
0
    {
1121
0
      hb_tag_t tag = g.get_feature_tag (i);
1122
0
      if (features_set.has (tag))
1123
0
  feature_indices_filter.add(i);
1124
0
    }
1125
0
  }
1126
1127
  bool visited (const OT::Script &s)
1128
0
  {
1129
    /* We might have Null() object here.  Don't want to involve
1130
     * that in the memoize.  So, detect empty objects and return. */
1131
0
    if (unlikely (!s.has_default_lang_sys () &&
1132
0
      !s.get_lang_sys_count ()))
1133
0
      return true;
1134
1135
0
    if (script_count++ > HB_MAX_SCRIPTS)
1136
0
      return true;
1137
1138
0
    return visited (s, visited_script);
1139
0
  }
1140
  bool visited (const OT::LangSys &l)
1141
0
  {
1142
    /* We might have Null() object here.  Don't want to involve
1143
     * that in the memoize.  So, detect empty objects and return. */
1144
0
    if (unlikely (!l.has_required_feature () &&
1145
0
      !l.get_feature_count ()))
1146
0
      return true;
1147
1148
0
    if (langsys_count++ > HB_MAX_LANGSYS)
1149
0
      return true;
1150
1151
0
    return visited (l, visited_langsys);
1152
0
  }
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
0
  {
1164
0
    hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g);
1165
0
     if (visited_set.has (delta))
1166
0
      return true;
1167
1168
0
    visited_set.add (delta);
1169
0
    return false;
1170
0
  }
Unexecuted instantiation: bool hb_collect_features_context_t::visited<OT::Script>(OT::Script const&, hb_set_t&)
Unexecuted instantiation: bool hb_collect_features_context_t::visited<OT::LangSys>(OT::LangSys const&, hb_set_t&)
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
0
{
1190
0
  if (c->visited (l)) return;
1191
1192
0
  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
0
  else
1203
0
  {
1204
0
    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
0
}
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
0
{
1222
0
  if (c->visited (s)) return;
1223
1224
0
  if (!languages)
1225
0
  {
1226
    /* All languages. */
1227
0
    if (s.has_default_lang_sys ())
1228
0
      langsys_collect_features (c,
1229
0
        s.get_default_lang_sys ());
1230
1231
1232
0
    unsigned int count = s.get_lang_sys_count ();
1233
0
    for (unsigned int language_index = 0; language_index < count; language_index++)
1234
0
      langsys_collect_features (c,
1235
0
        s.get_lang_sys (language_index));
1236
0
  }
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
0
}
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
0
{
1279
0
  hb_collect_features_context_t c (face, table_tag, feature_indexes, features);
1280
0
  if (!scripts)
1281
0
  {
1282
    /* All scripts. */
1283
0
    unsigned int count = c.g.get_script_count ();
1284
0
    for (unsigned int script_index = 0; script_index < count; script_index++)
1285
0
      script_collect_features (&c,
1286
0
             c.g.get_script (script_index),
1287
0
             languages);
1288
0
  }
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
0
}
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
3.45k
{
1322
3.45k
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1323
3.45k
  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
1324
1325
3.45k
  unsigned int count = l.get_feature_indexes (0, nullptr, nullptr);
1326
3.45k
  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
3.59k
  for (unsigned int i = count; i; i--)
1334
146
  {
1335
146
    unsigned feature_index = 0;
1336
146
    unsigned feature_count = 1;
1337
146
    l.get_feature_indexes (i - 1, &feature_count, &feature_index);
1338
146
    if (!feature_count)
1339
0
      break;
1340
146
    hb_tag_t feature_tag = g.get_feature_tag (feature_index);
1341
146
    feature_map->set (feature_tag, feature_index);
1342
146
  }
1343
3.45k
}
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
0
{
1374
0
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1375
1376
0
  hb_set_t feature_indexes;
1377
0
  hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
1378
1379
0
  for (auto feature_index : feature_indexes)
1380
0
    g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
1381
1382
0
  g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes);
1383
0
}
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_conditions)
1445
159k
{
1446
159k
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1447
159k
  const OT::GDEF &gdef = *face->table.GDEF->table;
1448
1449
159k
  auto instancer = OT::ItemVarStoreInstancer (&gdef.get_var_store (), nullptr,
1450
159k
                hb_array (coords, num_coords));
1451
1452
159k
  return g.get_feature_variations_state (coords,
1453
159k
           num_coords,
1454
159k
           variations_index,
1455
159k
           &instancer,
1456
159k
           lookup_conditions);
1457
159k
}
1458
1459
bool
1460
_hb_ot_layout_feature_get_lookup_variations (hb_face_t *face,
1461
                                             hb_tag_t table_tag,
1462
                                             unsigned feature_index,
1463
                                             unsigned variations_index,
1464
                                             hb_sorted_array_t<const unsigned> conditions,
1465
                                             hb_set_t *lookup_indexes)
1466
124
{
1467
124
  return get_gsubgpos_table (face, table_tag).get_lookup_variations (
1468
124
      feature_index, variations_index, conditions, lookup_indexes);
1469
124
}
1470
1471
1472
/**
1473
 * hb_ot_layout_table_find_feature_variations:
1474
 * @face: #hb_face_t to work upon
1475
 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
1476
 * @coords: The variation coordinates to query
1477
 * @num_coords: The number of variation coordinates
1478
 * @variations_index: (out): The array of feature variations found for the query
1479
 *
1480
 * Fetches a list of feature variations in the specified face's GSUB table
1481
 * or GPOS table, at the specified variation coordinates.
1482
 *
1483
 * Return value: `true` if feature variations were found, `false` otherwise.
1484
 *
1485
 * Since: 1.4.0
1486
 *
1487
 **/
1488
hb_bool_t
1489
hb_ot_layout_table_find_feature_variations (hb_face_t    *face,
1490
              hb_tag_t      table_tag,
1491
              const int    *coords,
1492
              unsigned int  num_coords,
1493
              unsigned int *variations_index /* out */)
1494
0
{
1495
0
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1496
0
  const OT::GDEF &gdef = *face->table.GDEF->table;
1497
1498
0
  auto instancer = OT::ItemVarStoreInstancer(&gdef.get_var_store(), nullptr,
1499
0
               hb_array (coords, num_coords));
1500
1501
0
  return g.find_variations_index (coords, num_coords, variations_index, &instancer);
1502
0
}
1503
1504
1505
/**
1506
 * hb_ot_layout_feature_with_variations_get_lookups:
1507
 * @face: #hb_face_t to work upon
1508
 * @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
1509
 * @feature_index: The index of the feature to query
1510
 * @variations_index: The index of the feature variation to query
1511
 * @start_offset: offset of the first lookup to retrieve
1512
 * @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return;
1513
 *                Output = the actual number of lookups returned (may be zero)
1514
 * @lookup_indexes: (out) (array length=lookup_count) (nullable): The array of lookups found for the query
1515
 *
1516
 * Fetches a list of all lookups enumerated for the specified feature, in
1517
 * the specified face's GSUB table or GPOS table, enabled at the specified
1518
 * variations index. The list returned will begin at the offset provided.
1519
 *
1520
 * Return value: Total number of lookups.
1521
 *
1522
 * Since: 1.4.0
1523
 *
1524
 **/
1525
unsigned int
1526
hb_ot_layout_feature_with_variations_get_lookups (hb_face_t    *face,
1527
              hb_tag_t      table_tag,
1528
              unsigned int  feature_index,
1529
              unsigned int  variations_index,
1530
              unsigned int  start_offset,
1531
              unsigned int *lookup_count /* IN/OUT */,
1532
              unsigned int *lookup_indexes /* OUT */)
1533
124
{
1534
124
  static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), "");
1535
124
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1536
1537
124
  const OT::Feature &f = g.get_feature_variation (feature_index, variations_index);
1538
1539
124
  return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
1540
124
}
1541
1542
1543
/*
1544
 * OT::GSUB
1545
 */
1546
1547
1548
/**
1549
 * hb_ot_layout_has_substitution:
1550
 * @face: #hb_face_t to work upon
1551
 *
1552
 * Tests whether the specified face includes any GSUB substitutions.
1553
 *
1554
 * Return value: `true` if data found, `false` otherwise
1555
 *
1556
 * Since: 0.6.0
1557
 *
1558
 **/
1559
hb_bool_t
1560
hb_ot_layout_has_substitution (hb_face_t *face)
1561
1.72k
{
1562
1.72k
  return face->table.GSUB->table->has_data ();
1563
1.72k
}
1564
1565
1566
/**
1567
 * hb_ot_layout_lookup_would_substitute:
1568
 * @face: #hb_face_t to work upon
1569
 * @lookup_index: The index of the lookup to query
1570
 * @glyphs: The sequence of glyphs to query for substitution
1571
 * @glyphs_length: The length of the glyph sequence
1572
 * @zero_context: #hb_bool_t indicating whether pre-/post-context are disallowed
1573
 * in substitutions
1574
 *
1575
 * Tests whether a specified lookup in the specified face would
1576
 * trigger a substitution on the given glyph sequence.
1577
 *
1578
 * Return value: `true` if a substitution would be triggered, `false` otherwise
1579
 *
1580
 * Since: 0.9.7
1581
 **/
1582
hb_bool_t
1583
hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
1584
              unsigned int          lookup_index,
1585
              const hb_codepoint_t *glyphs,
1586
              unsigned int          glyphs_length,
1587
              hb_bool_t             zero_context)
1588
0
{
1589
0
  auto &gsub = face->table.GSUB;
1590
0
  if (unlikely (lookup_index >= gsub->lookup_count)) return false;
1591
0
  OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
1592
1593
0
  const OT::SubstLookup& l = gsub->table->get_lookup (lookup_index);
1594
0
  auto *accel = gsub->get_accel (lookup_index);
1595
0
  return accel && l.would_apply (&c, accel);
1596
0
}
1597
1598
1599
/**
1600
 * hb_ot_layout_substitute_start:
1601
 * @font: #hb_font_t to use
1602
 * @buffer: #hb_buffer_t buffer to work upon
1603
 *
1604
 * Called before substitution lookups are performed, to ensure that glyph
1605
 * class and other properties are set on the glyphs in the buffer.
1606
 *
1607
 **/
1608
void
1609
hb_ot_layout_substitute_start (hb_font_t    *font,
1610
             hb_buffer_t  *buffer)
1611
0
{
1612
0
  _hb_ot_layout_set_glyph_props<false> (font, buffer);
1613
0
}
1614
1615
void
1616
hb_ot_layout_substitute_start_with_digest (hb_font_t    *font,
1617
             hb_buffer_t  *buffer)
1618
78.1k
{
1619
78.1k
  buffer->digest = hb_set_digest_t ();
1620
78.1k
  _hb_ot_layout_set_glyph_props<true> (font, buffer);
1621
78.1k
}
1622
1623
/**
1624
 * hb_ot_layout_lookup_substitute_closure:
1625
 * @face: #hb_face_t to work upon
1626
 * @lookup_index: index of the feature lookup to query
1627
 * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookup
1628
 *
1629
 * Compute the transitive closure of glyphs needed for a
1630
 * specified lookup.
1631
 *
1632
 * Since: 0.9.7
1633
 **/
1634
void
1635
hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
1636
          unsigned int  lookup_index,
1637
          hb_set_t     *glyphs /* OUT */)
1638
0
{
1639
0
  hb_map_t done_lookups_glyph_count;
1640
0
  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set;
1641
0
  OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
1642
1643
0
  const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
1644
1645
0
  l.closure (&c, lookup_index);
1646
0
}
1647
1648
/**
1649
 * hb_ot_layout_lookups_substitute_closure:
1650
 * @face: #hb_face_t to work upon
1651
 * @lookups: The set of lookups to query
1652
 * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookups
1653
 *
1654
 * Compute the transitive closure of glyphs needed for all of the
1655
 * provided lookups.
1656
 *
1657
 * Since: 1.8.1
1658
 **/
1659
void
1660
hb_ot_layout_lookups_substitute_closure (hb_face_t      *face,
1661
           const hb_set_t *lookups,
1662
           hb_set_t       *glyphs /* OUT */)
1663
0
{
1664
0
  hb_map_t done_lookups_glyph_count;
1665
0
  hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> done_lookups_glyph_set;
1666
0
  OT::hb_closure_context_t c (face, glyphs, &done_lookups_glyph_count, &done_lookups_glyph_set);
1667
0
  const GSUB& gsub = *face->table.GSUB->table;
1668
1669
0
  unsigned int iteration_count = 0;
1670
0
  unsigned int glyphs_length;
1671
0
  do
1672
0
  {
1673
0
    c.reset_lookup_visit_count ();
1674
0
    glyphs_length = glyphs->get_population ();
1675
0
    if (lookups)
1676
0
    {
1677
0
      for (auto lookup_index : *lookups)
1678
0
  gsub.get_lookup (lookup_index).closure (&c, lookup_index);
1679
0
    }
1680
0
    else
1681
0
    {
1682
0
      for (unsigned int i = 0; i < gsub.get_lookup_count (); i++)
1683
0
  gsub.get_lookup (i).closure (&c, i);
1684
0
    }
1685
0
  } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES &&
1686
0
     glyphs_length != glyphs->get_population ());
1687
0
}
1688
1689
/*
1690
 * GPOS
1691
 */
1692
1693
1694
/**
1695
 * hb_ot_layout_has_positioning:
1696
 * @face: #hb_face_t to work upon
1697
 *
1698
 * Tests whether the specified face includes any GPOS positioning.
1699
 *
1700
 * Return value: `true` if the face has GPOS data, `false` otherwise
1701
 *
1702
 **/
1703
hb_bool_t
1704
hb_ot_layout_has_positioning (hb_face_t *face)
1705
1.72k
{
1706
1.72k
  return face->table.GPOS->table->has_data ();
1707
1.72k
}
1708
1709
/**
1710
 * hb_ot_layout_position_start:
1711
 * @font: #hb_font_t to use
1712
 * @buffer: #hb_buffer_t buffer to work upon
1713
 *
1714
 * Called before positioning lookups are performed, to ensure that glyph
1715
 * attachment types and glyph-attachment chains are set for the glyphs in the buffer.
1716
 *
1717
 **/
1718
void
1719
hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
1720
78.1k
{
1721
78.1k
  GPOS::position_start (font, buffer);
1722
78.1k
}
1723
1724
1725
/**
1726
 * hb_ot_layout_position_finish_advances:
1727
 * @font: #hb_font_t to use
1728
 * @buffer: #hb_buffer_t buffer to work upon
1729
 *
1730
 * Called after positioning lookups are performed, to finish glyph advances.
1731
 *
1732
 **/
1733
void
1734
hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
1735
78.1k
{
1736
78.1k
  GPOS::position_finish_advances (font, buffer);
1737
78.1k
}
1738
1739
/**
1740
 * hb_ot_layout_position_finish_offsets:
1741
 * @font: #hb_font_t to use
1742
 * @buffer: #hb_buffer_t buffer to work upon
1743
 *
1744
 * Called after positioning lookups are performed, to finish glyph offsets.
1745
 *
1746
 **/
1747
void
1748
hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
1749
78.1k
{
1750
78.1k
  GPOS::position_finish_offsets (font, buffer);
1751
78.1k
}
1752
1753
1754
#ifndef HB_NO_LAYOUT_FEATURE_PARAMS
1755
/**
1756
 * hb_ot_layout_get_size_params:
1757
 * @face: #hb_face_t to work upon
1758
 * @design_size: (out): The design size of the face
1759
 * @subfamily_id: (out): The identifier of the face within the font subfamily
1760
 * @subfamily_name_id: (out): The ‘name’ table name ID of the face within the font subfamily
1761
 * @range_start: (out): The minimum size of the recommended size range for the face
1762
 * @range_end: (out): The maximum size of the recommended size range for the face
1763
 *
1764
 * Fetches optical-size feature data (i.e., the `size` feature from GPOS). Note that
1765
 * the subfamily_id and the subfamily name string (accessible via the subfamily_name_id)
1766
 * as used here are defined as pertaining only to fonts within a font family that differ
1767
 * specifically in their respective size ranges; other ways to differentiate fonts within
1768
 * a subfamily are not covered by the `size` feature.
1769
 *
1770
 * For more information on this distinction, see the [`size` feature documentation](
1771
 * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-size).
1772
 *
1773
 * Return value: `true` if data found, `false` otherwise
1774
 *
1775
 * Since: 0.9.10
1776
 **/
1777
hb_bool_t
1778
hb_ot_layout_get_size_params (hb_face_t       *face,
1779
            unsigned int    *design_size,       /* OUT.  May be NULL */
1780
            unsigned int    *subfamily_id,      /* OUT.  May be NULL */
1781
            hb_ot_name_id_t *subfamily_name_id, /* OUT.  May be NULL */
1782
            unsigned int    *range_start,       /* OUT.  May be NULL */
1783
            unsigned int    *range_end          /* OUT.  May be NULL */)
1784
0
{
1785
0
  const GPOS &gpos = *face->table.GPOS->table;
1786
0
  const hb_tag_t tag = HB_TAG ('s','i','z','e');
1787
1788
0
  unsigned int num_features = gpos.get_feature_count ();
1789
0
  for (unsigned int i = 0; i < num_features; i++)
1790
0
  {
1791
0
    if (tag == gpos.get_feature_tag (i))
1792
0
    {
1793
0
      const OT::Feature &f = gpos.get_feature (i);
1794
0
      const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
1795
1796
0
      if (params.designSize)
1797
0
      {
1798
0
  if (design_size) *design_size = params.designSize;
1799
0
  if (subfamily_id) *subfamily_id = params.subfamilyID;
1800
0
  if (subfamily_name_id) *subfamily_name_id = params.subfamilyNameID;
1801
0
  if (range_start) *range_start = params.rangeStart;
1802
0
  if (range_end) *range_end = params.rangeEnd;
1803
1804
0
  return true;
1805
0
      }
1806
0
    }
1807
0
  }
1808
1809
0
  if (design_size) *design_size = 0;
1810
0
  if (subfamily_id) *subfamily_id = 0;
1811
0
  if (subfamily_name_id) *subfamily_name_id = HB_OT_NAME_ID_INVALID;
1812
0
  if (range_start) *range_start = 0;
1813
0
  if (range_end) *range_end = 0;
1814
1815
0
  return false;
1816
0
}
1817
1818
1819
/**
1820
 * hb_ot_layout_feature_get_name_ids:
1821
 * @face: #hb_face_t to work upon
1822
 * @table_tag: table tag to query, "GSUB" or "GPOS".
1823
 * @feature_index: index of feature to query.
1824
 * @label_id: (out) (nullable): The ‘name’ table name ID that specifies a string
1825
 *            for a user-interface label for this feature.
1826
 * @tooltip_id: (out) (nullable): The ‘name’ table name ID that specifies a string
1827
 *              that an application can use for tooltip text for this
1828
 *              feature.
1829
 * @sample_id: (out) (nullable): The ‘name’ table name ID that specifies sample text
1830
 *             that illustrates the effect of this feature.
1831
 * @num_named_parameters: (out) (nullable):  Number of named parameters.
1832
 * @first_param_id: (out) (nullable): The first ‘name’ table name ID used to specify
1833
 *                  strings for user-interface labels for the feature
1834
 *                  parameters. (Must be zero if numParameters is zero.)
1835
 *
1836
 * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or
1837
 * "Character Variant" ('cvXX') features.
1838
 *
1839
 * Return value: `true` if data found, `false` otherwise
1840
 *
1841
 * Since: 2.0.0
1842
 **/
1843
hb_bool_t
1844
hb_ot_layout_feature_get_name_ids (hb_face_t       *face,
1845
           hb_tag_t         table_tag,
1846
           unsigned int     feature_index,
1847
           hb_ot_name_id_t *label_id,             /* OUT.  May be NULL */
1848
           hb_ot_name_id_t *tooltip_id,           /* OUT.  May be NULL */
1849
           hb_ot_name_id_t *sample_id,            /* OUT.  May be NULL */
1850
           unsigned int    *num_named_parameters, /* OUT.  May be NULL */
1851
           hb_ot_name_id_t *first_param_id        /* OUT.  May be NULL */)
1852
0
{
1853
0
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1854
1855
0
  hb_tag_t feature_tag = g.get_feature_tag (feature_index);
1856
0
  const OT::Feature &f = g.get_feature (feature_index);
1857
1858
0
  const OT::FeatureParams &feature_params = f.get_feature_params ();
1859
0
  if (&feature_params != &Null (OT::FeatureParams))
1860
0
  {
1861
0
    const OT::FeatureParamsStylisticSet& ss_params =
1862
0
      feature_params.get_stylistic_set_params (feature_tag);
1863
0
    if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */
1864
0
    {
1865
0
      if (label_id) *label_id = ss_params.uiNameID;
1866
      // ssXX features don't have the rest
1867
0
      if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
1868
0
      if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
1869
0
      if (num_named_parameters) *num_named_parameters = 0;
1870
0
      if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
1871
0
      return true;
1872
0
    }
1873
0
    const OT::FeatureParamsCharacterVariants& cv_params =
1874
0
      feature_params.get_character_variants_params (feature_tag);
1875
0
    if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */
1876
0
    {
1877
0
      if (label_id) *label_id = cv_params.featUILableNameID;
1878
0
      if (tooltip_id) *tooltip_id = cv_params.featUITooltipTextNameID;
1879
0
      if (sample_id) *sample_id = cv_params.sampleTextNameID;
1880
0
      if (num_named_parameters) *num_named_parameters = cv_params.numNamedParameters;
1881
0
      if (first_param_id) *first_param_id = cv_params.firstParamUILabelNameID;
1882
0
      return true;
1883
0
    }
1884
0
  }
1885
1886
0
  if (label_id) *label_id = HB_OT_NAME_ID_INVALID;
1887
0
  if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
1888
0
  if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
1889
0
  if (num_named_parameters) *num_named_parameters = 0;
1890
0
  if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
1891
0
  return false;
1892
0
}
1893
/**
1894
 * hb_ot_layout_feature_get_characters:
1895
 * @face: #hb_face_t to work upon
1896
 * @table_tag: table tag to query, "GSUB" or "GPOS".
1897
 * @feature_index: index of feature to query.
1898
 * @start_offset: offset of the first character to retrieve
1899
 * @char_count: (inout) (nullable): Input = the maximum number of characters to return;
1900
 *              Output = the actual number of characters returned (may be zero)
1901
 * @characters: (out caller-allocates) (array length=char_count) (nullable): A buffer pointer.
1902
 *              The Unicode codepoints of the characters for which this feature provides
1903
 *               glyph variants.
1904
 *
1905
 * Fetches a list of the characters defined as having a variant under the specified
1906
 * "Character Variant" ("cvXX") feature tag.
1907
 *
1908
 * Return value: Number of total sample characters in the cvXX feature.
1909
 *
1910
 * Since: 2.0.0
1911
 **/
1912
unsigned int
1913
hb_ot_layout_feature_get_characters (hb_face_t      *face,
1914
             hb_tag_t        table_tag,
1915
             unsigned int    feature_index,
1916
             unsigned int    start_offset,
1917
             unsigned int   *char_count, /* IN/OUT.  May be NULL */
1918
             hb_codepoint_t *characters  /* OUT.     May be NULL */)
1919
0
{
1920
0
  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1921
0
  return g.get_feature (feature_index)
1922
0
    .get_feature_params ()
1923
0
    .get_character_variants_params(g.get_feature_tag (feature_index))
1924
0
    .get_characters (start_offset, char_count, characters);
1925
0
}
1926
#endif
1927
1928
1929
/*
1930
 * Parts of different types are implemented here such that they have direct
1931
 * access to GSUB/GPOS lookups.
1932
 */
1933
1934
1935
struct GSUBProxy
1936
{
1937
  static constexpr unsigned table_index = 0u;
1938
  static constexpr bool always_inplace = false;
1939
  typedef OT::SubstLookup Lookup;
1940
1941
  GSUBProxy (hb_face_t *face) :
1942
78.1k
    accel (*face->table.GSUB) {}
1943
1944
  const GSUB::accelerator_t &accel;
1945
};
1946
1947
struct GPOSProxy
1948
{
1949
  static constexpr unsigned table_index = 1u;
1950
  static constexpr bool always_inplace = true;
1951
  typedef OT::PosLookup Lookup;
1952
1953
  GPOSProxy (hb_face_t *face) :
1954
78.1k
    accel (*face->table.GPOS) {}
1955
1956
  const GPOS::accelerator_t &accel;
1957
};
1958
1959
1960
static inline bool
1961
apply_forward (OT::hb_ot_apply_context_t *c,
1962
         const OT::hb_ot_layout_lookup_accelerator_t &accel)
1963
50
{
1964
50
  bool use_hot_subtable_cache = accel.cache_enter (c);
1965
1966
50
  bool ret = false;
1967
50
  hb_buffer_t *buffer = c->buffer;
1968
100
  while (buffer->successful)
1969
100
  {
1970
100
    hb_glyph_info_t *info = buffer->info;
1971
100
    unsigned j = buffer->idx;
1972
100
    while (j < buffer->len &&
1973
50
     !(accel.digest.may_have (info[j].codepoint) &&
1974
50
       (info[j].mask & c->lookup_mask) &&
1975
50
       c->check_glyph_property (&info[j], c->lookup_props)))
1976
0
      j++;
1977
100
    if (unlikely (j > buffer->idx && !buffer->next_glyphs (j - buffer->idx)))
1978
0
      break;
1979
100
    if (buffer->idx >= buffer->len)
1980
50
      break;
1981
1982
50
    if (accel.apply (c, use_hot_subtable_cache))
1983
0
      ret = true;
1984
50
    else
1985
50
      (void) buffer->next_glyph ();
1986
50
  }
1987
1988
50
  if (use_hot_subtable_cache)
1989
25
    accel.cache_leave (c);
1990
1991
50
  return ret;
1992
50
}
1993
1994
static inline bool
1995
apply_backward (OT::hb_ot_apply_context_t *c,
1996
         const OT::hb_ot_layout_lookup_accelerator_t &accel)
1997
0
{
1998
0
  bool ret = false;
1999
0
  hb_buffer_t *buffer = c->buffer;
2000
0
  do
2001
0
  {
2002
0
    auto &cur = buffer->cur();
2003
0
    if (accel.digest.may_have (cur.codepoint) &&
2004
0
  (cur.mask & c->lookup_mask) &&
2005
0
  c->check_glyph_property (&cur, c->lookup_props))
2006
0
      ret |= accel.apply (c, false);
2007
2008
    /* The reverse lookup doesn't "advance" cursor (for good reason). */
2009
0
    buffer->idx--;
2010
0
  }
2011
0
  while ((int) buffer->idx >= 0);
2012
0
  return ret;
2013
0
}
2014
2015
template <typename Proxy>
2016
static inline bool
2017
apply_string (OT::hb_ot_apply_context_t *c,
2018
        const typename Proxy::Lookup &lookup,
2019
        const OT::hb_ot_layout_lookup_accelerator_t &accel)
2020
50
{
2021
50
  hb_buffer_t *buffer = c->buffer;
2022
2023
50
  if (unlikely (!buffer->len || !c->lookup_mask))
2024
0
    return false;
2025
2026
50
  bool ret = false;
2027
2028
50
  c->set_lookup_props (lookup.get_props ());
2029
2030
50
  if (likely (!lookup.is_reverse ()))
2031
50
  {
2032
    /* in/out forward substitution/positioning */
2033
50
    if (!Proxy::always_inplace)
2034
25
      buffer->clear_output ();
2035
2036
50
    buffer->idx = 0;
2037
50
    ret = apply_forward (c, accel);
2038
2039
50
    if (!Proxy::always_inplace)
2040
25
      buffer->sync ();
2041
50
  }
2042
0
  else
2043
0
  {
2044
    /* in-place backward substitution/positioning */
2045
0
    assert (!buffer->have_output);
2046
0
    buffer->idx = buffer->len - 1;
2047
0
    ret = apply_backward (c, accel);
2048
0
  }
2049
2050
50
  return ret;
2051
50
}
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
2020
25
{
2021
25
  hb_buffer_t *buffer = c->buffer;
2022
2023
25
  if (unlikely (!buffer->len || !c->lookup_mask))
2024
0
    return false;
2025
2026
25
  bool ret = false;
2027
2028
25
  c->set_lookup_props (lookup.get_props ());
2029
2030
25
  if (likely (!lookup.is_reverse ()))
2031
25
  {
2032
    /* in/out forward substitution/positioning */
2033
25
    if (!Proxy::always_inplace)
2034
0
      buffer->clear_output ();
2035
2036
25
    buffer->idx = 0;
2037
25
    ret = apply_forward (c, accel);
2038
2039
25
    if (!Proxy::always_inplace)
2040
0
      buffer->sync ();
2041
25
  }
2042
0
  else
2043
0
  {
2044
    /* in-place backward substitution/positioning */
2045
0
    assert (!buffer->have_output);
2046
0
    buffer->idx = buffer->len - 1;
2047
0
    ret = apply_backward (c, accel);
2048
0
  }
2049
2050
25
  return ret;
2051
25
}
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
2020
25
{
2021
25
  hb_buffer_t *buffer = c->buffer;
2022
2023
25
  if (unlikely (!buffer->len || !c->lookup_mask))
2024
0
    return false;
2025
2026
25
  bool ret = false;
2027
2028
25
  c->set_lookup_props (lookup.get_props ());
2029
2030
25
  if (likely (!lookup.is_reverse ()))
2031
25
  {
2032
    /* in/out forward substitution/positioning */
2033
25
    if (!Proxy::always_inplace)
2034
25
      buffer->clear_output ();
2035
2036
25
    buffer->idx = 0;
2037
25
    ret = apply_forward (c, accel);
2038
2039
25
    if (!Proxy::always_inplace)
2040
25
      buffer->sync ();
2041
25
  }
2042
0
  else
2043
0
  {
2044
    /* in-place backward substitution/positioning */
2045
0
    assert (!buffer->have_output);
2046
0
    buffer->idx = buffer->len - 1;
2047
0
    ret = apply_backward (c, accel);
2048
0
  }
2049
2050
25
  return ret;
2051
25
}
2052
2053
template <typename Proxy>
2054
inline void hb_ot_map_t::apply (const Proxy &proxy,
2055
        const hb_ot_shape_plan_t *plan,
2056
        hb_font_t *font,
2057
        hb_buffer_t *buffer) const
2058
156k
{
2059
156k
  const unsigned int table_index = proxy.table_index;
2060
156k
  unsigned int i = 0;
2061
2062
156k
  auto *font_data = font->data.ot.get ();
2063
156k
  auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data;
2064
2065
156k
  OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache);
2066
156k
  c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
2067
2068
390k
  for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
2069
234k
  {
2070
234k
    const stage_map_t *stage = &stages[table_index][stage_index];
2071
236k
    for (; i < stage->last_lookup; i++)
2072
1.67k
    {
2073
1.67k
      auto &lookup = lookups[table_index][i];
2074
2075
1.67k
      unsigned int lookup_index = lookup.index;
2076
2077
1.67k
      auto *accel = proxy.accel.get_accel (lookup_index);
2078
1.67k
      if (unlikely (!accel)) continue;
2079
2080
1.67k
      if (buffer->messaging () &&
2081
0
    !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue;
2082
2083
      /* Only try applying the lookup if there is any overlap. */
2084
1.67k
      if (accel->digest.may_intersect (buffer->digest) &&
2085
100
    ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) ||
2086
100
     accel->digest_second.may_intersect (buffer->digest)))
2087
50
      {
2088
50
  c.set_lookup_index (lookup_index);
2089
50
  c.set_lookup_mask (lookup.mask, false);
2090
50
  c.set_auto_zwj (lookup.auto_zwj, false);
2091
50
  c.set_auto_zwnj (lookup.auto_zwnj, false);
2092
50
  c.set_random (lookup.random);
2093
50
  c.set_per_syllable (lookup.per_syllable, false);
2094
  /* apply_string's set_lookup_props initializes the iterators. */
2095
2096
50
  apply_string<Proxy> (&c,
2097
50
           proxy.accel.table->get_lookup (lookup_index),
2098
50
           *accel);
2099
50
      }
2100
1.62k
      else if (buffer->messaging ())
2101
0
  (void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag));
2102
2103
1.67k
      if (buffer->messaging ())
2104
0
  (void) buffer->message (font, "end lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag));
2105
1.67k
    }
2106
2107
234k
    if (stage->pause_func)
2108
0
    {
2109
0
      if (stage->pause_func (plan, font, buffer))
2110
0
      {
2111
  /* Refresh working buffer digest since buffer changed. */
2112
0
  buffer->update_digest ();
2113
0
      }
2114
0
    }
2115
234k
  }
2116
156k
}
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
2058
78.1k
{
2059
78.1k
  const unsigned int table_index = proxy.table_index;
2060
78.1k
  unsigned int i = 0;
2061
2062
78.1k
  auto *font_data = font->data.ot.get ();
2063
78.1k
  auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data;
2064
2065
78.1k
  OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache);
2066
78.1k
  c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
2067
2068
234k
  for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
2069
156k
  {
2070
156k
    const stage_map_t *stage = &stages[table_index][stage_index];
2071
157k
    for (; i < stage->last_lookup; i++)
2072
879
    {
2073
879
      auto &lookup = lookups[table_index][i];
2074
2075
879
      unsigned int lookup_index = lookup.index;
2076
2077
879
      auto *accel = proxy.accel.get_accel (lookup_index);
2078
879
      if (unlikely (!accel)) continue;
2079
2080
879
      if (buffer->messaging () &&
2081
0
    !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue;
2082
2083
      /* Only try applying the lookup if there is any overlap. */
2084
879
      if (accel->digest.may_intersect (buffer->digest) &&
2085
25
    ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) ||
2086
25
     accel->digest_second.may_intersect (buffer->digest)))
2087
25
      {
2088
25
  c.set_lookup_index (lookup_index);
2089
25
  c.set_lookup_mask (lookup.mask, false);
2090
25
  c.set_auto_zwj (lookup.auto_zwj, false);
2091
25
  c.set_auto_zwnj (lookup.auto_zwnj, false);
2092
25
  c.set_random (lookup.random);
2093
25
  c.set_per_syllable (lookup.per_syllable, false);
2094
  /* apply_string's set_lookup_props initializes the iterators. */
2095
2096
25
  apply_string<Proxy> (&c,
2097
25
           proxy.accel.table->get_lookup (lookup_index),
2098
25
           *accel);
2099
25
      }
2100
854
      else if (buffer->messaging ())
2101
0
  (void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag));
2102
2103
879
      if (buffer->messaging ())
2104
0
  (void) buffer->message (font, "end lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag));
2105
879
    }
2106
2107
156k
    if (stage->pause_func)
2108
0
    {
2109
0
      if (stage->pause_func (plan, font, buffer))
2110
0
      {
2111
  /* Refresh working buffer digest since buffer changed. */
2112
0
  buffer->update_digest ();
2113
0
      }
2114
0
    }
2115
156k
  }
2116
78.1k
}
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
2058
78.1k
{
2059
78.1k
  const unsigned int table_index = proxy.table_index;
2060
78.1k
  unsigned int i = 0;
2061
2062
78.1k
  auto *font_data = font->data.ot.get ();
2063
78.1k
  auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data;
2064
2065
78.1k
  OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache);
2066
78.1k
  c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
2067
2068
156k
  for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++)
2069
78.1k
  {
2070
78.1k
    const stage_map_t *stage = &stages[table_index][stage_index];
2071
78.9k
    for (; i < stage->last_lookup; i++)
2072
792
    {
2073
792
      auto &lookup = lookups[table_index][i];
2074
2075
792
      unsigned int lookup_index = lookup.index;
2076
2077
792
      auto *accel = proxy.accel.get_accel (lookup_index);
2078
792
      if (unlikely (!accel)) continue;
2079
2080
792
      if (buffer->messaging () &&
2081
0
    !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue;
2082
2083
      /* Only try applying the lookup if there is any overlap. */
2084
792
      if (accel->digest.may_intersect (buffer->digest) &&
2085
75
    ((buffer->flags & HB_BUFFER_FLAG_PRODUCE_UNSAFE_TO_CONCAT) ||
2086
75
     accel->digest_second.may_intersect (buffer->digest)))
2087
25
      {
2088
25
  c.set_lookup_index (lookup_index);
2089
25
  c.set_lookup_mask (lookup.mask, false);
2090
25
  c.set_auto_zwj (lookup.auto_zwj, false);
2091
25
  c.set_auto_zwnj (lookup.auto_zwnj, false);
2092
25
  c.set_random (lookup.random);
2093
25
  c.set_per_syllable (lookup.per_syllable, false);
2094
  /* apply_string's set_lookup_props initializes the iterators. */
2095
2096
25
  apply_string<Proxy> (&c,
2097
25
           proxy.accel.table->get_lookup (lookup_index),
2098
25
           *accel);
2099
25
      }
2100
767
      else if (buffer->messaging ())
2101
0
  (void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag));
2102
2103
792
      if (buffer->messaging ())
2104
0
  (void) buffer->message (font, "end lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag));
2105
792
    }
2106
2107
78.1k
    if (stage->pause_func)
2108
0
    {
2109
0
      if (stage->pause_func (plan, font, buffer))
2110
0
      {
2111
  /* Refresh working buffer digest since buffer changed. */
2112
0
  buffer->update_digest ();
2113
0
      }
2114
0
    }
2115
78.1k
  }
2116
78.1k
}
2117
2118
void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
2119
78.1k
{
2120
78.1k
  GSUBProxy proxy (font->face);
2121
78.1k
  char tag[5] = {0};
2122
78.1k
  hb_tag_to_string (chosen_script[0], tag);
2123
78.1k
  if (buffer->messaging () &&
2124
0
      !buffer->message (font, "start table GSUB script tag '%s'", tag)) return;
2125
78.1k
  apply (proxy, plan, font, buffer);
2126
78.1k
  if (buffer->messaging ())
2127
0
    (void) buffer->message (font, "end table GSUB script tag '%s'", tag);
2128
78.1k
}
2129
2130
void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
2131
78.1k
{
2132
78.1k
  GPOSProxy proxy (font->face);
2133
78.1k
  char tag[5] = {0};
2134
78.1k
  hb_tag_to_string (chosen_script[0], tag);
2135
78.1k
  if (buffer->messaging () &&
2136
0
      !buffer->message (font, "start table GPOS script tag '%s'", tag)) return;
2137
78.1k
  apply (proxy, plan, font, buffer);
2138
78.1k
  if (buffer->messaging ())
2139
0
    (void) buffer->message (font, "end table GPOS script tag '%s'", tag);
2140
78.1k
}
2141
2142
void
2143
hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
2144
        const OT::SubstLookup &lookup,
2145
        const OT::hb_ot_layout_lookup_accelerator_t &accel)
2146
0
{
2147
0
  apply_string<GSUBProxy> (c, lookup, accel);
2148
0
}
2149
2150
#ifndef HB_NO_BASE
2151
2152
static void
2153
choose_base_tags (hb_script_t    script,
2154
      hb_language_t  language,
2155
      hb_tag_t      *script_tag,
2156
      hb_tag_t      *language_tag)
2157
0
{
2158
0
  hb_tag_t script_tags[HB_OT_MAX_TAGS_PER_SCRIPT];
2159
0
  unsigned script_count = ARRAY_LENGTH (script_tags);
2160
2161
0
  hb_tag_t language_tags[HB_OT_MAX_TAGS_PER_LANGUAGE];
2162
0
  unsigned language_count = ARRAY_LENGTH (language_tags);
2163
2164
0
  hb_ot_tags_from_script_and_language (script, language,
2165
0
               &script_count, script_tags,
2166
0
               &language_count, language_tags);
2167
2168
0
  *script_tag = script_count ? script_tags[script_count - 1] : HB_OT_TAG_DEFAULT_SCRIPT;
2169
0
  *language_tag = language_count ? language_tags[language_count - 1] : HB_OT_TAG_DEFAULT_LANGUAGE;
2170
0
}
2171
2172
/**
2173
 * hb_ot_layout_get_font_extents:
2174
 * @font: a font
2175
 * @direction: text direction.
2176
 * @script_tag:  script tag.
2177
 * @language_tag: language tag.
2178
 * @extents: (out) (nullable): font extents if found.
2179
 *
2180
 * Fetches script/language-specific font extents.  These values are
2181
 * looked up in the `BASE` table's `MinMax` records.
2182
 *
2183
 * If no such extents are found, the default extents for the font are
2184
 * fetched. As such, the return value of this function can for the
2185
 * most part be ignored.  Note that the per-script/language extents
2186
 * do not have a line-gap value, and the line-gap is set to zero in
2187
 * that case.
2188
 *
2189
 * Return value: `true` if found script/language-specific font extents.
2190
 *
2191
 * Since: 8.0.0
2192
 **/
2193
hb_bool_t
2194
hb_ot_layout_get_font_extents (hb_font_t         *font,
2195
             hb_direction_t     direction,
2196
             hb_tag_t           script_tag,
2197
             hb_tag_t           language_tag,
2198
             hb_font_extents_t *extents)
2199
0
{
2200
0
  hb_position_t min = 0, max = 0;
2201
0
  if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE,
2202
0
             &min, &max))
2203
0
  {
2204
0
    if (extents)
2205
0
    {
2206
0
      extents->ascender  = max;
2207
0
      extents->descender = min;
2208
0
      extents->line_gap  = 0;
2209
0
    }
2210
0
    return true;
2211
0
  }
2212
2213
0
  hb_font_get_extents_for_direction (font, direction, extents);
2214
0
  return false;
2215
0
}
2216
2217
/**
2218
 * hb_ot_layout_get_font_extents2:
2219
 * @font: a font
2220
 * @direction: text direction.
2221
 * @script:  script.
2222
 * @language: (nullable): language.
2223
 * @extents: (out) (nullable): font extents if found.
2224
 *
2225
 * Fetches script/language-specific font extents.  These values are
2226
 * looked up in the `BASE` table's `MinMax` records.
2227
 *
2228
 * If no such extents are found, the default extents for the font are
2229
 * fetched. As such, the return value of this function can for the
2230
 * most part be ignored.  Note that the per-script/language extents
2231
 * do not have a line-gap value, and the line-gap is set to zero in
2232
 * that case.
2233
 *
2234
 * This function is like hb_ot_layout_get_font_extents() but takes
2235
 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
2236
 *
2237
 * Return value: `true` if found script/language-specific font extents.
2238
 *
2239
 * Since: 8.0.0
2240
 **/
2241
hb_bool_t
2242
hb_ot_layout_get_font_extents2 (hb_font_t         *font,
2243
        hb_direction_t     direction,
2244
        hb_script_t        script,
2245
        hb_language_t      language,
2246
        hb_font_extents_t *extents)
2247
0
{
2248
0
  hb_tag_t script_tag, language_tag;
2249
0
  choose_base_tags (script, language, &script_tag, &language_tag);
2250
0
  return hb_ot_layout_get_font_extents (font,
2251
0
          direction,
2252
0
          script_tag,
2253
0
          language_tag,
2254
0
          extents);
2255
0
}
2256
2257
/**
2258
 * hb_ot_layout_get_horizontal_baseline_tag_for_script:
2259
 * @script: a script tag.
2260
 *
2261
 * Fetches the dominant horizontal baseline tag used by @script.
2262
 *
2263
 * Return value: dominant baseline tag for the @script.
2264
 *
2265
 * Since: 4.0.0
2266
 **/
2267
hb_ot_layout_baseline_tag_t
2268
hb_ot_layout_get_horizontal_baseline_tag_for_script (hb_script_t script)
2269
0
{
2270
  /* Keep in sync with hb_ot_layout_get_baseline_with_fallback */
2271
0
  switch ((int) script)
2272
0
  {
2273
    /* Unicode-1.1 additions */
2274
0
    case HB_SCRIPT_BENGALI:
2275
0
    case HB_SCRIPT_DEVANAGARI:
2276
0
    case HB_SCRIPT_GUJARATI:
2277
0
    case HB_SCRIPT_GURMUKHI:
2278
    /* Unicode-2.0 additions */
2279
0
    case HB_SCRIPT_TIBETAN:
2280
    /* Unicode-4.0 additions */
2281
0
    case HB_SCRIPT_LIMBU:
2282
    /* Unicode-4.1 additions */
2283
0
    case HB_SCRIPT_SYLOTI_NAGRI:
2284
    /* Unicode-5.0 additions */
2285
0
    case HB_SCRIPT_PHAGS_PA:
2286
    /* Unicode-5.2 additions */
2287
0
    case HB_SCRIPT_MEETEI_MAYEK:
2288
    /* Unicode-6.1 additions */
2289
0
    case HB_SCRIPT_SHARADA:
2290
0
    case HB_SCRIPT_TAKRI:
2291
    /* Unicode-7.0 additions */
2292
0
    case HB_SCRIPT_MODI:
2293
0
    case HB_SCRIPT_SIDDHAM:
2294
0
    case HB_SCRIPT_TIRHUTA:
2295
    /* Unicode-9.0 additions */
2296
0
    case HB_SCRIPT_MARCHEN:
2297
0
    case HB_SCRIPT_NEWA:
2298
    /* Unicode-10.0 additions */
2299
0
    case HB_SCRIPT_SOYOMBO:
2300
0
    case HB_SCRIPT_ZANABAZAR_SQUARE:
2301
    /* Unicode-11.0 additions */
2302
0
    case HB_SCRIPT_DOGRA:
2303
0
    case HB_SCRIPT_GUNJALA_GONDI:
2304
    /* Unicode-12.0 additions */
2305
0
    case HB_SCRIPT_NANDINAGARI:
2306
0
      return HB_OT_LAYOUT_BASELINE_TAG_HANGING;
2307
2308
    /* Unicode-1.1 additions */
2309
0
    case HB_SCRIPT_HANGUL:
2310
0
    case HB_SCRIPT_HAN:
2311
0
    case HB_SCRIPT_HIRAGANA:
2312
0
    case HB_SCRIPT_KATAKANA:
2313
    /* Unicode-3.0 additions */
2314
0
    case HB_SCRIPT_BOPOMOFO:
2315
    /* Unicode-9.0 additions */
2316
0
    case HB_SCRIPT_TANGUT:
2317
    /* Unicode-10.0 additions */
2318
0
    case HB_SCRIPT_NUSHU:
2319
    /* Unicode-13.0 additions */
2320
0
    case HB_SCRIPT_KHITAN_SMALL_SCRIPT:
2321
    /* Unicode-18.0 additions */
2322
0
    case HB_SCRIPT_JURCHEN:
2323
0
    case HB_SCRIPT_SEAL:
2324
0
      return HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT;
2325
2326
0
    default:
2327
0
      return HB_OT_LAYOUT_BASELINE_TAG_ROMAN;
2328
0
  }
2329
0
}
2330
2331
/**
2332
 * hb_ot_layout_get_baseline:
2333
 * @font: a font
2334
 * @baseline_tag: a baseline tag
2335
 * @direction: text direction.
2336
 * @script_tag:  script tag.
2337
 * @language_tag: language tag, currently unused.
2338
 * @coord: (out) (nullable): baseline value if found.
2339
 *
2340
 * Fetches a baseline value from the font.
2341
 *
2342
 * Return value: `true` if found baseline value in the font.
2343
 *
2344
 * Since: 2.6.0
2345
 **/
2346
hb_bool_t
2347
hb_ot_layout_get_baseline (hb_font_t                   *font,
2348
         hb_ot_layout_baseline_tag_t  baseline_tag,
2349
         hb_direction_t               direction,
2350
         hb_tag_t                     script_tag,
2351
         hb_tag_t                     language_tag,
2352
         hb_position_t               *coord        /* OUT.  May be NULL. */)
2353
0
{
2354
0
  return font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord);
2355
0
}
2356
2357
/**
2358
 * hb_ot_layout_get_baseline2:
2359
 * @font: a font
2360
 * @baseline_tag: a baseline tag
2361
 * @direction: text direction.
2362
 * @script:  script.
2363
 * @language: (nullable): language, currently unused.
2364
 * @coord: (out) (nullable): baseline value if found.
2365
 *
2366
 * Fetches a baseline value from the font.
2367
 *
2368
 * This function is like hb_ot_layout_get_baseline() but takes
2369
 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
2370
 *
2371
 * Return value: `true` if found baseline value in the font.
2372
 *
2373
 * Since: 8.0.0
2374
 **/
2375
hb_bool_t
2376
hb_ot_layout_get_baseline2 (hb_font_t                   *font,
2377
          hb_ot_layout_baseline_tag_t  baseline_tag,
2378
          hb_direction_t               direction,
2379
          hb_script_t                  script,
2380
          hb_language_t                language,
2381
          hb_position_t               *coord        /* OUT.  May be NULL. */)
2382
0
{
2383
0
  hb_tag_t script_tag, language_tag;
2384
0
  choose_base_tags (script, language, &script_tag, &language_tag);
2385
0
  return hb_ot_layout_get_baseline (font,
2386
0
            baseline_tag,
2387
0
            direction,
2388
0
            script_tag,
2389
0
            language_tag,
2390
0
            coord);
2391
0
}
2392
2393
/**
2394
 * hb_ot_layout_get_baseline_with_fallback:
2395
 * @font: a font
2396
 * @baseline_tag: a baseline tag
2397
 * @direction: text direction.
2398
 * @script_tag:  script tag.
2399
 * @language_tag: language tag, currently unused.
2400
 * @coord: (out): baseline value if found.
2401
 *
2402
 * Fetches a baseline value from the font, and synthesizes
2403
 * it if the font does not have it.
2404
 *
2405
 * Since: 4.0.0
2406
 **/
2407
void
2408
hb_ot_layout_get_baseline_with_fallback (hb_font_t                   *font,
2409
           hb_ot_layout_baseline_tag_t  baseline_tag,
2410
           hb_direction_t               direction,
2411
           hb_tag_t                     script_tag,
2412
           hb_tag_t                     language_tag,
2413
           hb_position_t               *coord /* OUT */)
2414
0
{
2415
0
  if (hb_ot_layout_get_baseline (font,
2416
0
         baseline_tag,
2417
0
         direction,
2418
0
         script_tag,
2419
0
         language_tag,
2420
0
         coord))
2421
0
    return;
2422
2423
  /* Synthesize missing baselines.
2424
   * See https://www.w3.org/TR/css-inline-3/#baseline-synthesis-fonts
2425
   */
2426
0
  switch (baseline_tag)
2427
0
  {
2428
0
  case HB_OT_LAYOUT_BASELINE_TAG_ROMAN:
2429
0
    *coord = 0; // FIXME origin ?
2430
0
    break;
2431
2432
0
  case HB_OT_LAYOUT_BASELINE_TAG_MATH:
2433
0
    {
2434
0
      hb_codepoint_t glyph;
2435
0
      hb_glyph_extents_t extents;
2436
0
      if (HB_DIRECTION_IS_HORIZONTAL (direction) &&
2437
0
    (hb_font_get_nominal_glyph (font, 0x2212u, &glyph) ||
2438
0
     hb_font_get_nominal_glyph (font, '-', &glyph)) &&
2439
0
    hb_font_get_glyph_extents (font, glyph, &extents))
2440
0
      {
2441
0
  *coord = hb_saturate_add (extents.y_bearing, extents.height / 2);
2442
0
      }
2443
0
      else
2444
0
      {
2445
0
  hb_position_t x_height = font->y_scale / 2;
2446
0
#ifndef HB_NO_METRICS
2447
0
  hb_ot_metrics_get_position_with_fallback (font, HB_OT_METRICS_TAG_X_HEIGHT, &x_height);
2448
0
#endif
2449
0
  *coord = x_height / 2;
2450
0
      }
2451
0
    }
2452
0
    break;
2453
2454
0
  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT:
2455
0
  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT:
2456
0
    {
2457
0
      hb_position_t embox_top, embox_bottom;
2458
2459
0
      hb_ot_layout_get_baseline_with_fallback (font,
2460
0
                 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
2461
0
                 direction,
2462
0
                 script_tag,
2463
0
                 language_tag,
2464
0
                 &embox_top);
2465
0
      hb_ot_layout_get_baseline_with_fallback (font,
2466
0
                 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
2467
0
                 direction,
2468
0
                 script_tag,
2469
0
                 language_tag,
2470
0
                 &embox_bottom);
2471
2472
0
      if (baseline_tag == HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT)
2473
0
  *coord = hb_saturate_add (embox_top,
2474
0
          hb_clamp_to<hb_position_t> (((int64_t) embox_bottom - embox_top) / 10));
2475
0
      else
2476
0
  *coord = hb_saturate_add (embox_bottom,
2477
0
          hb_clamp_to<hb_position_t> (((int64_t) embox_top - embox_bottom) / 10));
2478
0
    }
2479
0
    break;
2480
2481
0
  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT:
2482
0
    if (hb_ot_layout_get_baseline (font,
2483
0
           HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
2484
0
           direction,
2485
0
           script_tag,
2486
0
           language_tag,
2487
0
           coord))
2488
0
      *coord = hb_saturate_add (*coord,
2489
0
        HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale);
2490
0
    else
2491
0
    {
2492
0
      hb_font_extents_t font_extents;
2493
0
      hb_font_get_extents_for_direction (font, direction, &font_extents);
2494
0
      *coord = font_extents.ascender;
2495
0
    }
2496
0
    break;
2497
2498
0
  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT:
2499
0
    if (hb_ot_layout_get_baseline (font,
2500
0
           HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
2501
0
           direction,
2502
0
           script_tag,
2503
0
           language_tag,
2504
0
           coord))
2505
0
      *coord = hb_saturate_sub (*coord,
2506
0
        HB_DIRECTION_IS_HORIZONTAL (direction) ? font->y_scale : font->x_scale);
2507
0
    else
2508
0
    {
2509
0
      hb_font_extents_t font_extents;
2510
0
      hb_font_get_extents_for_direction (font, direction, &font_extents);
2511
0
      *coord = font_extents.descender;
2512
0
    }
2513
0
    break;
2514
2515
0
  case HB_OT_LAYOUT_BASELINE_TAG_HANGING:
2516
0
    if (HB_DIRECTION_IS_HORIZONTAL (direction))
2517
0
    {
2518
0
      hb_codepoint_t ch;
2519
0
      hb_codepoint_t glyph;
2520
0
      hb_glyph_extents_t extents;
2521
2522
      /* Keep in sync with hb_ot_layout_get_horizontal_baseline_for_script */
2523
0
      switch ((int) script_tag)
2524
0
      {
2525
      /* Unicode-1.1 additions */
2526
0
      case HB_SCRIPT_BENGALI:          ch = 0x0995u; break;
2527
0
      case HB_SCRIPT_DEVANAGARI:       ch = 0x0915u; break;
2528
0
      case HB_SCRIPT_GUJARATI:         ch = 0x0a95u; break;
2529
0
      case HB_SCRIPT_GURMUKHI:         ch = 0x0a15u; break;
2530
      /* Unicode-2.0 additions */
2531
0
      case HB_SCRIPT_TIBETAN:          ch = 0x0f40u; break;
2532
      /* Unicode-4.0 additions */
2533
0
      case HB_SCRIPT_LIMBU:            ch = 0x1901u; break;
2534
      /* Unicode-4.1 additions */
2535
0
      case HB_SCRIPT_SYLOTI_NAGRI:     ch = 0xa807u; break;
2536
      /* Unicode-5.0 additions */
2537
0
      case HB_SCRIPT_PHAGS_PA:         ch = 0xa840u; break;
2538
      /* Unicode-5.2 additions */
2539
0
      case HB_SCRIPT_MEETEI_MAYEK:     ch = 0xabc0u; break;
2540
      /* Unicode-6.1 additions */
2541
0
      case HB_SCRIPT_SHARADA:          ch = 0x11191u; break;
2542
0
      case HB_SCRIPT_TAKRI:            ch = 0x1168cu; break;
2543
      /* Unicode-7.0 additions */
2544
0
      case HB_SCRIPT_MODI:             ch = 0x1160eu;break;
2545
0
      case HB_SCRIPT_SIDDHAM:          ch = 0x11590u; break;
2546
0
      case HB_SCRIPT_TIRHUTA:          ch = 0x1148fu; break;
2547
      /* Unicode-9.0 additions */
2548
0
      case HB_SCRIPT_MARCHEN:          ch = 0x11c72u; break;
2549
0
      case HB_SCRIPT_NEWA:             ch = 0x1140eu; break;
2550
      /* Unicode-10.0 additions */
2551
0
      case HB_SCRIPT_SOYOMBO:          ch = 0x11a5cu; break;
2552
0
      case HB_SCRIPT_ZANABAZAR_SQUARE: ch = 0x11a0bu; break;
2553
      /* Unicode-11.0 additions */
2554
0
      case HB_SCRIPT_DOGRA:            ch = 0x1180au; break;
2555
0
      case HB_SCRIPT_GUNJALA_GONDI:    ch = 0x11d6cu; break;
2556
      /* Unicode-12.0 additions */
2557
0
      case HB_SCRIPT_NANDINAGARI:      ch = 0x119b0u; break;
2558
0
      default:                         ch = 0;        break;
2559
0
      }
2560
2561
0
      if (ch &&
2562
0
    hb_font_get_nominal_glyph (font, ch, &glyph) &&
2563
0
    hb_font_get_glyph_extents (font, glyph, &extents))
2564
0
  *coord = extents.y_bearing;
2565
0
      else
2566
0
  *coord = hb_clamp_to<hb_position_t> ((int64_t) font->y_scale * 6 / 10); // FIXME makes assumptions about origin
2567
0
    }
2568
0
    else
2569
0
      *coord = hb_clamp_to<hb_position_t> ((int64_t) font->x_scale * 6 / 10); // FIXME makes assumptions about origin
2570
0
    break;
2571
2572
0
  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_CENTRAL:
2573
0
    {
2574
0
      hb_position_t top, bottom;
2575
0
      hb_ot_layout_get_baseline_with_fallback (font,
2576
0
                 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_TOP_OR_RIGHT,
2577
0
                 direction,
2578
0
                 script_tag,
2579
0
                 language_tag,
2580
0
                 &top);
2581
0
      hb_ot_layout_get_baseline_with_fallback (font,
2582
0
                 HB_OT_LAYOUT_BASELINE_TAG_IDEO_EMBOX_BOTTOM_OR_LEFT,
2583
0
                 direction,
2584
0
                 script_tag,
2585
0
                 language_tag,
2586
0
                 &bottom);
2587
0
      *coord = hb_clamp_to<hb_position_t> (((int64_t) top + bottom) / 2);
2588
2589
0
    }
2590
0
    break;
2591
2592
0
  case HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_CENTRAL:
2593
0
    {
2594
0
      hb_position_t top, bottom;
2595
0
      hb_ot_layout_get_baseline_with_fallback (font,
2596
0
                 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_TOP_OR_RIGHT,
2597
0
                 direction,
2598
0
                 script_tag,
2599
0
                 language_tag,
2600
0
                 &top);
2601
0
      hb_ot_layout_get_baseline_with_fallback (font,
2602
0
                 HB_OT_LAYOUT_BASELINE_TAG_IDEO_FACE_BOTTOM_OR_LEFT,
2603
0
                 direction,
2604
0
                 script_tag,
2605
0
                 language_tag,
2606
0
                 &bottom);
2607
0
      *coord = hb_clamp_to<hb_position_t> (((int64_t) top + bottom) / 2);
2608
2609
0
    }
2610
0
    break;
2611
2612
0
  case _HB_OT_LAYOUT_BASELINE_TAG_MAX_VALUE:
2613
0
  default:
2614
0
    *coord = 0;
2615
0
    break;
2616
0
  }
2617
0
}
2618
2619
/**
2620
 * hb_ot_layout_get_baseline_with_fallback2:
2621
 * @font: a font
2622
 * @baseline_tag: a baseline tag
2623
 * @direction: text direction.
2624
 * @script:  script.
2625
 * @language: (nullable): language, currently unused.
2626
 * @coord: (out): baseline value if found.
2627
 *
2628
 * Fetches a baseline value from the font, and synthesizes
2629
 * it if the font does not have it.
2630
 *
2631
 * This function is like hb_ot_layout_get_baseline_with_fallback() but takes
2632
 * #hb_script_t and #hb_language_t instead of OpenType #hb_tag_t.
2633
 *
2634
 * Since: 8.0.0
2635
 **/
2636
void
2637
hb_ot_layout_get_baseline_with_fallback2 (hb_font_t                   *font,
2638
            hb_ot_layout_baseline_tag_t  baseline_tag,
2639
            hb_direction_t               direction,
2640
            hb_script_t                  script,
2641
            hb_language_t                language,
2642
            hb_position_t               *coord        /* OUT */)
2643
0
{
2644
0
  hb_tag_t script_tag, language_tag;
2645
0
  choose_base_tags (script, language, &script_tag, &language_tag);
2646
0
  hb_ot_layout_get_baseline_with_fallback (font,
2647
0
             baseline_tag,
2648
0
             direction,
2649
0
             script_tag,
2650
0
             language_tag,
2651
0
             coord);
2652
0
}
2653
2654
#endif
2655
2656
2657
#ifndef HB_NO_LAYOUT_RARELY_USED
2658
struct hb_get_glyph_alternates_dispatch_t :
2659
       hb_dispatch_context_t<hb_get_glyph_alternates_dispatch_t, unsigned>
2660
{
2661
0
  static return_t default_return_value () { return 0; }
2662
0
  bool stop_sublookup_iteration (return_t r) const { return r; }
2663
2664
  private:
2665
  template <typename T, typename ...Ts> auto
2666
  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
2667
  ( obj.get_glyph_alternates (std::forward<Ts> (ds)...) )
2668
  template <typename T, typename ...Ts> auto
2669
  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
2670
  ( default_return_value () )
2671
  public:
2672
  template <typename T, typename ...Ts> auto
2673
  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
2674
  ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
2675
};
2676
2677
/**
2678
 * hb_ot_layout_lookup_get_glyph_alternates:
2679
 * @face: a face.
2680
 * @lookup_index: index of the feature lookup to query.
2681
 * @glyph: a glyph id.
2682
 * @start_offset: starting offset.
2683
 * @alternate_count: (inout) (nullable): Input = the maximum number of alternate glyphs to return;
2684
 *                   Output = the actual number of alternate glyphs returned (may be zero).
2685
 * @alternate_glyphs: (out caller-allocates) (array length=alternate_count) (nullable): A glyphs buffer.
2686
 *                    Alternate glyphs associated with the glyph id.
2687
 *
2688
 * Fetches alternates of a glyph from a given GSUB lookup index. Note that for one-to-one GSUB
2689
 * glyph substitutions, this function fetches the substituted glyph.
2690
 *
2691
 * Return value: Total number of alternates found in the specific lookup index for the given glyph id.
2692
 *
2693
 * Since: 2.6.8
2694
 **/
2695
HB_EXTERN unsigned
2696
hb_ot_layout_lookup_get_glyph_alternates (hb_face_t      *face,
2697
            unsigned        lookup_index,
2698
            hb_codepoint_t  glyph,
2699
            unsigned        start_offset,
2700
            unsigned       *alternate_count  /* IN/OUT.  May be NULL. */,
2701
            hb_codepoint_t *alternate_glyphs /* OUT.     May be NULL. */)
2702
0
{
2703
0
  hb_get_glyph_alternates_dispatch_t c;
2704
0
  const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index);
2705
0
  auto ret = lookup.dispatch (&c, glyph, start_offset, alternate_count, alternate_glyphs);
2706
0
  if (!ret && alternate_count) *alternate_count = 0;
2707
0
  return ret;
2708
0
}
2709
2710
struct hb_collect_glyph_alternates_dispatch_t :
2711
       hb_dispatch_context_t<hb_collect_glyph_alternates_dispatch_t, bool>
2712
{
2713
0
  static return_t default_return_value () { return false; }
2714
0
  bool stop_sublookup_iteration (return_t r) const { return false; }
2715
2716
  private:
2717
  template <typename T, typename ...Ts> auto
2718
  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
2719
  ( (obj.collect_glyph_alternates (std::forward<Ts> (ds)...), true) )
2720
  template <typename T, typename ...Ts> auto
2721
  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
2722
  ( default_return_value () )
2723
  public:
2724
  template <typename T, typename ...Ts> auto
2725
  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
2726
  ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
2727
};
2728
2729
/**
2730
 * hb_ot_layout_lookup_collect_glyph_alternates:
2731
 * @face: a face.
2732
 * @lookup_index: index of the feature lookup to query.
2733
 * @alternate_count: (inout): mapping from glyph index to number of alternates for that glyph.
2734
 * @alternate_glyphs: (inout): mapping from encoded glyph index and alternate index, to alternate glyph ids.
2735
 *
2736
 * Collects alternates of glyphs from a given GSUB lookup index.
2737
 *
2738
 * For one-to-one GSUB glyph substitutions, this function collects the
2739
 * substituted glyph.
2740
 *
2741
 * For lookups that assign multiple alternates to a glyph, all alternate glyphs are collected.
2742
 *
2743
 * For other lookup types, nothing is performed and `false` is returned.
2744
 *
2745
 * The `alternate_count` mapping will contain the number of alternates for each glyph id.
2746
 * Upon entry, this mapping should contain the glyph ids as keys, and the number of alternates
2747
 * currently known for each glyph id as values.
2748
 *
2749
 * The `alternate_glyphs` mapping will contain the alternate glyph ids for each glyph id.
2750
 * The mapping is encoded in the following way, upon entry and after processing:
2751
 * If G is the glyph id, and A0, A1, ..., A(n-1) are the alternate glyph ids,
2752
 * the mapping will contain the following entries: (G + (i << 24)) -> A(i)
2753
 * for i = 0, 1, ..., n-1 where n is the number of alternates for G as per `alternate_count`.
2754
 *
2755
 * Return value: `true` if alternates were collected, `false` otherwise.
2756
 * Since: 12.1.0
2757
 */
2758
HB_EXTERN hb_bool_t
2759
hb_ot_layout_lookup_collect_glyph_alternates (hb_face_t *face,
2760
                unsigned   lookup_index,
2761
                hb_map_t  *alternate_count /* IN/OUT */,
2762
                hb_map_t  *alternate_glyphs /* IN/OUT */)
2763
0
{
2764
0
  hb_collect_glyph_alternates_dispatch_t c;
2765
0
  const OT::SubstLookup &lookup = face->table.GSUB->table->get_lookup (lookup_index);
2766
0
  return lookup.dispatch (&c, alternate_count, alternate_glyphs);
2767
0
}
2768
2769
struct hb_position_single_dispatch_t :
2770
       hb_dispatch_context_t<hb_position_single_dispatch_t, bool>
2771
{
2772
0
  static return_t default_return_value () { return false; }
2773
0
  bool stop_sublookup_iteration (return_t r) const { return r; }
2774
2775
  private:
2776
  template <typename T, typename ...Ts> auto
2777
  _dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
2778
  ( obj.position_single (std::forward<Ts> (ds)...) )
2779
  template <typename T, typename ...Ts> auto
2780
  _dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
2781
  ( default_return_value () )
2782
  public:
2783
  template <typename T, typename ...Ts> auto
2784
  dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
2785
  ( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
2786
};
2787
2788
/**
2789
 * hb_ot_layout_lookup_get_optical_bound:
2790
 * @font: a font.
2791
 * @lookup_index: index of the feature lookup to query.
2792
 * @direction: edge of the glyph to query.
2793
 * @glyph: a glyph id.
2794
 *
2795
 * Fetches the optical bound of a glyph positioned at the margin of text.
2796
 * The direction identifies which edge of the glyph to query.
2797
 *
2798
 * Return value: Adjustment value. Negative values mean the glyph will stick out of the margin.
2799
 *
2800
 * Since: 5.3.0
2801
 **/
2802
hb_position_t
2803
hb_ot_layout_lookup_get_optical_bound (hb_font_t      *font,
2804
               unsigned        lookup_index,
2805
               hb_direction_t  direction,
2806
               hb_codepoint_t  glyph)
2807
0
{
2808
0
  const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index);
2809
0
  hb_blob_t *blob = font->face->table.GPOS->get_blob ();
2810
0
  hb_glyph_position_t pos = {0};
2811
0
  hb_position_single_dispatch_t c;
2812
0
  lookup.dispatch (&c, font, blob, direction, glyph, pos);
2813
0
  hb_position_t ret = 0;
2814
0
  switch (direction)
2815
0
  {
2816
0
    case HB_DIRECTION_LTR:
2817
0
      ret = pos.x_offset;
2818
0
      break;
2819
0
    case HB_DIRECTION_RTL:
2820
0
      ret = hb_saturate_sub (pos.x_advance, pos.x_offset);
2821
0
      break;
2822
0
    case HB_DIRECTION_TTB:
2823
0
      ret = pos.y_offset;
2824
0
      break;
2825
0
    case HB_DIRECTION_BTT:
2826
0
      ret = hb_saturate_sub (pos.y_advance, pos.y_offset);
2827
0
      break;
2828
0
    case HB_DIRECTION_INVALID:
2829
0
    default:
2830
0
      break;
2831
0
  }
2832
0
  return ret;
2833
0
}
2834
#endif
2835
2836
2837
#endif