• 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" // Just so we compile it; unused otherwise.
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-lcar-table.hh"
55 #include "hb-aat-layout-morx-table.hh"
56 
57 #include "hb-aat-layout-opbd-table.hh" // Just so we compile it; unused otherwise.
58 
59 /**
60  * SECTION:hb-ot-layout
61  * @title: hb-ot-layout
62  * @short_description: OpenType Layout
63  * @include: hb-ot.h
64  *
65  * Functions for querying OpenType Layout features in the font face.
66  **/
67 
68 
69 /*
70  * kern
71  */
72 
73 #ifndef HB_NO_OT_KERN
74 /**
75  * hb_ot_layout_has_kerning:
76  * @face: The #hb_face_t to work on
77  *
78  * Tests whether a face includes any kerning data in the 'kern' table.
79  * Does NOT test for kerning lookups in the GPOS table.
80  *
81  * Return value: true if data found, false otherwise
82  *
83  **/
84 bool
hb_ot_layout_has_kerning(hb_face_t * face)85 hb_ot_layout_has_kerning (hb_face_t *face)
86 {
87   return face->table.kern->has_data ();
88 }
89 
90 /**
91  * hb_ot_layout_has_machine_kerning:
92  * @face: The #hb_face_t to work on
93  *
94  * Tests whether a face includes any state-machine kerning in the 'kern' table.
95  * Does NOT examine the GPOS table.
96  *
97  * Return value: true if data found, false otherwise
98  *
99  **/
100 bool
hb_ot_layout_has_machine_kerning(hb_face_t * face)101 hb_ot_layout_has_machine_kerning (hb_face_t *face)
102 {
103   return face->table.kern->has_state_machine ();
104 }
105 
106 /**
107  * hb_ot_layout_has_cross_kerning:
108  * @face: The #hb_face_t to work on
109  *
110  * Tests whether a face has any cross-stream kerning (i.e., kerns
111  * that make adjustments perpendicular to the direction of the text
112  * flow: Y adjustments in horizontal text or X adjustments in
113  * vertical text) in the 'kern' table.
114  *
115  * Does NOT examine the GPOS table.
116  *
117  * Return value: true is data found, false otherwise
118  *
119  **/
120 bool
hb_ot_layout_has_cross_kerning(hb_face_t * face)121 hb_ot_layout_has_cross_kerning (hb_face_t *face)
122 {
123   return face->table.kern->has_cross_stream ();
124 }
125 
126 void
hb_ot_layout_kern(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer)127 hb_ot_layout_kern (const hb_ot_shape_plan_t *plan,
128 		   hb_font_t *font,
129 		   hb_buffer_t  *buffer)
130 {
131   hb_blob_t *blob = font->face->table.kern.get_blob ();
132   const AAT::kern& kern = *blob->as<AAT::kern> ();
133 
134   AAT::hb_aat_apply_context_t c (plan, font, buffer, blob);
135 
136   kern.apply (&c);
137 }
138 #endif
139 
140 
141 /*
142  * GDEF
143  */
144 
145 bool
is_blacklisted(hb_blob_t * blob,hb_face_t * face) const146 OT::GDEF::is_blacklisted (hb_blob_t *blob,
147 			  hb_face_t *face) const
148 {
149 #ifdef HB_NO_OT_LAYOUT_BLACKLIST
150   return false;
151 #endif
152   /* The ugly business of blacklisting individual fonts' tables happen here!
153    * See this thread for why we finally had to bend in and do this:
154    * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html
155    *
156    * In certain versions of Times New Roman Italic and Bold Italic,
157    * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark)
158    * in GDEF.  Many versions of Tahoma have bad GDEF tables that
159    * incorrectly classify some spacing marks such as certain IPA
160    * symbols as glyph class 3. So do older versions of Microsoft
161    * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04.
162    *
163    * Nuke the GDEF tables of to avoid unwanted width-zeroing.
164    *
165    * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925
166    *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279693
167    *     https://bugzilla.mozilla.org/show_bug.cgi?id=1279875
168    */
169   switch HB_CODEPOINT_ENCODE3(blob->length,
170 			      face->table.GSUB->table.get_length (),
171 			      face->table.GPOS->table.get_length ())
172   {
173     /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */
174     case HB_CODEPOINT_ENCODE3 (442, 2874, 42038):
175     /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */
176     case HB_CODEPOINT_ENCODE3 (430, 2874, 40662):
177     /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */
178     case HB_CODEPOINT_ENCODE3 (442, 2874, 39116):
179     /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */
180     case HB_CODEPOINT_ENCODE3 (430, 2874, 39374):
181     /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */
182     case HB_CODEPOINT_ENCODE3 (490, 3046, 41638):
183     /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */
184     case HB_CODEPOINT_ENCODE3 (478, 3046, 41902):
185     /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c  tahoma.ttf from Windows 8 */
186     case HB_CODEPOINT_ENCODE3 (898, 12554, 46470):
187     /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc  tahomabd.ttf from Windows 8 */
188     case HB_CODEPOINT_ENCODE3 (910, 12566, 47732):
189     /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e  tahoma.ttf from Windows 8.1 */
190     case HB_CODEPOINT_ENCODE3 (928, 23298, 59332):
191     /* sha1sum:6d400781948517c3c0441ba42acb309584b73033  tahomabd.ttf from Windows 8.1 */
192     case HB_CODEPOINT_ENCODE3 (940, 23310, 60732):
193     /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
194     case HB_CODEPOINT_ENCODE3 (964, 23836, 60072):
195     /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
196     case HB_CODEPOINT_ENCODE3 (976, 23832, 61456):
197     /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846  tahoma.ttf from Windows 10 */
198     case HB_CODEPOINT_ENCODE3 (994, 24474, 60336):
199     /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343  tahomabd.ttf from Windows 10 */
200     case HB_CODEPOINT_ENCODE3 (1006, 24470, 61740):
201     /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
202     case HB_CODEPOINT_ENCODE3 (1006, 24576, 61346):
203     /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */
204     case HB_CODEPOINT_ENCODE3 (1018, 24572, 62828):
205     /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5  tahoma.ttf from Windows 10 AU */
206     case HB_CODEPOINT_ENCODE3 (1006, 24576, 61352):
207     /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2  tahomabd.ttf from Windows 10 AU */
208     case HB_CODEPOINT_ENCODE3 (1018, 24572, 62834):
209     /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7  Tahoma.ttf from Mac OS X 10.9 */
210     case HB_CODEPOINT_ENCODE3 (832, 7324, 47162):
211     /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba  Tahoma Bold.ttf from Mac OS X 10.9 */
212     case HB_CODEPOINT_ENCODE3 (844, 7302, 45474):
213     /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc  himalaya.ttf from Windows 7 */
214     case HB_CODEPOINT_ENCODE3 (180, 13054, 7254):
215     /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0  himalaya.ttf from Windows 8 */
216     case HB_CODEPOINT_ENCODE3 (192, 12638, 7254):
217     /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427  himalaya.ttf from Windows 8.1 */
218     case HB_CODEPOINT_ENCODE3 (192, 12690, 7254):
219     /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44  cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */
220     /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371  cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */
221     case HB_CODEPOINT_ENCODE3 (188, 248, 3852):
222     /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f  cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */
223     /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b  cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */
224     case HB_CODEPOINT_ENCODE3 (188, 264, 3426):
225     /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */
226     case HB_CODEPOINT_ENCODE3 (1058, 47032, 11818):
227     /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/
228     case HB_CODEPOINT_ENCODE3 (1046, 47030, 12600):
229     /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */
230     case HB_CODEPOINT_ENCODE3 (1058, 71796, 16770):
231     /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */
232     case HB_CODEPOINT_ENCODE3 (1046, 71790, 17862):
233     /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */
234     case HB_CODEPOINT_ENCODE3 (1046, 71788, 17112):
235     /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */
236     case HB_CODEPOINT_ENCODE3 (1058, 71794, 17514):
237     /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */
238     case HB_CODEPOINT_ENCODE3 (1330, 109904, 57938):
239     /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */
240     case HB_CODEPOINT_ENCODE3 (1330, 109904, 58972):
241     /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85  Padauk.ttf
242      *  "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */
243     case HB_CODEPOINT_ENCODE3 (1004, 59092, 14836):
244       return true;
245   }
246   return false;
247 }
248 
249 static void
_hb_ot_layout_set_glyph_props(hb_font_t * font,hb_buffer_t * buffer)250 _hb_ot_layout_set_glyph_props (hb_font_t *font,
251 			       hb_buffer_t *buffer)
252 {
253   _hb_buffer_assert_gsubgpos_vars (buffer);
254 
255   const OT::GDEF &gdef = *font->face->table.GDEF->table;
256   unsigned int count = buffer->len;
257   for (unsigned int i = 0; i < count; i++)
258   {
259     _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint));
260     _hb_glyph_info_clear_lig_props (&buffer->info[i]);
261     buffer->info[i].syllable() = 0;
262   }
263 }
264 
265 /* Public API */
266 
267 /**
268  * hb_ot_layout_has_glyph_classes:
269  * @face: #hb_face_t to work upon
270  *
271  * Tests whether a face has any glyph classes defined in its GDEF table.
272  *
273  * Return value: true if data found, false otherwise
274  *
275  **/
276 hb_bool_t
hb_ot_layout_has_glyph_classes(hb_face_t * face)277 hb_ot_layout_has_glyph_classes (hb_face_t *face)
278 {
279   return face->table.GDEF->table->has_glyph_classes ();
280 }
281 
282 /**
283  * hb_ot_layout_get_glyph_class:
284  * @face: The #hb_face_t to work on
285  * @glyph: The #hb_codepoint_t code point to query
286  *
287  * Fetches the GDEF class of the requested glyph in the specified face.
288  *
289  * Return value: The #hb_ot_layout_glyph_class_t glyph class of the given code
290  * point in the GDEF table of the face.
291  *
292  * Since: 0.9.7
293  **/
294 hb_ot_layout_glyph_class_t
hb_ot_layout_get_glyph_class(hb_face_t * face,hb_codepoint_t glyph)295 hb_ot_layout_get_glyph_class (hb_face_t      *face,
296 			      hb_codepoint_t  glyph)
297 {
298   return (hb_ot_layout_glyph_class_t) face->table.GDEF->table->get_glyph_class (glyph);
299 }
300 
301 /**
302  * hb_ot_layout_get_glyphs_in_class:
303  * @face: The #hb_face_t to work on
304  * @klass: The #hb_ot_layout_glyph_class_t GDEF class to retrieve
305  * @glyphs: (out): The #hb_set_t set of all glyphs belonging to the requested
306  *          class.
307  *
308  * Retrieves the set of all glyphs from the face that belong to the requested
309  * glyph class in the face's GDEF table.
310  *
311  * Since: 0.9.7
312  **/
313 void
hb_ot_layout_get_glyphs_in_class(hb_face_t * face,hb_ot_layout_glyph_class_t klass,hb_set_t * glyphs)314 hb_ot_layout_get_glyphs_in_class (hb_face_t                  *face,
315 				  hb_ot_layout_glyph_class_t  klass,
316 				  hb_set_t                   *glyphs /* OUT */)
317 {
318   return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs);
319 }
320 
321 
322 #ifndef HB_NO_LAYOUT_UNUSED
323 /**
324  * hb_ot_layout_get_attach_points:
325  * @face: The #hb_face_t to work on
326  * @glyph: The #hb_codepoint_t code point to query
327  * @start_offset: offset of the first attachment point to retrieve
328  * @point_count: (inout) (allow-none): Input = the maximum number of attachment points to return;
329  *               Output = the actual number of attachment points returned (may be zero)
330  * @point_array: (out) (array length=point_count): The array of attachment points found for the query
331  *
332  * Fetches a list of all attachment points for the specified glyph in the GDEF
333  * table of the face. The list returned will begin at the offset provided.
334  *
335  * Useful if the client program wishes to cache the list.
336  *
337  **/
338 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)339 hb_ot_layout_get_attach_points (hb_face_t      *face,
340 				hb_codepoint_t  glyph,
341 				unsigned int    start_offset,
342 				unsigned int   *point_count /* IN/OUT */,
343 				unsigned int   *point_array /* OUT */)
344 {
345   return face->table.GDEF->table->get_attach_points (glyph,
346 						     start_offset,
347 						     point_count,
348 						     point_array);
349 }
350 /**
351  * hb_ot_layout_get_ligature_carets:
352  * @font: The #hb_font_t to work on
353  * @direction: The #hb_direction_t text direction to use
354  * @glyph: The #hb_codepoint_t code point to query
355  * @start_offset: offset of the first caret position to retrieve
356  * @caret_count: (inout) (allow-none): Input = the maximum number of caret positions to return;
357  *               Output = the actual number of caret positions returned (may be zero)
358  * @caret_array: (out) (array length=caret_count): The array of caret positions found for the query
359  *
360  * Fetches a list of the caret positions defined for a ligature glyph in the GDEF
361  * table of the font. The list returned will begin at the offset provided.
362  *
363  **/
364 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)365 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
366 				  hb_direction_t  direction,
367 				  hb_codepoint_t  glyph,
368 				  unsigned int    start_offset,
369 				  unsigned int   *caret_count /* IN/OUT */,
370 				  hb_position_t  *caret_array /* OUT */)
371 {
372   unsigned int result_caret_count = 0;
373   unsigned int result = font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, &result_caret_count, caret_array);
374   if (result)
375   {
376     if (caret_count) *caret_count = result_caret_count;
377   }
378   else
379   {
380 #ifndef HB_NO_AAT
381     result = font->face->table.lcar->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
382 #else
383     if (caret_count) *caret_count = 0;
384 #endif
385   }
386   return result;
387 }
388 #endif
389 
390 
391 /*
392  * GSUB/GPOS
393  */
394 
395 bool
is_blacklisted(hb_blob_t * blob HB_UNUSED,hb_face_t * face) const396 OT::GSUB::is_blacklisted (hb_blob_t *blob HB_UNUSED,
397 			  hb_face_t *face) const
398 {
399 #ifdef HB_NO_OT_LAYOUT_BLACKLIST
400   return false;
401 #endif
402 
403 #ifndef HB_NO_AAT_SHAPE
404   /* Mac OS X prefers morx over GSUB.  It also ships with various Indic fonts,
405    * all by 'MUTF' foundry (Tamil MN, Tamil Sangam MN, etc.), that have broken
406    * GSUB/GPOS tables.  Some have GSUB with zero scripts, those are ignored by
407    * our morx/GSUB preference code.  But if GSUB has non-zero scripts, we tend
408    * to prefer it over morx because we want to be consistent with other OpenType
409    * shapers.
410    *
411    * To work around broken Indic Mac system fonts, we ignore GSUB table if
412    * OS/2 VendorId is 'MUTF' and font has morx table as well.
413    *
414    * https://github.com/harfbuzz/harfbuzz/issues/1410
415    * https://github.com/harfbuzz/harfbuzz/issues/1348
416    * https://github.com/harfbuzz/harfbuzz/issues/1391
417    */
418   if (unlikely (face->table.OS2->achVendID == HB_TAG ('M','U','T','F') &&
419 		face->table.morx->has_data ()))
420     return true;
421 #endif
422 
423   return false;
424 }
425 
426 bool
is_blacklisted(hb_blob_t * blob HB_UNUSED,hb_face_t * face HB_UNUSED) const427 OT::GPOS::is_blacklisted (hb_blob_t *blob HB_UNUSED,
428 			  hb_face_t *face HB_UNUSED) const
429 {
430 #ifdef HB_NO_OT_LAYOUT_BLACKLIST
431   return false;
432 #endif
433   return false;
434 }
435 
436 static const OT::GSUBGPOS&
get_gsubgpos_table(hb_face_t * face,hb_tag_t table_tag)437 get_gsubgpos_table (hb_face_t *face,
438 		    hb_tag_t   table_tag)
439 {
440   switch (table_tag) {
441     case HB_OT_TAG_GSUB: return *face->table.GSUB->table;
442     case HB_OT_TAG_GPOS: return *face->table.GPOS->table;
443     default:             return Null(OT::GSUBGPOS);
444   }
445 }
446 
447 
448 /**
449  * hb_ot_layout_table_get_script_tags:
450  * @face: #hb_face_t to work upon
451  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
452  * @start_offset: offset of the first script tag to retrieve
453  * @script_count: (inout) (allow-none): Input = the maximum number of script tags to return;
454  *                Output = the actual number of script tags returned (may be zero)
455  * @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query
456  *
457  * Fetches a list of all scripts enumerated in the specified face's GSUB table
458  * or GPOS table. The list returned will begin at the offset provided.
459  *
460  **/
461 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)462 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
463 				    hb_tag_t      table_tag,
464 				    unsigned int  start_offset,
465 				    unsigned int *script_count /* IN/OUT */,
466 				    hb_tag_t     *script_tags  /* OUT */)
467 {
468   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
469 
470   return g.get_script_tags (start_offset, script_count, script_tags);
471 }
472 
473 #define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
474 
475 /**
476  * hb_ot_layout_table_find_script:
477  * @face: #hb_face_t to work upon
478  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
479  * @script_tag: #hb_tag_t of the script tag requested
480  * @script_index: (out): The index of the requested script tag
481  *
482  * Fetches the index if a given script tag in the specified face's GSUB table
483  * or GPOS table.
484  *
485  * Return value: true if the script is found, false otherwise
486  *
487  **/
488 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)489 hb_ot_layout_table_find_script (hb_face_t    *face,
490 				hb_tag_t      table_tag,
491 				hb_tag_t      script_tag,
492 				unsigned int *script_index /* OUT */)
493 {
494   static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
495   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
496 
497   if (g.find_script_index (script_tag, script_index))
498     return true;
499 
500   /* try finding 'DFLT' */
501   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
502     return false;
503 
504   /* try with 'dflt'; MS site has had typos and many fonts use it now :(.
505    * including many versions of DejaVu Sans Mono! */
506   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
507     return false;
508 
509   /* try with 'latn'; some old fonts put their features there even though
510      they're really trying to support Thai, for example :( */
511   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
512     return false;
513 
514   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
515   return false;
516 }
517 
518 #ifndef HB_DISABLE_DEPRECATED
519 /**
520  * hb_ot_layout_table_choose_script:
521  * @face: #hb_face_t to work upon
522  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
523  * @script_tags: Array of #hb_tag_t script tags
524  * @script_index: (out): The index of the requested script tag
525  * @chosen_script: (out): #hb_tag_t of the script tag requested
526  *
527  * Deprecated since 2.0.0
528  **/
529 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)530 hb_ot_layout_table_choose_script (hb_face_t      *face,
531 				  hb_tag_t        table_tag,
532 				  const hb_tag_t *script_tags,
533 				  unsigned int   *script_index  /* OUT */,
534 				  hb_tag_t       *chosen_script /* OUT */)
535 {
536   const hb_tag_t *t;
537   for (t = script_tags; *t; t++);
538   return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script);
539 }
540 #endif
541 
542 /**
543  * hb_ot_layout_table_select_script:
544  * @face: #hb_face_t to work upon
545  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
546  * @script_count: Number of script tags in the array
547  * @script_tags: Array of #hb_tag_t script tags
548  * @script_index: (out): The index of the requested script
549  * @chosen_script: (out): #hb_tag_t of the requested script
550  *
551  * Since: 2.0.0
552  **/
553 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)554 hb_ot_layout_table_select_script (hb_face_t      *face,
555 				  hb_tag_t        table_tag,
556 				  unsigned int    script_count,
557 				  const hb_tag_t *script_tags,
558 				  unsigned int   *script_index  /* OUT */,
559 				  hb_tag_t       *chosen_script /* OUT */)
560 {
561   static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "");
562   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
563   unsigned int i;
564 
565   for (i = 0; i < script_count; i++)
566   {
567     if (g.find_script_index (script_tags[i], script_index))
568     {
569       if (chosen_script)
570 	*chosen_script = script_tags[i];
571       return true;
572     }
573   }
574 
575   /* try finding 'DFLT' */
576   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) {
577     if (chosen_script)
578       *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT;
579     return false;
580   }
581 
582   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
583   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) {
584     if (chosen_script)
585       *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE;
586     return false;
587   }
588 
589   /* try with 'latn'; some old fonts put their features there even though
590      they're really trying to support Thai, for example :( */
591   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
592     if (chosen_script)
593       *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
594     return false;
595   }
596 
597   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
598   if (chosen_script)
599     *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
600   return false;
601 }
602 
603 
604 /**
605  * hb_ot_layout_table_get_feature_tags:
606  * @face: #hb_face_t to work upon
607  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
608  * @start_offset: offset of the first feature tag to retrieve
609  * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return;
610  *                 Output = the actual number of feature tags returned (may be zero)
611  * @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table
612  *
613  * Fetches a list of all feature tags in the given face's GSUB or GPOS table.
614  *
615  **/
616 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)617 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
618 				     hb_tag_t      table_tag,
619 				     unsigned int  start_offset,
620 				     unsigned int *feature_count /* IN/OUT */,
621 				     hb_tag_t     *feature_tags  /* OUT */)
622 {
623   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
624 
625   return g.get_feature_tags (start_offset, feature_count, feature_tags);
626 }
627 
628 
629 /**
630  * hb_ot_layout_table_find_feature:
631  * @face: #hb_face_t to work upon
632  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
633  * @feature_tag: The #hb_tag_t og the requested feature tag
634  * @feature_index: (out): The index of the requested feature
635  *
636  * Fetches the index for a given feature tag in the specified face's GSUB table
637  * or GPOS table.
638  *
639  * Return value: true if the feature is found, false otherwise
640  **/
641 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)642 hb_ot_layout_table_find_feature (hb_face_t    *face,
643 				 hb_tag_t      table_tag,
644 				 hb_tag_t      feature_tag,
645 				 unsigned int *feature_index /* OUT */)
646 {
647   static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
648   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
649 
650   unsigned int num_features = g.get_feature_count ();
651   for (unsigned int i = 0; i < num_features; i++)
652   {
653     if (feature_tag == g.get_feature_tag (i)) {
654       if (feature_index) *feature_index = i;
655       return true;
656     }
657   }
658 
659   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
660   return false;
661 }
662 
663 
664 /**
665  * hb_ot_layout_script_get_language_tags:
666  * @face: #hb_face_t to work upon
667  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
668  * @script_index: The index of the requested script tag
669  * @start_offset: offset of the first language tag to retrieve
670  * @language_count: (inout) (allow-none): Input = the maximum number of language tags to return;
671  *                  Output = the actual number of language tags returned (may be zero)
672  * @language_tags: (out) (array length=language_count): Array of language tags found in the table
673  *
674  * Fetches a list of language tags in the given face's GSUB or GPOS table, underneath
675  * the specified script index. The list returned will begin at the offset provided.
676  *
677  **/
678 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)679 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
680 				       hb_tag_t      table_tag,
681 				       unsigned int  script_index,
682 				       unsigned int  start_offset,
683 				       unsigned int *language_count /* IN/OUT */,
684 				       hb_tag_t     *language_tags  /* OUT */)
685 {
686   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
687 
688   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
689 }
690 
691 
692 #ifndef HB_DISABLE_DEPRECATED
693 /**
694  * hb_ot_layout_script_find_language:
695  * @face: #hb_face_t to work upon
696  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
697  * @script_index: The index of the requested script tag
698  * @language_tag: The #hb_tag_t of the requested language
699  * @language_index: The index of the requested language
700  *
701  * Fetches the index of a given language tag in the specified face's GSUB table
702  * or GPOS table, underneath the specified script tag.
703  *
704  * Return value: true if the language tag is found, false otherwise
705  *
706  * Since: ??
707  * Deprecated: ??
708  **/
709 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)710 hb_ot_layout_script_find_language (hb_face_t    *face,
711 				   hb_tag_t      table_tag,
712 				   unsigned int  script_index,
713 				   hb_tag_t      language_tag,
714 				   unsigned int *language_index)
715 {
716   return hb_ot_layout_script_select_language (face,
717 					      table_tag,
718 					      script_index,
719 					      1,
720 					      &language_tag,
721 					      language_index);
722 }
723 #endif
724 
725 
726 /**
727  * hb_ot_layout_script_select_language:
728  * @face: #hb_face_t to work upon
729  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
730  * @script_index: The index of the requested script tag
731  * @language_count: The number of languages in the specified script
732  * @language_tags: The array of language tags
733  * @language_index: (out): The index of the requested language
734  *
735  * Fetches the index of a given language tag in the specified face's GSUB table
736  * or GPOS table, underneath the specified script index.
737  *
738  * Return value: true if the language tag is found, false otherwise
739  *
740  * Since: 2.0.0
741  **/
742 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)743 hb_ot_layout_script_select_language (hb_face_t      *face,
744 				     hb_tag_t        table_tag,
745 				     unsigned int    script_index,
746 				     unsigned int    language_count,
747 				     const hb_tag_t *language_tags,
748 				     unsigned int   *language_index /* OUT */)
749 {
750   static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX), "");
751   const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
752   unsigned int i;
753 
754   for (i = 0; i < language_count; i++)
755   {
756     if (s.find_lang_sys_index (language_tags[i], language_index))
757       return true;
758   }
759 
760   /* try finding 'dflt' */
761   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
762     return false;
763 
764   if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX;
765   return false;
766 }
767 
768 
769 /**
770  * hb_ot_layout_language_get_required_feature_index:
771  * @face: #hb_face_t to work upon
772  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
773  * @script_index: The index of the requested script tag
774  * @language_index: The index of the requested language tag
775  * @feature_index: (out): The index of the requested feature
776  *
777  * Fetches the index of a requested feature in the given face's GSUB or GPOS table,
778  * underneath the specified script and language.
779  *
780  * Return value: true if the feature is found, false otherwise
781  *
782  **/
783 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)784 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
785 						  hb_tag_t      table_tag,
786 						  unsigned int  script_index,
787 						  unsigned int  language_index,
788 						  unsigned int *feature_index)
789 {
790   return hb_ot_layout_language_get_required_feature (face,
791 						     table_tag,
792 						     script_index,
793 						     language_index,
794 						     feature_index,
795 						     nullptr);
796 }
797 
798 
799 /**
800  * hb_ot_layout_language_get_required_feature:
801  * @face: #hb_face_t to work upon
802  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
803  * @script_index: The index of the requested script tag
804  * @language_index: The index of the requested language tag
805  * @feature_index: The index of the requested feature
806  * @feature_tag: (out): The #hb_tag_t of the requested feature
807  *
808  * Fetches the tag of a requested feature index in the given face's GSUB or GPOS table,
809  * underneath the specified script and language.
810  *
811  * Return value: true if the feature is found, false otherwise
812  *
813  * Since: 0.9.30
814  **/
815 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)816 hb_ot_layout_language_get_required_feature (hb_face_t    *face,
817 					    hb_tag_t      table_tag,
818 					    unsigned int  script_index,
819 					    unsigned int  language_index,
820 					    unsigned int *feature_index,
821 					    hb_tag_t     *feature_tag)
822 {
823   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
824   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
825 
826   unsigned int index = l.get_required_feature_index ();
827   if (feature_index) *feature_index = index;
828   if (feature_tag) *feature_tag = g.get_feature_tag (index);
829 
830   return l.has_required_feature ();
831 }
832 
833 
834 /**
835  * hb_ot_layout_language_get_feature_indexes:
836  * @face: #hb_face_t to work upon
837  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
838  * @script_index: The index of the requested script tag
839  * @language_index: The index of the requested language tag
840  * @start_offset: offset of the first feature tag to retrieve
841  * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return;
842  *                 Output: the actual number of feature tags returned (may be zero)
843  * @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query
844  *
845  * Fetches a list of all features in the specified face's GSUB table
846  * or GPOS table, underneath the specified script and language. The list
847  * returned will begin at the offset provided.
848  **/
849 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)850 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
851 					   hb_tag_t      table_tag,
852 					   unsigned int  script_index,
853 					   unsigned int  language_index,
854 					   unsigned int  start_offset,
855 					   unsigned int *feature_count   /* IN/OUT */,
856 					   unsigned int *feature_indexes /* OUT */)
857 {
858   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
859   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
860 
861   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
862 }
863 
864 
865 /**
866  * hb_ot_layout_language_get_feature_tags:
867  * @face: #hb_face_t to work upon
868  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
869  * @script_index: The index of the requested script tag
870  * @language_index: The index of the requested language tag
871  * @start_offset: offset of the first feature tag to retrieve
872  * @feature_count: (inout) (allow-none): Input = the maximum number of feature tags to return;
873  *                 Output = the actual number of feature tags returned (may be zero)
874  * @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query
875  *
876  * Fetches a list of all features in the specified face's GSUB table
877  * or GPOS table, underneath the specified script and language. The list
878  * returned will begin at the offset provided.
879  *
880  **/
881 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)882 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
883 					hb_tag_t      table_tag,
884 					unsigned int  script_index,
885 					unsigned int  language_index,
886 					unsigned int  start_offset,
887 					unsigned int *feature_count /* IN/OUT */,
888 					hb_tag_t     *feature_tags  /* OUT */)
889 {
890   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
891   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
892 
893   static_assert ((sizeof (unsigned int) == sizeof (hb_tag_t)), "");
894   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
895 
896   if (feature_tags) {
897     unsigned int count = *feature_count;
898     for (unsigned int i = 0; i < count; i++)
899       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
900   }
901 
902   return ret;
903 }
904 
905 
906 /**
907  * hb_ot_layout_language_find_feature:
908  * @face: #hb_face_t to work upon
909  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
910  * @script_index: The index of the requested script tag
911  * @language_index: The index of the requested language tag
912  * @feature_tag: #hb_tag_t of the feature tag requested
913  * @feature_index: (out): The index of the requested feature
914  *
915  * Fetches the index of a given feature tag in the specified face's GSUB table
916  * or GPOS table, underneath the specified script and language.
917  *
918  * Return value: true if the feature is found, false otherwise
919  *
920  **/
921 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)922 hb_ot_layout_language_find_feature (hb_face_t    *face,
923 				    hb_tag_t      table_tag,
924 				    unsigned int  script_index,
925 				    unsigned int  language_index,
926 				    hb_tag_t      feature_tag,
927 				    unsigned int *feature_index /* OUT */)
928 {
929   static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "");
930   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
931   const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
932 
933   unsigned int num_features = l.get_feature_count ();
934   for (unsigned int i = 0; i < num_features; i++) {
935     unsigned int f_index = l.get_feature_index (i);
936 
937     if (feature_tag == g.get_feature_tag (f_index)) {
938       if (feature_index) *feature_index = f_index;
939       return true;
940     }
941   }
942 
943   if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX;
944   return false;
945 }
946 
947 
948 /**
949  * hb_ot_layout_feature_get_lookups:
950  * @face: #hb_face_t to work upon
951  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
952  * @feature_index: The index of the requested feature
953  * @start_offset: offset of the first lookup to retrieve
954  * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return;
955  *                Output = the actual number of lookups returned (may be zero)
956  * @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query
957  *
958  * Fetches a list of all lookups enumerated for the specified feature, in
959  * the specified face's GSUB table or GPOS table. The list returned will
960  * begin at the offset provided.
961  *
962  * Since: 0.9.7
963  **/
964 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)965 hb_ot_layout_feature_get_lookups (hb_face_t    *face,
966 				  hb_tag_t      table_tag,
967 				  unsigned int  feature_index,
968 				  unsigned int  start_offset,
969 				  unsigned int *lookup_count   /* IN/OUT */,
970 				  unsigned int *lookup_indexes /* OUT */)
971 {
972   return hb_ot_layout_feature_with_variations_get_lookups (face,
973 							   table_tag,
974 							   feature_index,
975 							   HB_OT_LAYOUT_NO_VARIATIONS_INDEX,
976 							   start_offset,
977 							   lookup_count,
978 							   lookup_indexes);
979 }
980 
981 
982 /**
983  * hb_ot_layout_table_get_lookup_count:
984  * @face: #hb_face_t to work upon
985  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
986  *
987  * Fetches the total number of lookups enumerated in the specified
988  * face's GSUB table or GPOS table.
989  *
990  * Since: 0.9.22
991  **/
992 unsigned int
hb_ot_layout_table_get_lookup_count(hb_face_t * face,hb_tag_t table_tag)993 hb_ot_layout_table_get_lookup_count (hb_face_t    *face,
994 				     hb_tag_t      table_tag)
995 {
996   return get_gsubgpos_table (face, table_tag).get_lookup_count ();
997 }
998 
999 
1000 struct hb_collect_features_context_t
1001 {
hb_collect_features_context_thb_collect_features_context_t1002   hb_collect_features_context_t (hb_face_t *face,
1003 				 hb_tag_t   table_tag,
1004 				 hb_set_t  *feature_indexes_)
1005     : g (get_gsubgpos_table (face, table_tag)),
1006       feature_indexes (feature_indexes_),
1007       script_count(0),langsys_count(0) {}
1008 
visitedhb_collect_features_context_t1009   bool visited (const OT::Script &s)
1010   {
1011     /* We might have Null() object here.  Don't want to involve
1012      * that in the memoize.  So, detect empty objects and return. */
1013     if (unlikely (!s.has_default_lang_sys () &&
1014 		  !s.get_lang_sys_count ()))
1015       return true;
1016 
1017     if (script_count++ > HB_MAX_SCRIPTS)
1018       return true;
1019 
1020     return visited (s, visited_script);
1021   }
visitedhb_collect_features_context_t1022   bool visited (const OT::LangSys &l)
1023   {
1024     /* We might have Null() object here.  Don't want to involve
1025      * that in the memoize.  So, detect empty objects and return. */
1026     if (unlikely (!l.has_required_feature () &&
1027 		  !l.get_feature_count ()))
1028       return true;
1029 
1030     if (langsys_count++ > HB_MAX_LANGSYS)
1031       return true;
1032 
1033     return visited (l, visited_langsys);
1034   }
1035 
1036   private:
1037   template <typename T>
visitedhb_collect_features_context_t1038   bool visited (const T &p, hb_set_t &visited_set)
1039   {
1040     hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g);
1041      if (visited_set.has (delta))
1042       return true;
1043 
1044     visited_set.add (delta);
1045     return false;
1046   }
1047 
1048   public:
1049   const OT::GSUBGPOS &g;
1050   hb_set_t           *feature_indexes;
1051 
1052   private:
1053   hb_set_t visited_script;
1054   hb_set_t visited_langsys;
1055   unsigned int script_count;
1056   unsigned int langsys_count;
1057 };
1058 
1059 static void
langsys_collect_features(hb_collect_features_context_t * c,const OT::LangSys & l,const hb_tag_t * features)1060 langsys_collect_features (hb_collect_features_context_t *c,
1061 			  const OT::LangSys  &l,
1062 			  const hb_tag_t     *features)
1063 {
1064   if (c->visited (l)) return;
1065 
1066   if (!features)
1067   {
1068     /* All features. */
1069     if (l.has_required_feature ())
1070       c->feature_indexes->add (l.get_required_feature_index ());
1071 
1072     l.add_feature_indexes_to (c->feature_indexes);
1073   }
1074   else
1075   {
1076     /* Ugh. Any faster way? */
1077     for (; *features; features++)
1078     {
1079       hb_tag_t feature_tag = *features;
1080       unsigned int num_features = l.get_feature_count ();
1081       for (unsigned int i = 0; i < num_features; i++)
1082       {
1083 	unsigned int feature_index = l.get_feature_index (i);
1084 
1085 	if (feature_tag == c->g.get_feature_tag (feature_index))
1086 	{
1087 	  c->feature_indexes->add (feature_index);
1088 	  break;
1089 	}
1090       }
1091     }
1092   }
1093 }
1094 
1095 static void
script_collect_features(hb_collect_features_context_t * c,const OT::Script & s,const hb_tag_t * languages,const hb_tag_t * features)1096 script_collect_features (hb_collect_features_context_t *c,
1097 			 const OT::Script   &s,
1098 			 const hb_tag_t *languages,
1099 			 const hb_tag_t *features)
1100 {
1101   if (c->visited (s)) return;
1102 
1103   if (!languages)
1104   {
1105     /* All languages. */
1106     if (s.has_default_lang_sys ())
1107       langsys_collect_features (c,
1108 				s.get_default_lang_sys (),
1109 				features);
1110 
1111     unsigned int count = s.get_lang_sys_count ();
1112     for (unsigned int language_index = 0; language_index < count; language_index++)
1113       langsys_collect_features (c,
1114 				s.get_lang_sys (language_index),
1115 				features);
1116   }
1117   else
1118   {
1119     for (; *languages; languages++)
1120     {
1121       unsigned int language_index;
1122       if (s.find_lang_sys_index (*languages, &language_index))
1123 	langsys_collect_features (c,
1124 				  s.get_lang_sys (language_index),
1125 				  features);
1126     }
1127   }
1128 }
1129 
1130 
1131 /**
1132  * hb_ot_layout_collect_features:
1133  * @face: #hb_face_t to work upon
1134  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
1135  * @scripts: The array of scripts to collect features for
1136  * @languages: The array of languages to collect features for
1137  * @features: The array of features to collect
1138  * @feature_indexes: (out): The array of feature indexes found for the query
1139  *
1140  * Fetches a list of all feature indexes in the specified face's GSUB table
1141  * or GPOS table, underneath the specified scripts, languages, and features.
1142  * If no list of scripts is provided, all scripts will be queried. If no list
1143  * of languages is provided, all languages will be queried. If no list of
1144  * features is provided, all features will be queried.
1145  *
1146  * Since: 1.8.5
1147  **/
1148 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)1149 hb_ot_layout_collect_features (hb_face_t      *face,
1150 			       hb_tag_t        table_tag,
1151 			       const hb_tag_t *scripts,
1152 			       const hb_tag_t *languages,
1153 			       const hb_tag_t *features,
1154 			       hb_set_t       *feature_indexes /* OUT */)
1155 {
1156   hb_collect_features_context_t c (face, table_tag, feature_indexes);
1157   if (!scripts)
1158   {
1159     /* All scripts. */
1160     unsigned int count = c.g.get_script_count ();
1161     for (unsigned int script_index = 0; script_index < count; script_index++)
1162       script_collect_features (&c,
1163 			       c.g.get_script (script_index),
1164 			       languages,
1165 			       features);
1166   }
1167   else
1168   {
1169     for (; *scripts; scripts++)
1170     {
1171       unsigned int script_index;
1172       if (c.g.find_script_index (*scripts, &script_index))
1173 	script_collect_features (&c,
1174 				 c.g.get_script (script_index),
1175 				 languages,
1176 				 features);
1177     }
1178   }
1179 }
1180 
1181 
1182 /**
1183  * hb_ot_layout_collect_lookups:
1184  * @face: #hb_face_t to work upon
1185  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
1186  * @scripts: The array of scripts to collect lookups for
1187  * @languages: The array of languages to collect lookups for
1188  * @features: The array of features to collect lookups for
1189  * @lookup_indexes: (out): The array of lookup indexes found for the query
1190  *
1191  * Fetches a list of all feature-lookup indexes in the specified face's GSUB
1192  * table or GPOS table, underneath the specified scripts, languages, and
1193  * features. If no list of scripts is provided, all scripts will be queried.
1194  * If no list of languages is provided, all languages will be queried. If no
1195  * list of features is provided, all features will be queried.
1196  *
1197  * Since: 0.9.8
1198  **/
1199 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)1200 hb_ot_layout_collect_lookups (hb_face_t      *face,
1201 			      hb_tag_t        table_tag,
1202 			      const hb_tag_t *scripts,
1203 			      const hb_tag_t *languages,
1204 			      const hb_tag_t *features,
1205 			      hb_set_t       *lookup_indexes /* OUT */)
1206 {
1207   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1208 
1209   hb_set_t feature_indexes;
1210   hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes);
1211 
1212   for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID;
1213        hb_set_next (&feature_indexes, &feature_index);)
1214     g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
1215 }
1216 
1217 
1218 #ifndef HB_NO_LAYOUT_COLLECT_GLYPHS
1219 /**
1220  * hb_ot_layout_lookup_collect_glyphs:
1221  * @face: #hb_face_t to work upon
1222  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
1223  * @lookup_index: The index of the feature lookup to query
1224  * @glyphs_before: (out): Array of glyphs preceding the substitution range
1225  * @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup
1226  * @glyphs_after: (out): Array of glyphs following the substition range
1227  * @glyphs_output: (out): Array of glyphs that would be the substitued output of the lookup
1228  *
1229  * Fetches a list of all glyphs affected by the specified lookup in the
1230  * specified face's GSUB table or GPOS table.
1231  *
1232  * Since: 0.9.7
1233  **/
1234 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)1235 hb_ot_layout_lookup_collect_glyphs (hb_face_t    *face,
1236 				    hb_tag_t      table_tag,
1237 				    unsigned int  lookup_index,
1238 				    hb_set_t     *glyphs_before, /* OUT.  May be NULL */
1239 				    hb_set_t     *glyphs_input,  /* OUT.  May be NULL */
1240 				    hb_set_t     *glyphs_after,  /* OUT.  May be NULL */
1241 				    hb_set_t     *glyphs_output  /* OUT.  May be NULL */)
1242 {
1243   OT::hb_collect_glyphs_context_t c (face,
1244 				     glyphs_before,
1245 				     glyphs_input,
1246 				     glyphs_after,
1247 				     glyphs_output);
1248 
1249   switch (table_tag)
1250   {
1251     case HB_OT_TAG_GSUB:
1252     {
1253       const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
1254       l.collect_glyphs (&c);
1255       return;
1256     }
1257     case HB_OT_TAG_GPOS:
1258     {
1259       const OT::PosLookup& l = face->table.GPOS->table->get_lookup (lookup_index);
1260       l.collect_glyphs (&c);
1261       return;
1262     }
1263   }
1264 }
1265 #endif
1266 
1267 
1268 /* Variations support */
1269 
1270 
1271 /**
1272  * hb_ot_layout_table_find_feature_variations:
1273  * @face: #hb_face_t to work upon
1274  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
1275  * @coords: The variation coordinates to query
1276  * @num_coords: The number of variation coorinates
1277  * @variations_index: (out): The array of feature variations found for the query
1278  *
1279  * Fetches a list of feature variations in the specified face's GSUB table
1280  * or GPOS table, at the specified variation coordinates.
1281  *
1282  **/
1283 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)1284 hb_ot_layout_table_find_feature_variations (hb_face_t    *face,
1285 					    hb_tag_t      table_tag,
1286 					    const int    *coords,
1287 					    unsigned int  num_coords,
1288 					    unsigned int *variations_index /* out */)
1289 {
1290   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1291 
1292   return g.find_variations_index (coords, num_coords, variations_index);
1293 }
1294 
1295 
1296 /**
1297  * hb_ot_layout_feature_with_variations_get_lookups:
1298  * @face: #hb_face_t to work upon
1299  * @table_tag: HB_OT_TAG_GSUB or HB_OT_TAG_GPOS
1300  * @feature_index: The index of the feature to query
1301  * @variations_index: The index of the feature variation to query
1302  * @start_offset: offset of the first lookup to retrieve
1303  * @lookup_count: (inout) (allow-none): Input = the maximum number of lookups to return;
1304  *                Output = the actual number of lookups returned (may be zero)
1305  * @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query
1306  *
1307  * Fetches a list of all lookups enumerated for the specified feature, in
1308  * the specified face's GSUB table or GPOS table, enabled at the specified
1309  * variations index. The list returned will begin at the offset provided.
1310  *
1311  **/
1312 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)1313 hb_ot_layout_feature_with_variations_get_lookups (hb_face_t    *face,
1314 						  hb_tag_t      table_tag,
1315 						  unsigned int  feature_index,
1316 						  unsigned int  variations_index,
1317 						  unsigned int  start_offset,
1318 						  unsigned int *lookup_count /* IN/OUT */,
1319 						  unsigned int *lookup_indexes /* OUT */)
1320 {
1321   static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), "");
1322   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1323 
1324   const OT::Feature &f = g.get_feature_variation (feature_index, variations_index);
1325 
1326   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
1327 }
1328 
1329 
1330 /*
1331  * OT::GSUB
1332  */
1333 
1334 
1335 /**
1336  * hb_ot_layout_has_substitution:
1337  * @face: #hb_face_t to work upon
1338  *
1339  * Tests whether the specified face includes any GSUB substitutions.
1340  *
1341  * Return value: true if data found, false otherwise
1342  *
1343  **/
1344 hb_bool_t
hb_ot_layout_has_substitution(hb_face_t * face)1345 hb_ot_layout_has_substitution (hb_face_t *face)
1346 {
1347   return face->table.GSUB->table->has_data ();
1348 }
1349 
1350 
1351 /**
1352  * hb_ot_layout_lookup_would_substitute:
1353  * @face: #hb_face_t to work upon
1354  * @lookup_index: The index of the lookup to query
1355  * @glyphs: The sequence of glyphs to query for substitution
1356  * @glyphs_length: The length of the glyph sequence
1357  * @zero_context: #hb_bool_t indicating whether substitutions should be context-free
1358  *
1359  * Tests whether a specified lookup in the specified face would
1360  * trigger a substitution on the given glyph sequence.
1361  *
1362  * Return value: true if a substitution would be triggered, false otherwise
1363  *
1364  * Since: 0.9.7
1365  **/
1366 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)1367 hb_ot_layout_lookup_would_substitute (hb_face_t            *face,
1368 				      unsigned int          lookup_index,
1369 				      const hb_codepoint_t *glyphs,
1370 				      unsigned int          glyphs_length,
1371 				      hb_bool_t             zero_context)
1372 {
1373   if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false;
1374   OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
1375 
1376   const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
1377 
1378   return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]);
1379 }
1380 
1381 
1382 /**
1383  * hb_ot_layout_substitute_start:
1384  * @font: #hb_font_t to use
1385  * @buffer: #hb_buffer_t buffer to work upon
1386  *
1387  * Called before substitution lookups are performed, to ensure that glyph
1388  * class and other properties are set on the glyphs in the buffer.
1389  *
1390  **/
1391 void
hb_ot_layout_substitute_start(hb_font_t * font,hb_buffer_t * buffer)1392 hb_ot_layout_substitute_start (hb_font_t    *font,
1393 			       hb_buffer_t  *buffer)
1394 {
1395 _hb_ot_layout_set_glyph_props (font, buffer);
1396 }
1397 
1398 void
hb_ot_layout_delete_glyphs_inplace(hb_buffer_t * buffer,bool (* filter)(const hb_glyph_info_t * info))1399 hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer,
1400 				    bool (*filter) (const hb_glyph_info_t *info))
1401 {
1402   /* Merge clusters and delete filtered glyphs.
1403    * NOTE! We can't use out-buffer as we have positioning data. */
1404   unsigned int j = 0;
1405   unsigned int count = buffer->len;
1406   hb_glyph_info_t *info = buffer->info;
1407   hb_glyph_position_t *pos = buffer->pos;
1408   for (unsigned int i = 0; i < count; i++)
1409   {
1410     if (filter (&info[i]))
1411     {
1412       /* Merge clusters.
1413        * Same logic as buffer->delete_glyph(), but for in-place removal. */
1414 
1415       unsigned int cluster = info[i].cluster;
1416       if (i + 1 < count && cluster == info[i + 1].cluster)
1417 	continue; /* Cluster survives; do nothing. */
1418 
1419       if (j)
1420       {
1421 	/* Merge cluster backward. */
1422 	if (cluster < info[j - 1].cluster)
1423 	{
1424 	  unsigned int mask = info[i].mask;
1425 	  unsigned int old_cluster = info[j - 1].cluster;
1426 	  for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--)
1427 	    buffer->set_cluster (info[k - 1], cluster, mask);
1428 	}
1429 	continue;
1430       }
1431 
1432       if (i + 1 < count)
1433 	buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */
1434 
1435       continue;
1436     }
1437 
1438     if (j != i)
1439     {
1440       info[j] = info[i];
1441       pos[j] = pos[i];
1442     }
1443     j++;
1444   }
1445   buffer->len = j;
1446 }
1447 
1448 /**
1449  * hb_ot_layout_lookup_substitute_closure:
1450  * @face: #hb_face_t to work upon
1451  * @lookup_index: index of the feature lookup to query
1452  * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookup
1453  *
1454  * Compute the transitive closure of glyphs needed for a
1455  * specified lookup.
1456  *
1457  * Since: 0.9.7
1458  **/
1459 void
hb_ot_layout_lookup_substitute_closure(hb_face_t * face,unsigned int lookup_index,hb_set_t * glyphs)1460 hb_ot_layout_lookup_substitute_closure (hb_face_t    *face,
1461 				        unsigned int  lookup_index,
1462 				        hb_set_t     *glyphs /* OUT */)
1463 {
1464   hb_map_t done_lookups;
1465   OT::hb_closure_context_t c (face, glyphs, &done_lookups);
1466 
1467   const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
1468 
1469   l.closure (&c, lookup_index);
1470 }
1471 
1472 /**
1473  * hb_ot_layout_lookups_substitute_closure:
1474  * @face: #hb_face_t to work upon
1475  * @lookups: The set of lookups to query
1476  * @glyphs: (out): Array of glyphs comprising the transitive closure of the lookups
1477  *
1478  * Compute the transitive closure of glyphs needed for all of the
1479  * provided lookups.
1480  *
1481  * Since: 1.8.1
1482  **/
1483 void
hb_ot_layout_lookups_substitute_closure(hb_face_t * face,const hb_set_t * lookups,hb_set_t * glyphs)1484 hb_ot_layout_lookups_substitute_closure (hb_face_t      *face,
1485 					 const hb_set_t *lookups,
1486 					 hb_set_t       *glyphs /* OUT */)
1487 {
1488   hb_map_t done_lookups;
1489   OT::hb_closure_context_t c (face, glyphs, &done_lookups);
1490   const OT::GSUB& gsub = *face->table.GSUB->table;
1491 
1492   unsigned int iteration_count = 0;
1493   unsigned int glyphs_length;
1494   do
1495   {
1496     glyphs_length = glyphs->get_population ();
1497     if (lookups != nullptr)
1498     {
1499       for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);)
1500 	gsub.get_lookup (lookup_index).closure (&c, lookup_index);
1501     }
1502     else
1503     {
1504       for (unsigned int i = 0; i < gsub.get_lookup_count (); i++)
1505 	gsub.get_lookup (i).closure (&c, i);
1506     }
1507   } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES &&
1508 	   glyphs_length != glyphs->get_population ());
1509 }
1510 
1511 /*
1512  * OT::GPOS
1513  */
1514 
1515 
1516 /**
1517  * hb_ot_layout_has_positioning:
1518  * @face: #hb_face_t to work upon
1519  *
1520  * Return value: true if the face has GPOS data, false otherwise
1521  *
1522  **/
1523 hb_bool_t
hb_ot_layout_has_positioning(hb_face_t * face)1524 hb_ot_layout_has_positioning (hb_face_t *face)
1525 {
1526   return face->table.GPOS->table->has_data ();
1527 }
1528 
1529 /**
1530  * hb_ot_layout_position_start:
1531  * @font: #hb_font_t to use
1532  * @buffer: #hb_buffer_t buffer to work upon
1533  *
1534  * Called before positioning lookups are performed, to ensure that glyph
1535  * attachment types and glyph-attachment chains are set for the glyphs in the buffer.
1536  *
1537  **/
1538 void
hb_ot_layout_position_start(hb_font_t * font,hb_buffer_t * buffer)1539 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
1540 {
1541   OT::GPOS::position_start (font, buffer);
1542 }
1543 
1544 
1545 /**
1546  * hb_ot_layout_position_finish_advances:
1547  * @font: #hb_font_t to use
1548  * @buffer: #hb_buffer_t buffer to work upon
1549  *
1550  * Called after positioning lookups are performed, to finish glyph advances.
1551  *
1552  **/
1553 void
hb_ot_layout_position_finish_advances(hb_font_t * font,hb_buffer_t * buffer)1554 hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer)
1555 {
1556   OT::GPOS::position_finish_advances (font, buffer);
1557 }
1558 
1559 /**
1560  * hb_ot_layout_position_finish_offsets:
1561  * @font: #hb_font_t to use
1562  * @buffer: #hb_buffer_t buffer to work upon
1563  *
1564  * Called after positioning lookups are performed, to finish glyph offsets.
1565  *
1566  **/
1567 void
hb_ot_layout_position_finish_offsets(hb_font_t * font,hb_buffer_t * buffer)1568 hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer)
1569 {
1570   OT::GPOS::position_finish_offsets (font, buffer);
1571 }
1572 
1573 
1574 #ifndef HB_NO_LAYOUT_FEATURE_PARAMS
1575 /**
1576  * hb_ot_layout_get_size_params:
1577  * @face: #hb_face_t to work upon
1578  * @design_size: (out): The design size of the face
1579  * @subfamily_id: (out): The identifier of the face within the font subfamily
1580  * @subfamily_name_id: (out): The ‘name’ table name ID of the face within the font subfamily
1581  * @range_start: (out): The minimum size of the recommended size range for the face
1582  * @range_end: (out): The maximum size of the recommended size range for the face
1583  *
1584  * Fetches optical-size feature data (i.e., the `size` feature from GPOS). Note that
1585  * the subfamily_id and the subfamily name string (accessible via the subfamily_name_id)
1586  * as used here are defined as pertaining only to fonts within a font family that differ
1587  * specifically in their respective size ranges; other ways to differentiate fonts within
1588  * a subfamily are not covered by the `size` feature.
1589  *
1590  * For more information on this distinction, see the `size` documentation at
1591  * https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#tag-39size39
1592  *
1593  * Return value: true if data found, false otherwise
1594  *
1595  * Since: 0.9.10
1596  **/
1597 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)1598 hb_ot_layout_get_size_params (hb_face_t       *face,
1599 			      unsigned int    *design_size,       /* OUT.  May be NULL */
1600 			      unsigned int    *subfamily_id,      /* OUT.  May be NULL */
1601 			      hb_ot_name_id_t *subfamily_name_id, /* OUT.  May be NULL */
1602 			      unsigned int    *range_start,       /* OUT.  May be NULL */
1603 			      unsigned int    *range_end          /* OUT.  May be NULL */)
1604 {
1605   const OT::GPOS &gpos = *face->table.GPOS->table;
1606   const hb_tag_t tag = HB_TAG ('s','i','z','e');
1607 
1608   unsigned int num_features = gpos.get_feature_count ();
1609   for (unsigned int i = 0; i < num_features; i++)
1610   {
1611     if (tag == gpos.get_feature_tag (i))
1612     {
1613       const OT::Feature &f = gpos.get_feature (i);
1614       const OT::FeatureParamsSize &params = f.get_feature_params ().get_size_params (tag);
1615 
1616       if (params.designSize)
1617       {
1618 	if (design_size) *design_size = params.designSize;
1619 	if (subfamily_id) *subfamily_id = params.subfamilyID;
1620 	if (subfamily_name_id) *subfamily_name_id = params.subfamilyNameID;
1621 	if (range_start) *range_start = params.rangeStart;
1622 	if (range_end) *range_end = params.rangeEnd;
1623 
1624 	return true;
1625       }
1626     }
1627   }
1628 
1629   if (design_size) *design_size = 0;
1630   if (subfamily_id) *subfamily_id = 0;
1631   if (subfamily_name_id) *subfamily_name_id = HB_OT_NAME_ID_INVALID;
1632   if (range_start) *range_start = 0;
1633   if (range_end) *range_end = 0;
1634 
1635   return false;
1636 }
1637 /**
1638  * hb_ot_layout_feature_get_name_ids:
1639  * @face: #hb_face_t to work upon
1640  * @table_tag: table tag to query, "GSUB" or "GPOS".
1641  * @feature_index: index of feature to query.
1642  * @label_id: (out) (allow-none): The ‘name’ table name ID that specifies a string
1643  *            for a user-interface label for this feature. (May be NULL.)
1644  * @tooltip_id: (out) (allow-none): The ‘name’ table name ID that specifies a string
1645  *              that an application can use for tooltip text for this
1646  *              feature. (May be NULL.)
1647  * @sample_id: (out) (allow-none): The ‘name’ table name ID that specifies sample text
1648  *             that illustrates the effect of this feature. (May be NULL.)
1649  * @num_named_parameters: (out) (allow-none):  Number of named parameters. (May be zero.)
1650  * @first_param_id: (out) (allow-none): The first ‘name’ table name ID used to specify
1651  *                  strings for user-interface labels for the feature
1652  *                  parameters. (Must be zero if numParameters is zero.)
1653  *
1654  * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or
1655  * "Character Variant" ('cvXX') features.
1656  *
1657  * Return value: true if data found, false otherwise
1658  *
1659  * Since: 2.0.0
1660  **/
1661 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)1662 hb_ot_layout_feature_get_name_ids (hb_face_t       *face,
1663 				   hb_tag_t         table_tag,
1664 				   unsigned int     feature_index,
1665 				   hb_ot_name_id_t *label_id,             /* OUT.  May be NULL */
1666 				   hb_ot_name_id_t *tooltip_id,           /* OUT.  May be NULL */
1667 				   hb_ot_name_id_t *sample_id,            /* OUT.  May be NULL */
1668 				   unsigned int    *num_named_parameters, /* OUT.  May be NULL */
1669 				   hb_ot_name_id_t *first_param_id        /* OUT.  May be NULL */)
1670 {
1671   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1672 
1673   hb_tag_t feature_tag = g.get_feature_tag (feature_index);
1674   const OT::Feature &f = g.get_feature (feature_index);
1675 
1676   const OT::FeatureParams &feature_params = f.get_feature_params ();
1677   if (&feature_params != &Null (OT::FeatureParams))
1678   {
1679     const OT::FeatureParamsStylisticSet& ss_params =
1680       feature_params.get_stylistic_set_params (feature_tag);
1681     if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */
1682     {
1683       if (label_id) *label_id = ss_params.uiNameID;
1684       // ssXX features don't have the rest
1685       if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
1686       if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
1687       if (num_named_parameters) *num_named_parameters = 0;
1688       if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
1689       return true;
1690     }
1691     const OT::FeatureParamsCharacterVariants& cv_params =
1692       feature_params.get_character_variants_params (feature_tag);
1693     if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */
1694     {
1695       if (label_id) *label_id = cv_params.featUILableNameID;
1696       if (tooltip_id) *tooltip_id = cv_params.featUITooltipTextNameID;
1697       if (sample_id) *sample_id = cv_params.sampleTextNameID;
1698       if (num_named_parameters) *num_named_parameters = cv_params.numNamedParameters;
1699       if (first_param_id) *first_param_id = cv_params.firstParamUILabelNameID;
1700       return true;
1701     }
1702   }
1703 
1704   if (label_id) *label_id = HB_OT_NAME_ID_INVALID;
1705   if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID;
1706   if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID;
1707   if (num_named_parameters) *num_named_parameters = 0;
1708   if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID;
1709   return false;
1710 }
1711 /**
1712  * hb_ot_layout_feature_get_characters:
1713  * @face: #hb_face_t to work upon
1714  * @table_tag: table tag to query, "GSUB" or "GPOS".
1715  * @feature_index: index of feature to query.
1716  * @start_offset: offset of the first character to retrieve
1717  * @char_count: (inout) (allow-none): Input = the maximum number of characters to return;
1718  *              Output = the actual number of characters returned (may be zero)
1719  * @characters: (out caller-allocates) (array length=char_count): A buffer pointer.
1720  *              The Unicode codepoints of the characters for which this feature provides
1721  *               glyph variants.
1722  *
1723  * Fetches a list of the characters defined as having a variant under the specified
1724  * "Character Variant" ("cvXX") feature tag.
1725  *
1726  * <note>Note: If the char_count output value is equal to its input value, then there
1727  *       is a chance there were more characters defined under the feature tag than were
1728  *       returned. This function can be called with incrementally larger start_offset
1729  *       until the char_count output value is lower than its input value, or the size
1730  *       of the characters array can be increased.</note>
1731  *
1732  * Return value: Number of total sample characters in the cvXX feature.
1733  *
1734  * Since: 2.0.0
1735  **/
1736 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)1737 hb_ot_layout_feature_get_characters (hb_face_t      *face,
1738 				     hb_tag_t        table_tag,
1739 				     unsigned int    feature_index,
1740 				     unsigned int    start_offset,
1741 				     unsigned int   *char_count, /* IN/OUT.  May be NULL */
1742 				     hb_codepoint_t *characters  /* OUT.     May be NULL */)
1743 {
1744   const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
1745 
1746   hb_tag_t feature_tag = g.get_feature_tag (feature_index);
1747   const OT::Feature &f = g.get_feature (feature_index);
1748 
1749   const OT::FeatureParams &feature_params = f.get_feature_params ();
1750 
1751   const OT::FeatureParamsCharacterVariants& cv_params =
1752     feature_params.get_character_variants_params(feature_tag);
1753 
1754   unsigned int len = 0;
1755   if (char_count && characters && start_offset < cv_params.characters.len)
1756   {
1757     len = hb_min (cv_params.characters.len - start_offset, *char_count);
1758     for (unsigned int i = 0; i < len; ++i)
1759       characters[i] = cv_params.characters[start_offset + i];
1760   }
1761   if (char_count) *char_count = len;
1762   return cv_params.characters.len;
1763 }
1764 #endif
1765 
1766 
1767 /*
1768  * Parts of different types are implemented here such that they have direct
1769  * access to GSUB/GPOS lookups.
1770  */
1771 
1772 
1773 struct GSUBProxy
1774 {
1775   static constexpr unsigned table_index = 0u;
1776   static constexpr bool inplace = false;
1777   typedef OT::SubstLookup Lookup;
1778 
GSUBProxyGSUBProxy1779   GSUBProxy (hb_face_t *face) :
1780     table (*face->table.GSUB->table),
1781     accels (face->table.GSUB->accels) {}
1782 
1783   const OT::GSUB &table;
1784   const OT::hb_ot_layout_lookup_accelerator_t *accels;
1785 };
1786 
1787 struct GPOSProxy
1788 {
1789   static constexpr unsigned table_index = 1u;
1790   static constexpr bool inplace = true;
1791   typedef OT::PosLookup Lookup;
1792 
GPOSProxyGPOSProxy1793   GPOSProxy (hb_face_t *face) :
1794     table (*face->table.GPOS->table),
1795     accels (face->table.GPOS->accels) {}
1796 
1797   const OT::GPOS &table;
1798   const OT::hb_ot_layout_lookup_accelerator_t *accels;
1799 };
1800 
1801 
1802 static inline bool
apply_forward(OT::hb_ot_apply_context_t * c,const OT::hb_ot_layout_lookup_accelerator_t & accel)1803 apply_forward (OT::hb_ot_apply_context_t *c,
1804 	       const OT::hb_ot_layout_lookup_accelerator_t &accel)
1805 {
1806   bool ret = false;
1807   hb_buffer_t *buffer = c->buffer;
1808   while (buffer->idx < buffer->len && buffer->successful)
1809   {
1810     bool applied = false;
1811     if (accel.may_have (buffer->cur().codepoint) &&
1812 	(buffer->cur().mask & c->lookup_mask) &&
1813 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
1814      {
1815        applied = accel.apply (c);
1816      }
1817 
1818     if (applied)
1819       ret = true;
1820     else
1821       buffer->next_glyph ();
1822   }
1823   return ret;
1824 }
1825 
1826 static inline bool
apply_backward(OT::hb_ot_apply_context_t * c,const OT::hb_ot_layout_lookup_accelerator_t & accel)1827 apply_backward (OT::hb_ot_apply_context_t *c,
1828 	       const OT::hb_ot_layout_lookup_accelerator_t &accel)
1829 {
1830   bool ret = false;
1831   hb_buffer_t *buffer = c->buffer;
1832   do
1833   {
1834     if (accel.may_have (buffer->cur().codepoint) &&
1835 	(buffer->cur().mask & c->lookup_mask) &&
1836 	c->check_glyph_property (&buffer->cur(), c->lookup_props))
1837      ret |= accel.apply (c);
1838 
1839     /* The reverse lookup doesn't "advance" cursor (for good reason). */
1840     buffer->idx--;
1841 
1842   }
1843   while ((int) buffer->idx >= 0);
1844   return ret;
1845 }
1846 
1847 template <typename Proxy>
1848 static inline void
apply_string(OT::hb_ot_apply_context_t * c,const typename Proxy::Lookup & lookup,const OT::hb_ot_layout_lookup_accelerator_t & accel)1849 apply_string (OT::hb_ot_apply_context_t *c,
1850 	      const typename Proxy::Lookup &lookup,
1851 	      const OT::hb_ot_layout_lookup_accelerator_t &accel)
1852 {
1853   hb_buffer_t *buffer = c->buffer;
1854 
1855   if (unlikely (!buffer->len || !c->lookup_mask))
1856     return;
1857 
1858   c->set_lookup_props (lookup.get_props ());
1859 
1860   if (likely (!lookup.is_reverse ()))
1861   {
1862     /* in/out forward substitution/positioning */
1863     if (Proxy::table_index == 0u)
1864       buffer->clear_output ();
1865     buffer->idx = 0;
1866 
1867     bool ret;
1868     ret = apply_forward (c, accel);
1869     if (ret)
1870     {
1871       if (!Proxy::inplace)
1872 	buffer->swap_buffers ();
1873       else
1874 	assert (!buffer->has_separate_output ());
1875     }
1876   }
1877   else
1878   {
1879     /* in-place backward substitution/positioning */
1880     if (Proxy::table_index == 0u)
1881       buffer->remove_output ();
1882     buffer->idx = buffer->len - 1;
1883 
1884     apply_backward (c, accel);
1885   }
1886 }
1887 
1888 template <typename Proxy>
apply(const Proxy & proxy,const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const1889 inline void hb_ot_map_t::apply (const Proxy &proxy,
1890 				const hb_ot_shape_plan_t *plan,
1891 				hb_font_t *font,
1892 				hb_buffer_t *buffer) const
1893 {
1894   const unsigned int table_index = proxy.table_index;
1895   unsigned int i = 0;
1896   OT::hb_ot_apply_context_t c (table_index, font, buffer);
1897   c.set_recurse_func (Proxy::Lookup::apply_recurse_func);
1898 
1899   for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) {
1900     const stage_map_t *stage = &stages[table_index][stage_index];
1901     for (; i < stage->last_lookup; i++)
1902     {
1903       unsigned int lookup_index = lookups[table_index][i].index;
1904       if (!buffer->message (font, "start lookup %d", lookup_index)) continue;
1905       c.set_lookup_index (lookup_index);
1906       c.set_lookup_mask (lookups[table_index][i].mask);
1907       c.set_auto_zwj (lookups[table_index][i].auto_zwj);
1908       c.set_auto_zwnj (lookups[table_index][i].auto_zwnj);
1909       if (lookups[table_index][i].random)
1910       {
1911 	c.set_random (true);
1912 	buffer->unsafe_to_break_all ();
1913       }
1914       apply_string<Proxy> (&c,
1915 			   proxy.table.get_lookup (lookup_index),
1916 			   proxy.accels[lookup_index]);
1917       (void) buffer->message (font, "end lookup %d", lookup_index);
1918     }
1919 
1920     if (stage->pause_func)
1921     {
1922       buffer->clear_output ();
1923       stage->pause_func (plan, font, buffer);
1924     }
1925   }
1926 }
1927 
substitute(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const1928 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1929 {
1930   GSUBProxy proxy (font->face);
1931   apply (proxy, plan, font, buffer);
1932 }
1933 
position(const hb_ot_shape_plan_t * plan,hb_font_t * font,hb_buffer_t * buffer) const1934 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
1935 {
1936   GPOSProxy proxy (font->face);
1937   apply (proxy, plan, font, buffer);
1938 }
1939 
1940 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)1941 hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c,
1942 				const OT::SubstLookup &lookup,
1943 				const OT::hb_ot_layout_lookup_accelerator_t &accel)
1944 {
1945   apply_string<GSUBProxy> (c, lookup, accel);
1946 }
1947 
1948 #ifndef HB_NO_BASE
1949 /**
1950  * hb_ot_layout_get_baseline:
1951  * @font: a font
1952  * @baseline_tag: a baseline tag
1953  * @direction: text direction.
1954  * @script_tag:  script tag.
1955  * @language_tag: language tag.
1956  * @coord: (out): baseline value if found.
1957  *
1958  * Fetches a baseline value from the face.
1959  *
1960  * Return value: if found baseline value in the the font.
1961  *
1962  * Since: 2.6.0
1963  **/
1964 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)1965 hb_ot_layout_get_baseline (hb_font_t                   *font,
1966 			   hb_ot_layout_baseline_tag_t  baseline_tag,
1967 			   hb_direction_t               direction,
1968 			   hb_tag_t                     script_tag,
1969 			   hb_tag_t                     language_tag,
1970 			   hb_position_t               *coord        /* OUT.  May be NULL. */)
1971 {
1972   bool result = font->face->table.BASE->get_baseline (font, baseline_tag, direction, script_tag, language_tag, coord);
1973 
1974   if (result && coord)
1975     *coord = HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (*coord) : font->em_scale_x (*coord);
1976 
1977   return result;
1978 }
1979 #endif
1980 #endif
1981