• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &params = 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