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-open-type.hh" |
32 | #include "hb-ot-layout.hh" |
33 | #include "hb-ot-face.hh" |
34 | #include "hb-ot-map.hh" |
35 | #include "hb-map.hh" |
36 | |
37 | #include "hb-ot-kern-table.hh" |
38 | #include "hb-ot-gasp-table.hh" // Just so we compile it; unused otherwise. |
39 | #include "hb-ot-layout-gdef-table.hh" |
40 | #include "hb-ot-layout-gsub-table.hh" |
41 | #include "hb-ot-layout-gpos-table.hh" |
42 | #include "hb-ot-layout-base-table.hh" // Just so we compile it; unused otherwise. |
43 | #include "hb-ot-layout-jstf-table.hh" // Just so we compile it; unused otherwise. |
44 | #include "hb-ot-name-table.hh" |
45 | #include "hb-ot-os2-table.hh" |
46 | |
47 | #include "hb-aat-layout-lcar-table.hh" |
48 | #include "hb-aat-layout-morx-table.hh" |
49 | |
50 | |
51 | /** |
52 | * SECTION:hb-ot-layout |
53 | * @title: hb-ot-layout |
54 | * @short_description: OpenType Layout |
55 | * @include: hb-ot.h |
56 | * |
57 | * Functions for querying OpenType Layout features in the font face. |
58 | **/ |
59 | |
60 | |
61 | /* |
62 | * kern |
63 | */ |
64 | |
65 | bool |
66 | hb_ot_layout_has_kerning (hb_face_t *face) |
67 | { |
68 | return face->table.kern->has_data (); |
69 | } |
70 | |
71 | bool |
72 | hb_ot_layout_has_machine_kerning (hb_face_t *face) |
73 | { |
74 | return face->table.kern->has_state_machine (); |
75 | } |
76 | |
77 | bool |
78 | hb_ot_layout_has_cross_kerning (hb_face_t *face) |
79 | { |
80 | return face->table.kern->has_cross_stream (); |
81 | } |
82 | |
83 | void |
84 | hb_ot_layout_kern (const hb_ot_shape_plan_t *plan, |
85 | hb_font_t *font, |
86 | hb_buffer_t *buffer) |
87 | { |
88 | hb_blob_t *blob = font->face->table.kern.get_blob (); |
89 | const AAT::kern& kern = *blob->as<AAT::kern> (); |
90 | |
91 | AAT::hb_aat_apply_context_t c (plan, font, buffer, blob); |
92 | |
93 | kern.apply (&c); |
94 | } |
95 | |
96 | |
97 | /* |
98 | * GDEF |
99 | */ |
100 | |
101 | bool |
102 | OT::GDEF::is_blacklisted (hb_blob_t *blob, |
103 | hb_face_t *face) const |
104 | { |
105 | /* The ugly business of blacklisting individual fonts' tables happen here! |
106 | * See this thread for why we finally had to bend in and do this: |
107 | * https://lists.freedesktop.org/archives/harfbuzz/2016-February/005489.html |
108 | * |
109 | * In certain versions of Times New Roman Italic and Bold Italic, |
110 | * ASCII double quotation mark U+0022 has wrong glyph class 3 (mark) |
111 | * in GDEF. Many versions of Tahoma have bad GDEF tables that |
112 | * incorrectly classify some spacing marks such as certain IPA |
113 | * symbols as glyph class 3. So do older versions of Microsoft |
114 | * Himalaya, and the version of Cantarell shipped by Ubuntu 16.04. |
115 | * |
116 | * Nuke the GDEF tables of to avoid unwanted width-zeroing. |
117 | * |
118 | * See https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 |
119 | * https://bugzilla.mozilla.org/show_bug.cgi?id=1279693 |
120 | * https://bugzilla.mozilla.org/show_bug.cgi?id=1279875 |
121 | */ |
122 | #define ENCODE(x,y,z) (((uint64_t) (x) << 48) | ((uint64_t) (y) << 24) | (uint64_t) (z)) |
123 | switch ENCODE(blob->length, |
124 | face->table.GSUB->table.get_length (), |
125 | face->table.GPOS->table.get_length ()) |
126 | { |
127 | /* sha1sum:c5ee92f0bca4bfb7d06c4d03e8cf9f9cf75d2e8a Windows 7? timesi.ttf */ |
128 | case ENCODE (442, 2874, 42038): |
129 | /* sha1sum:37fc8c16a0894ab7b749e35579856c73c840867b Windows 7? timesbi.ttf */ |
130 | case ENCODE (430, 2874, 40662): |
131 | /* sha1sum:19fc45110ea6cd3cdd0a5faca256a3797a069a80 Windows 7 timesi.ttf */ |
132 | case ENCODE (442, 2874, 39116): |
133 | /* sha1sum:6d2d3c9ed5b7de87bc84eae0df95ee5232ecde26 Windows 7 timesbi.ttf */ |
134 | case ENCODE (430, 2874, 39374): |
135 | /* sha1sum:8583225a8b49667c077b3525333f84af08c6bcd8 OS X 10.11.3 Times New Roman Italic.ttf */ |
136 | case ENCODE (490, 3046, 41638): |
137 | /* sha1sum:ec0f5a8751845355b7c3271d11f9918a966cb8c9 OS X 10.11.3 Times New Roman Bold Italic.ttf */ |
138 | case ENCODE (478, 3046, 41902): |
139 | /* sha1sum:96eda93f7d33e79962451c6c39a6b51ee893ce8c tahoma.ttf from Windows 8 */ |
140 | case ENCODE (898, 12554, 46470): |
141 | /* sha1sum:20928dc06014e0cd120b6fc942d0c3b1a46ac2bc tahomabd.ttf from Windows 8 */ |
142 | case ENCODE (910, 12566, 47732): |
143 | /* sha1sum:4f95b7e4878f60fa3a39ca269618dfde9721a79e tahoma.ttf from Windows 8.1 */ |
144 | case ENCODE (928, 23298, 59332): |
145 | /* sha1sum:6d400781948517c3c0441ba42acb309584b73033 tahomabd.ttf from Windows 8.1 */ |
146 | case ENCODE (940, 23310, 60732): |
147 | /* tahoma.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ |
148 | case ENCODE (964, 23836, 60072): |
149 | /* tahomabd.ttf v6.04 from Windows 8.1 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ |
150 | case ENCODE (976, 23832, 61456): |
151 | /* sha1sum:e55fa2dfe957a9f7ec26be516a0e30b0c925f846 tahoma.ttf from Windows 10 */ |
152 | case ENCODE (994, 24474, 60336): |
153 | /* sha1sum:7199385abb4c2cc81c83a151a7599b6368e92343 tahomabd.ttf from Windows 10 */ |
154 | case ENCODE (1006, 24470, 61740): |
155 | /* tahoma.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ |
156 | case ENCODE (1006, 24576, 61346): |
157 | /* tahomabd.ttf v6.91 from Windows 10 x64, see https://bugzilla.mozilla.org/show_bug.cgi?id=1279925 */ |
158 | case ENCODE (1018, 24572, 62828): |
159 | /* sha1sum:b9c84d820c49850d3d27ec498be93955b82772b5 tahoma.ttf from Windows 10 AU */ |
160 | case ENCODE (1006, 24576, 61352): |
161 | /* sha1sum:2bdfaab28174bdadd2f3d4200a30a7ae31db79d2 tahomabd.ttf from Windows 10 AU */ |
162 | case ENCODE (1018, 24572, 62834): |
163 | /* sha1sum:b0d36cf5a2fbe746a3dd277bffc6756a820807a7 Tahoma.ttf from Mac OS X 10.9 */ |
164 | case ENCODE (832, 7324, 47162): |
165 | /* sha1sum:12fc4538e84d461771b30c18b5eb6bd434e30fba Tahoma Bold.ttf from Mac OS X 10.9 */ |
166 | case ENCODE (844, 7302, 45474): |
167 | /* sha1sum:eb8afadd28e9cf963e886b23a30b44ab4fd83acc himalaya.ttf from Windows 7 */ |
168 | case ENCODE (180, 13054, 7254): |
169 | /* sha1sum:73da7f025b238a3f737aa1fde22577a6370f77b0 himalaya.ttf from Windows 8 */ |
170 | case ENCODE (192, 12638, 7254): |
171 | /* sha1sum:6e80fd1c0b059bbee49272401583160dc1e6a427 himalaya.ttf from Windows 8.1 */ |
172 | case ENCODE (192, 12690, 7254): |
173 | /* 8d9267aea9cd2c852ecfb9f12a6e834bfaeafe44 cantarell-fonts-0.0.21/otf/Cantarell-Regular.otf */ |
174 | /* 983988ff7b47439ab79aeaf9a45bd4a2c5b9d371 cantarell-fonts-0.0.21/otf/Cantarell-Oblique.otf */ |
175 | case ENCODE (188, 248, 3852): |
176 | /* 2c0c90c6f6087ffbfea76589c93113a9cbb0e75f cantarell-fonts-0.0.21/otf/Cantarell-Bold.otf */ |
177 | /* 55461f5b853c6da88069ffcdf7f4dd3f8d7e3e6b cantarell-fonts-0.0.21/otf/Cantarell-Bold-Oblique.otf */ |
178 | case ENCODE (188, 264, 3426): |
179 | /* d125afa82a77a6475ac0e74e7c207914af84b37a padauk-2.80/Padauk.ttf RHEL 7.2 */ |
180 | case ENCODE (1058, 47032, 11818): |
181 | /* 0f7b80437227b90a577cc078c0216160ae61b031 padauk-2.80/Padauk-Bold.ttf RHEL 7.2*/ |
182 | case ENCODE (1046, 47030, 12600): |
183 | /* d3dde9aa0a6b7f8f6a89ef1002e9aaa11b882290 padauk-2.80/Padauk.ttf Ubuntu 16.04 */ |
184 | case ENCODE (1058, 71796, 16770): |
185 | /* 5f3c98ccccae8a953be2d122c1b3a77fd805093f padauk-2.80/Padauk-Bold.ttf Ubuntu 16.04 */ |
186 | case ENCODE (1046, 71790, 17862): |
187 | /* 6c93b63b64e8b2c93f5e824e78caca555dc887c7 padauk-2.80/Padauk-book.ttf */ |
188 | case ENCODE (1046, 71788, 17112): |
189 | /* d89b1664058359b8ec82e35d3531931125991fb9 padauk-2.80/Padauk-bookbold.ttf */ |
190 | case ENCODE (1058, 71794, 17514): |
191 | /* 824cfd193aaf6234b2b4dc0cf3c6ef576c0d00ef padauk-3.0/Padauk-book.ttf */ |
192 | case ENCODE (1330, 109904, 57938): |
193 | /* 91fcc10cf15e012d27571e075b3b4dfe31754a8a padauk-3.0/Padauk-bookbold.ttf */ |
194 | case ENCODE (1330, 109904, 58972): |
195 | /* sha1sum: c26e41d567ed821bed997e937bc0c41435689e85 Padauk.ttf |
196 | * "Padauk Regular" "Version 2.5", see https://crbug.com/681813 */ |
197 | case ENCODE (1004, 59092, 14836): |
198 | return true; |
199 | #undef ENCODE |
200 | } |
201 | return false; |
202 | } |
203 | |
204 | static void |
205 | _hb_ot_layout_set_glyph_props (hb_font_t *font, |
206 | hb_buffer_t *buffer) |
207 | { |
208 | _hb_buffer_assert_gsubgpos_vars (buffer); |
209 | |
210 | const OT::GDEF &gdef = *font->face->table.GDEF->table; |
211 | unsigned int count = buffer->len; |
212 | for (unsigned int i = 0; i < count; i++) |
213 | { |
214 | _hb_glyph_info_set_glyph_props (&buffer->info[i], gdef.get_glyph_props (buffer->info[i].codepoint)); |
215 | _hb_glyph_info_clear_lig_props (&buffer->info[i]); |
216 | buffer->info[i].syllable() = 0; |
217 | } |
218 | } |
219 | |
220 | /* Public API */ |
221 | |
222 | hb_bool_t |
223 | hb_ot_layout_has_glyph_classes (hb_face_t *face) |
224 | { |
225 | return face->table.GDEF->table->has_glyph_classes (); |
226 | } |
227 | |
228 | /** |
229 | * hb_ot_layout_get_glyph_class: |
230 | * |
231 | * Since: 0.9.7 |
232 | **/ |
233 | hb_ot_layout_glyph_class_t |
234 | hb_ot_layout_get_glyph_class (hb_face_t *face, |
235 | hb_codepoint_t glyph) |
236 | { |
237 | return (hb_ot_layout_glyph_class_t) face->table.GDEF->table->get_glyph_class (glyph); |
238 | } |
239 | |
240 | /** |
241 | * hb_ot_layout_get_glyphs_in_class: |
242 | * |
243 | * Since: 0.9.7 |
244 | **/ |
245 | void |
246 | hb_ot_layout_get_glyphs_in_class (hb_face_t *face, |
247 | hb_ot_layout_glyph_class_t klass, |
248 | hb_set_t *glyphs /* OUT */) |
249 | { |
250 | return face->table.GDEF->table->get_glyphs_in_class (klass, glyphs); |
251 | } |
252 | |
253 | unsigned int |
254 | hb_ot_layout_get_attach_points (hb_face_t *face, |
255 | hb_codepoint_t glyph, |
256 | unsigned int start_offset, |
257 | unsigned int *point_count /* IN/OUT */, |
258 | unsigned int *point_array /* OUT */) |
259 | { |
260 | return face->table.GDEF->table->get_attach_points (glyph, |
261 | start_offset, |
262 | point_count, |
263 | point_array); |
264 | } |
265 | |
266 | unsigned int |
267 | hb_ot_layout_get_ligature_carets (hb_font_t *font, |
268 | hb_direction_t direction, |
269 | hb_codepoint_t glyph, |
270 | unsigned int start_offset, |
271 | unsigned int *caret_count /* IN/OUT */, |
272 | hb_position_t *caret_array /* OUT */) |
273 | { |
274 | unsigned int result_caret_count = 0; |
275 | unsigned int result = font->face->table.GDEF->table->get_lig_carets (font, direction, glyph, start_offset, &result_caret_count, caret_array); |
276 | if (result) |
277 | { |
278 | if (caret_count) *caret_count = result_caret_count; |
279 | } |
280 | else |
281 | result = font->face->table.lcar->get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array); |
282 | return result; |
283 | } |
284 | |
285 | |
286 | /* |
287 | * GSUB/GPOS |
288 | */ |
289 | |
290 | bool |
291 | OT::GSUB::is_blacklisted (hb_blob_t *blob HB_UNUSED, |
292 | hb_face_t *face) const |
293 | { |
294 | /* Mac OS X prefers morx over GSUB. It also ships with various Indic fonts, |
295 | * all by 'MUTF' foundry (Tamil MN, Tamil Sangam MN, etc.), that have broken |
296 | * GSUB/GPOS tables. Some have GSUB with zero scripts, those are ignored by |
297 | * our morx/GSUB preference code. But if GSUB has non-zero scripts, we tend |
298 | * to prefer it over morx because we want to be consistent with other OpenType |
299 | * shapers. |
300 | * |
301 | * To work around broken Indic Mac system fonts, we ignore GSUB table if |
302 | * OS/2 VendorId is 'MUTF' and font has morx table as well. |
303 | * |
304 | * https://github.com/harfbuzz/harfbuzz/issues/1410 |
305 | * https://github.com/harfbuzz/harfbuzz/issues/1348 |
306 | * https://github.com/harfbuzz/harfbuzz/issues/1391 |
307 | */ |
308 | if (unlikely (face->table.OS2->achVendID == HB_TAG ('M','U','T','F') && |
309 | face->table.morx->has_data ())) |
310 | return true; |
311 | |
312 | return false; |
313 | } |
314 | |
315 | bool |
316 | OT::GPOS::is_blacklisted (hb_blob_t *blob HB_UNUSED, |
317 | hb_face_t *face HB_UNUSED) const |
318 | { |
319 | return false; |
320 | } |
321 | |
322 | static const OT::GSUBGPOS& |
323 | get_gsubgpos_table (hb_face_t *face, |
324 | hb_tag_t table_tag) |
325 | { |
326 | switch (table_tag) { |
327 | case HB_OT_TAG_GSUB: return *face->table.GSUB->table; |
328 | case HB_OT_TAG_GPOS: return *face->table.GPOS->table; |
329 | default: return Null(OT::GSUBGPOS); |
330 | } |
331 | } |
332 | |
333 | |
334 | unsigned int |
335 | hb_ot_layout_table_get_script_tags (hb_face_t *face, |
336 | hb_tag_t table_tag, |
337 | unsigned int start_offset, |
338 | unsigned int *script_count /* IN/OUT */, |
339 | hb_tag_t *script_tags /* OUT */) |
340 | { |
341 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
342 | |
343 | return g.get_script_tags (start_offset, script_count, script_tags); |
344 | } |
345 | |
346 | #define HB_OT_TAG_LATIN_SCRIPT HB_TAG ('l', 'a', 't', 'n') |
347 | |
348 | hb_bool_t |
349 | hb_ot_layout_table_find_script (hb_face_t *face, |
350 | hb_tag_t table_tag, |
351 | hb_tag_t script_tag, |
352 | unsigned int *script_index) |
353 | { |
354 | static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "" ); |
355 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
356 | |
357 | if (g.find_script_index (script_tag, script_index)) |
358 | return true; |
359 | |
360 | /* try finding 'DFLT' */ |
361 | if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) |
362 | return false; |
363 | |
364 | /* try with 'dflt'; MS site has had typos and many fonts use it now :(. |
365 | * including many versions of DejaVu Sans Mono! */ |
366 | if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) |
367 | return false; |
368 | |
369 | /* try with 'latn'; some old fonts put their features there even though |
370 | they're really trying to support Thai, for example :( */ |
371 | if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) |
372 | return false; |
373 | |
374 | if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; |
375 | return false; |
376 | } |
377 | |
378 | hb_bool_t |
379 | hb_ot_layout_table_choose_script (hb_face_t *face, |
380 | hb_tag_t table_tag, |
381 | const hb_tag_t *script_tags, |
382 | unsigned int *script_index, |
383 | hb_tag_t *chosen_script) |
384 | { |
385 | const hb_tag_t *t; |
386 | for (t = script_tags; *t; t++); |
387 | return hb_ot_layout_table_select_script (face, table_tag, t - script_tags, script_tags, script_index, chosen_script); |
388 | } |
389 | |
390 | /** |
391 | * hb_ot_layout_table_select_script: |
392 | * |
393 | * Since: 2.0.0 |
394 | **/ |
395 | hb_bool_t |
396 | hb_ot_layout_table_select_script (hb_face_t *face, |
397 | hb_tag_t table_tag, |
398 | unsigned int script_count, |
399 | const hb_tag_t *script_tags, |
400 | unsigned int *script_index /* OUT */, |
401 | hb_tag_t *chosen_script /* OUT */) |
402 | { |
403 | static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX), "" ); |
404 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
405 | unsigned int i; |
406 | |
407 | for (i = 0; i < script_count; i++) |
408 | { |
409 | if (g.find_script_index (script_tags[i], script_index)) |
410 | { |
411 | if (chosen_script) |
412 | *chosen_script = script_tags[i]; |
413 | return true; |
414 | } |
415 | } |
416 | |
417 | /* try finding 'DFLT' */ |
418 | if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index)) { |
419 | if (chosen_script) |
420 | *chosen_script = HB_OT_TAG_DEFAULT_SCRIPT; |
421 | return false; |
422 | } |
423 | |
424 | /* try with 'dflt'; MS site has had typos and many fonts use it now :( */ |
425 | if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index)) { |
426 | if (chosen_script) |
427 | *chosen_script = HB_OT_TAG_DEFAULT_LANGUAGE; |
428 | return false; |
429 | } |
430 | |
431 | /* try with 'latn'; some old fonts put their features there even though |
432 | they're really trying to support Thai, for example :( */ |
433 | if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) { |
434 | if (chosen_script) |
435 | *chosen_script = HB_OT_TAG_LATIN_SCRIPT; |
436 | return false; |
437 | } |
438 | |
439 | if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX; |
440 | if (chosen_script) |
441 | *chosen_script = HB_OT_LAYOUT_NO_SCRIPT_INDEX; |
442 | return false; |
443 | } |
444 | |
445 | unsigned int |
446 | hb_ot_layout_table_get_feature_tags (hb_face_t *face, |
447 | hb_tag_t table_tag, |
448 | unsigned int start_offset, |
449 | unsigned int *feature_count /* IN/OUT */, |
450 | hb_tag_t *feature_tags /* OUT */) |
451 | { |
452 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
453 | |
454 | return g.get_feature_tags (start_offset, feature_count, feature_tags); |
455 | } |
456 | |
457 | bool |
458 | hb_ot_layout_table_find_feature (hb_face_t *face, |
459 | hb_tag_t table_tag, |
460 | hb_tag_t feature_tag, |
461 | unsigned int *feature_index) |
462 | { |
463 | static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "" ); |
464 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
465 | |
466 | unsigned int num_features = g.get_feature_count (); |
467 | for (unsigned int i = 0; i < num_features; i++) |
468 | { |
469 | if (feature_tag == g.get_feature_tag (i)) { |
470 | if (feature_index) *feature_index = i; |
471 | return true; |
472 | } |
473 | } |
474 | |
475 | if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; |
476 | return false; |
477 | } |
478 | |
479 | |
480 | unsigned int |
481 | hb_ot_layout_script_get_language_tags (hb_face_t *face, |
482 | hb_tag_t table_tag, |
483 | unsigned int script_index, |
484 | unsigned int start_offset, |
485 | unsigned int *language_count /* IN/OUT */, |
486 | hb_tag_t *language_tags /* OUT */) |
487 | { |
488 | const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); |
489 | |
490 | return s.get_lang_sys_tags (start_offset, language_count, language_tags); |
491 | } |
492 | |
493 | hb_bool_t |
494 | hb_ot_layout_script_find_language (hb_face_t *face, |
495 | hb_tag_t table_tag, |
496 | unsigned int script_index, |
497 | hb_tag_t language_tag, |
498 | unsigned int *language_index) |
499 | { |
500 | return hb_ot_layout_script_select_language (face, |
501 | table_tag, |
502 | script_index, |
503 | 1, |
504 | &language_tag, |
505 | language_index); |
506 | } |
507 | |
508 | /** |
509 | * hb_ot_layout_script_select_language: |
510 | * |
511 | * Since: 2.0.0 |
512 | **/ |
513 | hb_bool_t |
514 | hb_ot_layout_script_select_language (hb_face_t *face, |
515 | hb_tag_t table_tag, |
516 | unsigned int script_index, |
517 | unsigned int language_count, |
518 | const hb_tag_t *language_tags, |
519 | unsigned int *language_index /* OUT */) |
520 | { |
521 | static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX), "" ); |
522 | const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index); |
523 | unsigned int i; |
524 | |
525 | for (i = 0; i < language_count; i++) |
526 | { |
527 | if (s.find_lang_sys_index (language_tags[i], language_index)) |
528 | return true; |
529 | } |
530 | |
531 | /* try finding 'dflt' */ |
532 | if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index)) |
533 | return false; |
534 | |
535 | if (language_index) *language_index = HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX; |
536 | return false; |
537 | } |
538 | |
539 | hb_bool_t |
540 | hb_ot_layout_language_get_required_feature_index (hb_face_t *face, |
541 | hb_tag_t table_tag, |
542 | unsigned int script_index, |
543 | unsigned int language_index, |
544 | unsigned int *feature_index) |
545 | { |
546 | return hb_ot_layout_language_get_required_feature (face, |
547 | table_tag, |
548 | script_index, |
549 | language_index, |
550 | feature_index, |
551 | nullptr); |
552 | } |
553 | |
554 | /** |
555 | * hb_ot_layout_language_get_required_feature: |
556 | * |
557 | * Since: 0.9.30 |
558 | **/ |
559 | hb_bool_t |
560 | hb_ot_layout_language_get_required_feature (hb_face_t *face, |
561 | hb_tag_t table_tag, |
562 | unsigned int script_index, |
563 | unsigned int language_index, |
564 | unsigned int *feature_index, |
565 | hb_tag_t *feature_tag) |
566 | { |
567 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
568 | const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); |
569 | |
570 | unsigned int index = l.get_required_feature_index (); |
571 | if (feature_index) *feature_index = index; |
572 | if (feature_tag) *feature_tag = g.get_feature_tag (index); |
573 | |
574 | return l.has_required_feature (); |
575 | } |
576 | |
577 | unsigned int |
578 | hb_ot_layout_language_get_feature_indexes (hb_face_t *face, |
579 | hb_tag_t table_tag, |
580 | unsigned int script_index, |
581 | unsigned int language_index, |
582 | unsigned int start_offset, |
583 | unsigned int *feature_count /* IN/OUT */, |
584 | unsigned int *feature_indexes /* OUT */) |
585 | { |
586 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
587 | const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); |
588 | |
589 | return l.get_feature_indexes (start_offset, feature_count, feature_indexes); |
590 | } |
591 | |
592 | unsigned int |
593 | hb_ot_layout_language_get_feature_tags (hb_face_t *face, |
594 | hb_tag_t table_tag, |
595 | unsigned int script_index, |
596 | unsigned int language_index, |
597 | unsigned int start_offset, |
598 | unsigned int *feature_count /* IN/OUT */, |
599 | hb_tag_t *feature_tags /* OUT */) |
600 | { |
601 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
602 | const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); |
603 | |
604 | static_assert ((sizeof (unsigned int) == sizeof (hb_tag_t)), "" ); |
605 | unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags); |
606 | |
607 | if (feature_tags) { |
608 | unsigned int count = *feature_count; |
609 | for (unsigned int i = 0; i < count; i++) |
610 | feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]); |
611 | } |
612 | |
613 | return ret; |
614 | } |
615 | |
616 | |
617 | hb_bool_t |
618 | hb_ot_layout_language_find_feature (hb_face_t *face, |
619 | hb_tag_t table_tag, |
620 | unsigned int script_index, |
621 | unsigned int language_index, |
622 | hb_tag_t feature_tag, |
623 | unsigned int *feature_index) |
624 | { |
625 | static_assert ((OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX), "" ); |
626 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
627 | const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index); |
628 | |
629 | unsigned int num_features = l.get_feature_count (); |
630 | for (unsigned int i = 0; i < num_features; i++) { |
631 | unsigned int f_index = l.get_feature_index (i); |
632 | |
633 | if (feature_tag == g.get_feature_tag (f_index)) { |
634 | if (feature_index) *feature_index = f_index; |
635 | return true; |
636 | } |
637 | } |
638 | |
639 | if (feature_index) *feature_index = HB_OT_LAYOUT_NO_FEATURE_INDEX; |
640 | return false; |
641 | } |
642 | |
643 | /** |
644 | * hb_ot_layout_feature_get_lookups: |
645 | * |
646 | * Since: 0.9.7 |
647 | **/ |
648 | unsigned int |
649 | hb_ot_layout_feature_get_lookups (hb_face_t *face, |
650 | hb_tag_t table_tag, |
651 | unsigned int feature_index, |
652 | unsigned int start_offset, |
653 | unsigned int *lookup_count /* IN/OUT */, |
654 | unsigned int *lookup_indexes /* OUT */) |
655 | { |
656 | return hb_ot_layout_feature_with_variations_get_lookups (face, |
657 | table_tag, |
658 | feature_index, |
659 | HB_OT_LAYOUT_NO_VARIATIONS_INDEX, |
660 | start_offset, |
661 | lookup_count, |
662 | lookup_indexes); |
663 | } |
664 | |
665 | /** |
666 | * hb_ot_layout_table_get_lookup_count: |
667 | * |
668 | * Since: 0.9.22 |
669 | **/ |
670 | unsigned int |
671 | hb_ot_layout_table_get_lookup_count (hb_face_t *face, |
672 | hb_tag_t table_tag) |
673 | { |
674 | return get_gsubgpos_table (face, table_tag).get_lookup_count (); |
675 | } |
676 | |
677 | |
678 | struct hb_collect_features_context_t |
679 | { |
680 | hb_collect_features_context_t (hb_face_t *face, |
681 | hb_tag_t table_tag, |
682 | hb_set_t *feature_indexes_) |
683 | : g (get_gsubgpos_table (face, table_tag)), |
684 | feature_indexes (feature_indexes_), |
685 | script_count(0),langsys_count(0) {} |
686 | |
687 | bool visited (const OT::Script &s) |
688 | { |
689 | /* We might have Null() object here. Don't want to involve |
690 | * that in the memoize. So, detect empty objects and return. */ |
691 | if (unlikely (!s.has_default_lang_sys () && |
692 | !s.get_lang_sys_count ())) |
693 | return true; |
694 | |
695 | if (script_count++ > HB_MAX_SCRIPTS) |
696 | return true; |
697 | |
698 | return visited (s, visited_script); |
699 | } |
700 | bool visited (const OT::LangSys &l) |
701 | { |
702 | /* We might have Null() object here. Don't want to involve |
703 | * that in the memoize. So, detect empty objects and return. */ |
704 | if (unlikely (!l.has_required_feature () && |
705 | !l.get_feature_count ())) |
706 | return true; |
707 | |
708 | if (langsys_count++ > HB_MAX_LANGSYS) |
709 | return true; |
710 | |
711 | return visited (l, visited_langsys); |
712 | } |
713 | |
714 | private: |
715 | template <typename T> |
716 | bool visited (const T &p, hb_set_t &visited_set) |
717 | { |
718 | hb_codepoint_t delta = (hb_codepoint_t) ((uintptr_t) &p - (uintptr_t) &g); |
719 | if (visited_set.has (delta)) |
720 | return true; |
721 | |
722 | visited_set.add (delta); |
723 | return false; |
724 | } |
725 | |
726 | public: |
727 | const OT::GSUBGPOS &g; |
728 | hb_set_t *feature_indexes; |
729 | |
730 | private: |
731 | hb_set_t visited_script; |
732 | hb_set_t visited_langsys; |
733 | unsigned int script_count; |
734 | unsigned int langsys_count; |
735 | }; |
736 | |
737 | static void |
738 | langsys_collect_features (hb_collect_features_context_t *c, |
739 | const OT::LangSys &l, |
740 | const hb_tag_t *features) |
741 | { |
742 | if (c->visited (l)) return; |
743 | |
744 | if (!features) |
745 | { |
746 | /* All features. */ |
747 | if (l.has_required_feature ()) |
748 | c->feature_indexes->add (l.get_required_feature_index ()); |
749 | |
750 | l.add_feature_indexes_to (c->feature_indexes); |
751 | } |
752 | else |
753 | { |
754 | /* Ugh. Any faster way? */ |
755 | for (; *features; features++) |
756 | { |
757 | hb_tag_t feature_tag = *features; |
758 | unsigned int num_features = l.get_feature_count (); |
759 | for (unsigned int i = 0; i < num_features; i++) |
760 | { |
761 | unsigned int feature_index = l.get_feature_index (i); |
762 | |
763 | if (feature_tag == c->g.get_feature_tag (feature_index)) |
764 | { |
765 | c->feature_indexes->add (feature_index); |
766 | break; |
767 | } |
768 | } |
769 | } |
770 | } |
771 | } |
772 | |
773 | static void |
774 | script_collect_features (hb_collect_features_context_t *c, |
775 | const OT::Script &s, |
776 | const hb_tag_t *languages, |
777 | const hb_tag_t *features) |
778 | { |
779 | if (c->visited (s)) return; |
780 | |
781 | if (!languages) |
782 | { |
783 | /* All languages. */ |
784 | if (s.has_default_lang_sys ()) |
785 | langsys_collect_features (c, |
786 | s.get_default_lang_sys (), |
787 | features); |
788 | |
789 | unsigned int count = s.get_lang_sys_count (); |
790 | for (unsigned int language_index = 0; language_index < count; language_index++) |
791 | langsys_collect_features (c, |
792 | s.get_lang_sys (language_index), |
793 | features); |
794 | } |
795 | else |
796 | { |
797 | for (; *languages; languages++) |
798 | { |
799 | unsigned int language_index; |
800 | if (s.find_lang_sys_index (*languages, &language_index)) |
801 | langsys_collect_features (c, |
802 | s.get_lang_sys (language_index), |
803 | features); |
804 | } |
805 | } |
806 | } |
807 | |
808 | /** |
809 | * hb_ot_layout_collect_features: |
810 | * |
811 | * Since: 1.8.5 |
812 | **/ |
813 | void |
814 | hb_ot_layout_collect_features (hb_face_t *face, |
815 | hb_tag_t table_tag, |
816 | const hb_tag_t *scripts, |
817 | const hb_tag_t *languages, |
818 | const hb_tag_t *features, |
819 | hb_set_t *feature_indexes /* OUT */) |
820 | { |
821 | hb_collect_features_context_t c (face, table_tag, feature_indexes); |
822 | if (!scripts) |
823 | { |
824 | /* All scripts. */ |
825 | unsigned int count = c.g.get_script_count (); |
826 | for (unsigned int script_index = 0; script_index < count; script_index++) |
827 | script_collect_features (&c, |
828 | c.g.get_script (script_index), |
829 | languages, |
830 | features); |
831 | } |
832 | else |
833 | { |
834 | for (; *scripts; scripts++) |
835 | { |
836 | unsigned int script_index; |
837 | if (c.g.find_script_index (*scripts, &script_index)) |
838 | script_collect_features (&c, |
839 | c.g.get_script (script_index), |
840 | languages, |
841 | features); |
842 | } |
843 | } |
844 | } |
845 | |
846 | /** |
847 | * hb_ot_layout_collect_lookups: |
848 | * |
849 | * Since: 0.9.8 |
850 | **/ |
851 | void |
852 | hb_ot_layout_collect_lookups (hb_face_t *face, |
853 | hb_tag_t table_tag, |
854 | const hb_tag_t *scripts, |
855 | const hb_tag_t *languages, |
856 | const hb_tag_t *features, |
857 | hb_set_t *lookup_indexes /* OUT */) |
858 | { |
859 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
860 | |
861 | hb_set_t feature_indexes; |
862 | hb_ot_layout_collect_features (face, table_tag, scripts, languages, features, &feature_indexes); |
863 | |
864 | for (hb_codepoint_t feature_index = HB_SET_VALUE_INVALID; |
865 | hb_set_next (&feature_indexes, &feature_index);) |
866 | g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes); |
867 | } |
868 | |
869 | /** |
870 | * hb_ot_layout_lookup_collect_glyphs: |
871 | * |
872 | * Since: 0.9.7 |
873 | **/ |
874 | void |
875 | hb_ot_layout_lookup_collect_glyphs (hb_face_t *face, |
876 | hb_tag_t table_tag, |
877 | unsigned int lookup_index, |
878 | hb_set_t *glyphs_before, /* OUT. May be NULL */ |
879 | hb_set_t *glyphs_input, /* OUT. May be NULL */ |
880 | hb_set_t *glyphs_after, /* OUT. May be NULL */ |
881 | hb_set_t *glyphs_output /* OUT. May be NULL */) |
882 | { |
883 | OT::hb_collect_glyphs_context_t c (face, |
884 | glyphs_before, |
885 | glyphs_input, |
886 | glyphs_after, |
887 | glyphs_output); |
888 | |
889 | switch (table_tag) |
890 | { |
891 | case HB_OT_TAG_GSUB: |
892 | { |
893 | const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); |
894 | l.collect_glyphs (&c); |
895 | return; |
896 | } |
897 | case HB_OT_TAG_GPOS: |
898 | { |
899 | const OT::PosLookup& l = face->table.GPOS->table->get_lookup (lookup_index); |
900 | l.collect_glyphs (&c); |
901 | return; |
902 | } |
903 | } |
904 | } |
905 | |
906 | |
907 | /* Variations support */ |
908 | |
909 | hb_bool_t |
910 | hb_ot_layout_table_find_feature_variations (hb_face_t *face, |
911 | hb_tag_t table_tag, |
912 | const int *coords, |
913 | unsigned int num_coords, |
914 | unsigned int *variations_index /* out */) |
915 | { |
916 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
917 | |
918 | return g.find_variations_index (coords, num_coords, variations_index); |
919 | } |
920 | |
921 | unsigned int |
922 | hb_ot_layout_feature_with_variations_get_lookups (hb_face_t *face, |
923 | hb_tag_t table_tag, |
924 | unsigned int feature_index, |
925 | unsigned int variations_index, |
926 | unsigned int start_offset, |
927 | unsigned int *lookup_count /* IN/OUT */, |
928 | unsigned int *lookup_indexes /* OUT */) |
929 | { |
930 | static_assert ((OT::FeatureVariations::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_VARIATIONS_INDEX), "" ); |
931 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
932 | |
933 | const OT::Feature &f = g.get_feature_variation (feature_index, variations_index); |
934 | |
935 | return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes); |
936 | } |
937 | |
938 | |
939 | /* |
940 | * OT::GSUB |
941 | */ |
942 | |
943 | hb_bool_t |
944 | hb_ot_layout_has_substitution (hb_face_t *face) |
945 | { |
946 | return face->table.GSUB->table->has_data (); |
947 | } |
948 | |
949 | /** |
950 | * hb_ot_layout_lookup_would_substitute: |
951 | * |
952 | * Since: 0.9.7 |
953 | **/ |
954 | hb_bool_t |
955 | hb_ot_layout_lookup_would_substitute (hb_face_t *face, |
956 | unsigned int lookup_index, |
957 | const hb_codepoint_t *glyphs, |
958 | unsigned int glyphs_length, |
959 | hb_bool_t zero_context) |
960 | { |
961 | return hb_ot_layout_lookup_would_substitute_fast (face, |
962 | lookup_index, |
963 | glyphs, glyphs_length, |
964 | zero_context); |
965 | } |
966 | |
967 | bool |
968 | hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, |
969 | unsigned int lookup_index, |
970 | const hb_codepoint_t *glyphs, |
971 | unsigned int glyphs_length, |
972 | bool zero_context) |
973 | { |
974 | if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false; |
975 | OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); |
976 | |
977 | const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); |
978 | |
979 | return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]); |
980 | } |
981 | |
982 | void |
983 | hb_ot_layout_substitute_start (hb_font_t *font, |
984 | hb_buffer_t *buffer) |
985 | { |
986 | _hb_ot_layout_set_glyph_props (font, buffer); |
987 | } |
988 | |
989 | void |
990 | hb_ot_layout_delete_glyphs_inplace (hb_buffer_t *buffer, |
991 | bool (*filter) (const hb_glyph_info_t *info)) |
992 | { |
993 | /* Merge clusters and delete filtered glyphs. |
994 | * NOTE! We can't use out-buffer as we have positioning data. */ |
995 | unsigned int j = 0; |
996 | unsigned int count = buffer->len; |
997 | hb_glyph_info_t *info = buffer->info; |
998 | hb_glyph_position_t *pos = buffer->pos; |
999 | for (unsigned int i = 0; i < count; i++) |
1000 | { |
1001 | if (filter (&info[i])) |
1002 | { |
1003 | /* Merge clusters. |
1004 | * Same logic as buffer->delete_glyph(), but for in-place removal. */ |
1005 | |
1006 | unsigned int cluster = info[i].cluster; |
1007 | if (i + 1 < count && cluster == info[i + 1].cluster) |
1008 | continue; /* Cluster survives; do nothing. */ |
1009 | |
1010 | if (j) |
1011 | { |
1012 | /* Merge cluster backward. */ |
1013 | if (cluster < info[j - 1].cluster) |
1014 | { |
1015 | unsigned int mask = info[i].mask; |
1016 | unsigned int old_cluster = info[j - 1].cluster; |
1017 | for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) |
1018 | buffer->set_cluster (info[k - 1], cluster, mask); |
1019 | } |
1020 | continue; |
1021 | } |
1022 | |
1023 | if (i + 1 < count) |
1024 | buffer->merge_clusters (i, i + 2); /* Merge cluster forward. */ |
1025 | |
1026 | continue; |
1027 | } |
1028 | |
1029 | if (j != i) |
1030 | { |
1031 | info[j] = info[i]; |
1032 | pos[j] = pos[i]; |
1033 | } |
1034 | j++; |
1035 | } |
1036 | buffer->len = j; |
1037 | } |
1038 | |
1039 | /** |
1040 | * hb_ot_layout_lookup_substitute_closure: |
1041 | * |
1042 | * Since: 0.9.7 |
1043 | **/ |
1044 | void |
1045 | hb_ot_layout_lookup_substitute_closure (hb_face_t *face, |
1046 | unsigned int lookup_index, |
1047 | hb_set_t *glyphs) |
1048 | { |
1049 | hb_map_t done_lookups; |
1050 | OT::hb_closure_context_t c (face, glyphs, &done_lookups); |
1051 | |
1052 | const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); |
1053 | |
1054 | l.closure (&c, lookup_index); |
1055 | } |
1056 | |
1057 | /** |
1058 | * hb_ot_layout_lookups_substitute_closure: |
1059 | * |
1060 | * Compute the transitive closure of glyphs needed for all of the |
1061 | * provided lookups. |
1062 | * |
1063 | * Since: 1.8.1 |
1064 | **/ |
1065 | void |
1066 | hb_ot_layout_lookups_substitute_closure (hb_face_t *face, |
1067 | const hb_set_t *lookups, |
1068 | hb_set_t *glyphs) |
1069 | { |
1070 | hb_map_t done_lookups; |
1071 | OT::hb_closure_context_t c (face, glyphs, &done_lookups); |
1072 | const OT::GSUB& gsub = *face->table.GSUB->table; |
1073 | |
1074 | unsigned int iteration_count = 0; |
1075 | unsigned int glyphs_length; |
1076 | do |
1077 | { |
1078 | glyphs_length = glyphs->get_population (); |
1079 | if (lookups != nullptr) |
1080 | { |
1081 | for (hb_codepoint_t lookup_index = HB_SET_VALUE_INVALID; hb_set_next (lookups, &lookup_index);) |
1082 | gsub.get_lookup (lookup_index).closure (&c, lookup_index); |
1083 | } |
1084 | else |
1085 | { |
1086 | for (unsigned int i = 0; i < gsub.get_lookup_count (); i++) |
1087 | gsub.get_lookup (i).closure (&c, i); |
1088 | } |
1089 | } while (iteration_count++ <= HB_CLOSURE_MAX_STAGES && |
1090 | glyphs_length != glyphs->get_population ()); |
1091 | } |
1092 | |
1093 | /* |
1094 | * OT::GPOS |
1095 | */ |
1096 | |
1097 | hb_bool_t |
1098 | hb_ot_layout_has_positioning (hb_face_t *face) |
1099 | { |
1100 | return face->table.GPOS->table->has_data (); |
1101 | } |
1102 | |
1103 | void |
1104 | hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer) |
1105 | { |
1106 | OT::GPOS::position_start (font, buffer); |
1107 | } |
1108 | |
1109 | void |
1110 | hb_ot_layout_position_finish_advances (hb_font_t *font, hb_buffer_t *buffer) |
1111 | { |
1112 | OT::GPOS::position_finish_advances (font, buffer); |
1113 | } |
1114 | |
1115 | void |
1116 | hb_ot_layout_position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer) |
1117 | { |
1118 | OT::GPOS::position_finish_offsets (font, buffer); |
1119 | } |
1120 | |
1121 | /** |
1122 | * hb_ot_layout_get_size_params: |
1123 | * |
1124 | * Since: 0.9.10 |
1125 | **/ |
1126 | hb_bool_t |
1127 | hb_ot_layout_get_size_params (hb_face_t *face, |
1128 | unsigned int *design_size, /* OUT. May be NULL */ |
1129 | unsigned int *subfamily_id, /* OUT. May be NULL */ |
1130 | hb_ot_name_id_t *subfamily_name_id, /* OUT. May be NULL */ |
1131 | unsigned int *range_start, /* OUT. May be NULL */ |
1132 | unsigned int *range_end /* OUT. May be NULL */) |
1133 | { |
1134 | const OT::GPOS &gpos = *face->table.GPOS->table; |
1135 | const hb_tag_t tag = HB_TAG ('s','i','z','e'); |
1136 | |
1137 | unsigned int num_features = gpos.get_feature_count (); |
1138 | for (unsigned int i = 0; i < num_features; i++) |
1139 | { |
1140 | if (tag == gpos.get_feature_tag (i)) |
1141 | { |
1142 | const OT::Feature &f = gpos.get_feature (i); |
1143 | const OT::FeatureParamsSize ¶ms = f.get_feature_params ().get_size_params (tag); |
1144 | |
1145 | if (params.designSize) |
1146 | { |
1147 | if (design_size) *design_size = params.designSize; |
1148 | if (subfamily_id) *subfamily_id = params.subfamilyID; |
1149 | if (subfamily_name_id) *subfamily_name_id = params.subfamilyNameID; |
1150 | if (range_start) *range_start = params.rangeStart; |
1151 | if (range_end) *range_end = params.rangeEnd; |
1152 | |
1153 | return true; |
1154 | } |
1155 | } |
1156 | } |
1157 | |
1158 | if (design_size) *design_size = 0; |
1159 | if (subfamily_id) *subfamily_id = 0; |
1160 | if (subfamily_name_id) *subfamily_name_id = HB_OT_NAME_ID_INVALID; |
1161 | if (range_start) *range_start = 0; |
1162 | if (range_end) *range_end = 0; |
1163 | |
1164 | return false; |
1165 | } |
1166 | |
1167 | /** |
1168 | * hb_ot_layout_feature_get_name_ids: |
1169 | * @face: #hb_face_t to work upon |
1170 | * @table_tag: table tag to query, "GSUB" or "GPOS". |
1171 | * @feature_index: index of feature to query. |
1172 | * @label_id: (out) (allow-none): The ‘name’ table name ID that specifies a string |
1173 | * for a user-interface label for this feature. (May be NULL.) |
1174 | * @tooltip_id: (out) (allow-none): The ‘name’ table name ID that specifies a string |
1175 | * that an application can use for tooltip text for this |
1176 | * feature. (May be NULL.) |
1177 | * @sample_id: (out) (allow-none): The ‘name’ table name ID that specifies sample text |
1178 | * that illustrates the effect of this feature. (May be NULL.) |
1179 | * @num_named_parameters: (out) (allow-none): Number of named parameters. (May be zero.) |
1180 | * @first_param_id: (out) (allow-none): The first ‘name’ table name ID used to specify |
1181 | * strings for user-interface labels for the feature |
1182 | * parameters. (Must be zero if numParameters is zero.) |
1183 | * |
1184 | * Fetches name indices from feature parameters for "Stylistic Set" ('ssXX') or |
1185 | * "Character Variant" ('cvXX') features. |
1186 | * |
1187 | * Return value: true if data found, false otherwise |
1188 | * |
1189 | * Since: 2.0.0 |
1190 | **/ |
1191 | hb_bool_t |
1192 | hb_ot_layout_feature_get_name_ids (hb_face_t *face, |
1193 | hb_tag_t table_tag, |
1194 | unsigned int feature_index, |
1195 | hb_ot_name_id_t *label_id, /* OUT. May be NULL */ |
1196 | hb_ot_name_id_t *tooltip_id, /* OUT. May be NULL */ |
1197 | hb_ot_name_id_t *sample_id, /* OUT. May be NULL */ |
1198 | unsigned int *num_named_parameters, /* OUT. May be NULL */ |
1199 | hb_ot_name_id_t *first_param_id /* OUT. May be NULL */) |
1200 | { |
1201 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
1202 | |
1203 | hb_tag_t feature_tag = g.get_feature_tag (feature_index); |
1204 | const OT::Feature &f = g.get_feature (feature_index); |
1205 | |
1206 | const OT::FeatureParams &feature_params = f.get_feature_params (); |
1207 | if (&feature_params != &Null (OT::FeatureParams)) |
1208 | { |
1209 | const OT::FeatureParamsStylisticSet& ss_params = |
1210 | feature_params.get_stylistic_set_params (feature_tag); |
1211 | if (&ss_params != &Null (OT::FeatureParamsStylisticSet)) /* ssXX */ |
1212 | { |
1213 | if (label_id) *label_id = ss_params.uiNameID; |
1214 | // ssXX features don't have the rest |
1215 | if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID; |
1216 | if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID; |
1217 | if (num_named_parameters) *num_named_parameters = 0; |
1218 | if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; |
1219 | return true; |
1220 | } |
1221 | const OT::FeatureParamsCharacterVariants& cv_params = |
1222 | feature_params.get_character_variants_params (feature_tag); |
1223 | if (&cv_params != &Null (OT::FeatureParamsCharacterVariants)) /* cvXX */ |
1224 | { |
1225 | if (label_id) *label_id = cv_params.featUILableNameID; |
1226 | if (tooltip_id) *tooltip_id = cv_params.featUITooltipTextNameID; |
1227 | if (sample_id) *sample_id = cv_params.sampleTextNameID; |
1228 | if (num_named_parameters) *num_named_parameters = cv_params.numNamedParameters; |
1229 | if (first_param_id) *first_param_id = cv_params.firstParamUILabelNameID; |
1230 | return true; |
1231 | } |
1232 | } |
1233 | |
1234 | if (label_id) *label_id = HB_OT_NAME_ID_INVALID; |
1235 | if (tooltip_id) *tooltip_id = HB_OT_NAME_ID_INVALID; |
1236 | if (sample_id) *sample_id = HB_OT_NAME_ID_INVALID; |
1237 | if (num_named_parameters) *num_named_parameters = 0; |
1238 | if (first_param_id) *first_param_id = HB_OT_NAME_ID_INVALID; |
1239 | return false; |
1240 | } |
1241 | |
1242 | /** |
1243 | * hb_ot_layout_feature_get_characters: |
1244 | * @face: #hb_face_t to work upon |
1245 | * @table_tag: table tag to query, "GSUB" or "GPOS". |
1246 | * @feature_index: index of feature to query. |
1247 | * @start_offset: In case the resulting char_count was equal to its input value, there |
1248 | * is a chance there were more characters on the tag so this API can be |
1249 | * called with an offset till resulting char_count gets to a number |
1250 | * lower than input buffer (or consider using just a bigger buffer for |
1251 | * one shot copying). |
1252 | * @char_count: (inout) (allow-none): The count of characters for which this feature |
1253 | * provides glyph variants. (May be zero.) |
1254 | * @characters: (out caller-allocates) (array length=char_count): A buffer pointer. The Unicode codepoints |
1255 | * of the characters for which this feature provides glyph variants. |
1256 | * |
1257 | * Fetches characters listed by designer under feature parameters for "Character |
1258 | * Variant" ("cvXX") features. |
1259 | * |
1260 | * Return value: Number of total sample characters in the cvXX feature. |
1261 | * |
1262 | * Since: 2.0.0 |
1263 | **/ |
1264 | unsigned int |
1265 | hb_ot_layout_feature_get_characters (hb_face_t *face, |
1266 | hb_tag_t table_tag, |
1267 | unsigned int feature_index, |
1268 | unsigned int start_offset, |
1269 | unsigned int *char_count, /* IN/OUT. May be NULL */ |
1270 | hb_codepoint_t *characters /* OUT. May be NULL */) |
1271 | { |
1272 | const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag); |
1273 | |
1274 | hb_tag_t feature_tag = g.get_feature_tag (feature_index); |
1275 | const OT::Feature &f = g.get_feature (feature_index); |
1276 | |
1277 | const OT::FeatureParams &feature_params = f.get_feature_params (); |
1278 | |
1279 | const OT::FeatureParamsCharacterVariants& cv_params = |
1280 | feature_params.get_character_variants_params(feature_tag); |
1281 | |
1282 | unsigned int len = 0; |
1283 | if (char_count && characters && start_offset < cv_params.characters.len) |
1284 | { |
1285 | len = MIN (cv_params.characters.len - start_offset, *char_count); |
1286 | for (unsigned int i = 0; i < len; ++i) |
1287 | characters[i] = cv_params.characters[start_offset + i]; |
1288 | } |
1289 | if (char_count) *char_count = len; |
1290 | return cv_params.characters.len; |
1291 | } |
1292 | |
1293 | |
1294 | /* |
1295 | * Parts of different types are implemented here such that they have direct |
1296 | * access to GSUB/GPOS lookups. |
1297 | */ |
1298 | |
1299 | |
1300 | struct GSUBProxy |
1301 | { |
1302 | static constexpr unsigned table_index = 0u; |
1303 | static constexpr bool inplace = false; |
1304 | typedef OT::SubstLookup Lookup; |
1305 | |
1306 | GSUBProxy (hb_face_t *face) : |
1307 | table (*face->table.GSUB->table), |
1308 | accels (face->table.GSUB->accels) {} |
1309 | |
1310 | const OT::GSUB &table; |
1311 | const OT::hb_ot_layout_lookup_accelerator_t *accels; |
1312 | }; |
1313 | |
1314 | struct GPOSProxy |
1315 | { |
1316 | static constexpr unsigned table_index = 1u; |
1317 | static constexpr bool inplace = true; |
1318 | typedef OT::PosLookup Lookup; |
1319 | |
1320 | GPOSProxy (hb_face_t *face) : |
1321 | table (*face->table.GPOS->table), |
1322 | accels (face->table.GPOS->accels) {} |
1323 | |
1324 | const OT::GPOS &table; |
1325 | const OT::hb_ot_layout_lookup_accelerator_t *accels; |
1326 | }; |
1327 | |
1328 | |
1329 | static inline bool |
1330 | apply_forward (OT::hb_ot_apply_context_t *c, |
1331 | const OT::hb_ot_layout_lookup_accelerator_t &accel) |
1332 | { |
1333 | bool ret = false; |
1334 | hb_buffer_t *buffer = c->buffer; |
1335 | while (buffer->idx < buffer->len && buffer->successful) |
1336 | { |
1337 | bool applied = false; |
1338 | if (accel.may_have (buffer->cur().codepoint) && |
1339 | (buffer->cur().mask & c->lookup_mask) && |
1340 | c->check_glyph_property (&buffer->cur(), c->lookup_props)) |
1341 | { |
1342 | applied = accel.apply (c); |
1343 | } |
1344 | |
1345 | if (applied) |
1346 | ret = true; |
1347 | else |
1348 | buffer->next_glyph (); |
1349 | } |
1350 | return ret; |
1351 | } |
1352 | |
1353 | static inline bool |
1354 | apply_backward (OT::hb_ot_apply_context_t *c, |
1355 | const OT::hb_ot_layout_lookup_accelerator_t &accel) |
1356 | { |
1357 | bool ret = false; |
1358 | hb_buffer_t *buffer = c->buffer; |
1359 | do |
1360 | { |
1361 | if (accel.may_have (buffer->cur().codepoint) && |
1362 | (buffer->cur().mask & c->lookup_mask) && |
1363 | c->check_glyph_property (&buffer->cur(), c->lookup_props)) |
1364 | ret |= accel.apply (c); |
1365 | |
1366 | /* The reverse lookup doesn't "advance" cursor (for good reason). */ |
1367 | buffer->idx--; |
1368 | |
1369 | } |
1370 | while ((int) buffer->idx >= 0); |
1371 | return ret; |
1372 | } |
1373 | |
1374 | template <typename Proxy> |
1375 | static inline void |
1376 | apply_string (OT::hb_ot_apply_context_t *c, |
1377 | const typename Proxy::Lookup &lookup, |
1378 | const OT::hb_ot_layout_lookup_accelerator_t &accel) |
1379 | { |
1380 | hb_buffer_t *buffer = c->buffer; |
1381 | |
1382 | if (unlikely (!buffer->len || !c->lookup_mask)) |
1383 | return; |
1384 | |
1385 | c->set_lookup_props (lookup.get_props ()); |
1386 | |
1387 | if (likely (!lookup.is_reverse ())) |
1388 | { |
1389 | /* in/out forward substitution/positioning */ |
1390 | if (Proxy::table_index == 0u) |
1391 | buffer->clear_output (); |
1392 | buffer->idx = 0; |
1393 | |
1394 | bool ret; |
1395 | ret = apply_forward (c, accel); |
1396 | if (ret) |
1397 | { |
1398 | if (!Proxy::inplace) |
1399 | buffer->swap_buffers (); |
1400 | else |
1401 | assert (!buffer->has_separate_output ()); |
1402 | } |
1403 | } |
1404 | else |
1405 | { |
1406 | /* in-place backward substitution/positioning */ |
1407 | if (Proxy::table_index == 0u) |
1408 | buffer->remove_output (); |
1409 | buffer->idx = buffer->len - 1; |
1410 | |
1411 | apply_backward (c, accel); |
1412 | } |
1413 | } |
1414 | |
1415 | template <typename Proxy> |
1416 | inline void hb_ot_map_t::apply (const Proxy &proxy, |
1417 | const hb_ot_shape_plan_t *plan, |
1418 | hb_font_t *font, |
1419 | hb_buffer_t *buffer) const |
1420 | { |
1421 | const unsigned int table_index = proxy.table_index; |
1422 | unsigned int i = 0; |
1423 | OT::hb_ot_apply_context_t c (table_index, font, buffer); |
1424 | c.set_recurse_func (Proxy::Lookup::apply_recurse_func); |
1425 | |
1426 | for (unsigned int stage_index = 0; stage_index < stages[table_index].length; stage_index++) { |
1427 | const stage_map_t *stage = &stages[table_index][stage_index]; |
1428 | for (; i < stage->last_lookup; i++) |
1429 | { |
1430 | unsigned int lookup_index = lookups[table_index][i].index; |
1431 | if (!buffer->message (font, "start lookup %d" , lookup_index)) continue; |
1432 | c.set_lookup_index (lookup_index); |
1433 | c.set_lookup_mask (lookups[table_index][i].mask); |
1434 | c.set_auto_zwj (lookups[table_index][i].auto_zwj); |
1435 | c.set_auto_zwnj (lookups[table_index][i].auto_zwnj); |
1436 | if (lookups[table_index][i].random) |
1437 | { |
1438 | c.set_random (true); |
1439 | buffer->unsafe_to_break_all (); |
1440 | } |
1441 | apply_string<Proxy> (&c, |
1442 | proxy.table.get_lookup (lookup_index), |
1443 | proxy.accels[lookup_index]); |
1444 | (void) buffer->message (font, "end lookup %d" , lookup_index); |
1445 | } |
1446 | |
1447 | if (stage->pause_func) |
1448 | { |
1449 | buffer->clear_output (); |
1450 | stage->pause_func (plan, font, buffer); |
1451 | } |
1452 | } |
1453 | } |
1454 | |
1455 | void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const |
1456 | { |
1457 | GSUBProxy proxy (font->face); |
1458 | apply (proxy, plan, font, buffer); |
1459 | } |
1460 | |
1461 | void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const |
1462 | { |
1463 | GPOSProxy proxy (font->face); |
1464 | apply (proxy, plan, font, buffer); |
1465 | } |
1466 | |
1467 | void |
1468 | hb_ot_layout_substitute_lookup (OT::hb_ot_apply_context_t *c, |
1469 | const OT::SubstLookup &lookup, |
1470 | const OT::hb_ot_layout_lookup_accelerator_t &accel) |
1471 | { |
1472 | apply_string<GSUBProxy> (c, lookup, accel); |
1473 | } |
1474 | |
1475 | #if 0 |
1476 | static const OT::BASE& _get_base (hb_face_t *face) |
1477 | { |
1478 | return *face->table.BASE; |
1479 | } |
1480 | |
1481 | hb_bool_t |
1482 | hb_ot_layout_get_baseline (hb_font_t *font, |
1483 | hb_ot_layout_baseline_t baseline, |
1484 | hb_direction_t direction, |
1485 | hb_tag_t script_tag, |
1486 | hb_tag_t language_tag, |
1487 | hb_position_t *coord /* OUT. May be NULL. */) |
1488 | { |
1489 | const OT::BASE &base = _get_base (font->face); |
1490 | bool result = base.get_baseline (font, baseline, direction, script_tag, |
1491 | language_tag, coord); |
1492 | |
1493 | /* TODO: Simulate https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags#ideographic-em-box */ |
1494 | if (!result && coord) *coord = 0; |
1495 | |
1496 | if (coord) *coord = font->em_scale_dir (*coord, direction); |
1497 | |
1498 | return result; |
1499 | } |
1500 | |
1501 | /* To be moved to public header */ |
1502 | /* |
1503 | * BASE |
1504 | */ |
1505 | |
1506 | /** |
1507 | * hb_ot_layout_baseline_t: |
1508 | * |
1509 | * https://docs.microsoft.com/en-us/typography/opentype/spec/baselinetags |
1510 | * |
1511 | * Since: DONTREPLACEME |
1512 | */ |
1513 | typedef enum { |
1514 | HB_OT_LAYOUT_BASELINE_HANG = HB_TAG('h','a','n','g'), |
1515 | HB_OT_LAYOUT_BASELINE_ICFB = HB_TAG('i','c','f','b'), |
1516 | HB_OT_LAYOUT_BASELINE_ICFT = HB_TAG('i','c','f','t'), |
1517 | HB_OT_LAYOUT_BASELINE_IDEO = HB_TAG('i','d','e','o'), |
1518 | HB_OT_LAYOUT_BASELINE_IDTB = HB_TAG('i','d','t','b'), |
1519 | HB_OT_LAYOUT_BASELINE_MATH = HB_TAG('m','a','t','h'), |
1520 | HB_OT_LAYOUT_BASELINE_ROMN = HB_TAG('r','o','m','n') |
1521 | } hb_ot_layout_baseline_t; |
1522 | |
1523 | HB_EXTERN hb_bool_t |
1524 | hb_ot_layout_get_baseline (hb_font_t *font, |
1525 | hb_ot_layout_baseline_t baseline, |
1526 | hb_direction_t direction, |
1527 | hb_tag_t script_tag, |
1528 | hb_tag_t language_tag, |
1529 | hb_position_t *coord /* OUT. May be NULL. */); |
1530 | |
1531 | #endif |
1532 | |