Coverage Report

Created: 2023-12-12 01:56

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