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