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