1 | /* |
2 | * Copyright © 2009,2010 Red Hat, Inc. |
3 | * Copyright © 2010,2011,2012 Google, Inc. |
4 | * |
5 | * This is part of HarfBuzz, a text shaping library. |
6 | * |
7 | * Permission is hereby granted, without written agreement and without |
8 | * license or royalty fees, to use, copy, modify, and distribute this |
9 | * software and its documentation for any purpose, provided that the |
10 | * above copyright notice and the following two paragraphs appear in |
11 | * all copies of this software. |
12 | * |
13 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
14 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
15 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
16 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
17 | * DAMAGE. |
18 | * |
19 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
20 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
21 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
22 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
23 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
24 | * |
25 | * Red Hat Author(s): Behdad Esfahbod |
26 | * Google Author(s): Behdad Esfahbod |
27 | */ |
28 | |
29 | #include "hb.hh" |
30 | |
31 | #ifndef HB_NO_OT_SHAPE |
32 | |
33 | #ifdef HB_NO_OT_LAYOUT |
34 | #error "Cannot compile 'ot' shaper with HB_NO_OT_LAYOUT." |
35 | #endif |
36 | |
37 | #include "hb-shaper-impl.hh" |
38 | |
39 | #include "hb-ot-shape.hh" |
40 | #include "hb-ot-shape-complex.hh" |
41 | #include "hb-ot-shape-fallback.hh" |
42 | #include "hb-ot-shape-normalize.hh" |
43 | |
44 | #include "hb-ot-face.hh" |
45 | |
46 | #include "hb-set.hh" |
47 | |
48 | #include "hb-aat-layout.hh" |
49 | |
50 | |
51 | /** |
52 | * SECTION:hb-ot-shape |
53 | * @title: hb-ot-shape |
54 | * @short_description: OpenType shaping support |
55 | * @include: hb-ot.h |
56 | * |
57 | * Support functions for OpenType shaping related queries. |
58 | **/ |
59 | |
60 | |
61 | static void |
62 | hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, |
63 | const hb_feature_t *user_features, |
64 | unsigned int num_user_features); |
65 | |
66 | #ifndef HB_NO_AAT_SHAPE |
67 | static inline bool |
68 | _hb_apply_morx (hb_face_t *face) |
69 | { |
70 | if (hb_options ().aat && |
71 | hb_aat_layout_has_substitution (face)) |
72 | return true; |
73 | |
74 | /* Ignore empty GSUB tables. */ |
75 | return (!hb_ot_layout_has_substitution (face) || |
76 | !hb_ot_layout_table_get_script_tags (face, |
77 | HB_OT_TAG_GSUB, |
78 | 0, nullptr, nullptr)) && |
79 | hb_aat_layout_has_substitution (face); |
80 | } |
81 | #endif |
82 | |
83 | hb_ot_shape_planner_t::hb_ot_shape_planner_t (hb_face_t *face, |
84 | const hb_segment_properties_t *props) : |
85 | face (face), |
86 | props (*props), |
87 | map (face, props), |
88 | aat_map (face, props) |
89 | #ifndef HB_NO_AAT_SHAPE |
90 | , apply_morx (_hb_apply_morx (face)) |
91 | #endif |
92 | { |
93 | shaper = hb_ot_shape_complex_categorize (this); |
94 | |
95 | script_zero_marks = shaper->zero_width_marks != HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE; |
96 | script_fallback_mark_positioning = shaper->fallback_position; |
97 | |
98 | if (apply_morx) |
99 | shaper = &_hb_ot_complex_shaper_default; |
100 | } |
101 | |
102 | void |
103 | hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan, |
104 | const hb_ot_shape_plan_key_t &key) |
105 | { |
106 | plan.props = props; |
107 | plan.shaper = shaper; |
108 | map.compile (plan.map, key); |
109 | #ifndef HB_NO_AAT_SHAPE |
110 | if (apply_morx) |
111 | aat_map.compile (plan.aat_map); |
112 | #endif |
113 | |
114 | #ifndef HB_NO_OT_SHAPE_FRACTIONS |
115 | plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c')); |
116 | plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r')); |
117 | plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m')); |
118 | plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask); |
119 | #endif |
120 | |
121 | plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m')); |
122 | hb_tag_t kern_tag = HB_DIRECTION_IS_HORIZONTAL (props.direction) ? |
123 | HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'); |
124 | #ifndef HB_NO_OT_KERN |
125 | plan.kern_mask = plan.map.get_mask (kern_tag); |
126 | plan.requested_kerning = !!plan.kern_mask; |
127 | #endif |
128 | #ifndef HB_NO_AAT_SHAPE |
129 | plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k')); |
130 | plan.requested_tracking = !!plan.trak_mask; |
131 | #endif |
132 | |
133 | bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; |
134 | bool disable_gpos = plan.shaper->gpos_tag && |
135 | plan.shaper->gpos_tag != plan.map.chosen_script[1]; |
136 | |
137 | /* |
138 | * Decide who provides glyph classes. GDEF or Unicode. |
139 | */ |
140 | |
141 | if (!hb_ot_layout_has_glyph_classes (face)) |
142 | plan.fallback_glyph_classes = true; |
143 | |
144 | /* |
145 | * Decide who does substitutions. GSUB, morx, or fallback. |
146 | */ |
147 | |
148 | #ifndef HB_NO_AAT_SHAPE |
149 | plan.apply_morx = apply_morx; |
150 | #endif |
151 | |
152 | /* |
153 | * Decide who does positioning. GPOS, kerx, kern, or fallback. |
154 | */ |
155 | |
156 | if (0) |
157 | ; |
158 | #ifndef HB_NO_AAT_SHAPE |
159 | else if (hb_options ().aat && hb_aat_layout_has_positioning (face)) |
160 | plan.apply_kerx = true; |
161 | #endif |
162 | else if (!apply_morx && !disable_gpos && hb_ot_layout_has_positioning (face)) |
163 | plan.apply_gpos = true; |
164 | #ifndef HB_NO_AAT_SHAPE |
165 | else if (hb_aat_layout_has_positioning (face)) |
166 | plan.apply_kerx = true; |
167 | #endif |
168 | |
169 | if (!plan.apply_kerx && !has_gpos_kern) |
170 | { |
171 | /* Apparently Apple applies kerx if GPOS kern was not applied. */ |
172 | #ifndef HB_NO_AAT_SHAPE |
173 | if (hb_aat_layout_has_positioning (face)) |
174 | plan.apply_kerx = true; |
175 | else |
176 | #endif |
177 | #ifndef HB_NO_OT_KERN |
178 | if (hb_ot_layout_has_kerning (face)) |
179 | plan.apply_kern = true; |
180 | #endif |
181 | } |
182 | |
183 | plan.zero_marks = script_zero_marks && |
184 | !plan.apply_kerx && |
185 | (!plan.apply_kern |
186 | #ifndef HB_NO_OT_KERN |
187 | || !hb_ot_layout_has_machine_kerning (face) |
188 | #endif |
189 | ); |
190 | plan.has_gpos_mark = !!plan.map.get_1_mask (HB_TAG ('m','a','r','k')); |
191 | |
192 | plan.adjust_mark_positioning_when_zeroing = !plan.apply_gpos && |
193 | !plan.apply_kerx && |
194 | (!plan.apply_kern |
195 | #ifndef HB_NO_OT_KERN |
196 | || !hb_ot_layout_has_cross_kerning (face) |
197 | #endif |
198 | ); |
199 | |
200 | plan.fallback_mark_positioning = plan.adjust_mark_positioning_when_zeroing && |
201 | script_fallback_mark_positioning; |
202 | |
203 | #ifndef HB_NO_AAT_SHAPE |
204 | /* Currently we always apply trak. */ |
205 | plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face); |
206 | #endif |
207 | } |
208 | |
209 | bool |
210 | hb_ot_shape_plan_t::init0 (hb_face_t *face, |
211 | const hb_shape_plan_key_t *key) |
212 | { |
213 | map.init (); |
214 | #ifndef HB_NO_AAT_SHAPE |
215 | aat_map.init (); |
216 | #endif |
217 | |
218 | hb_ot_shape_planner_t planner (face, |
219 | &key->props); |
220 | |
221 | hb_ot_shape_collect_features (&planner, |
222 | key->user_features, |
223 | key->num_user_features); |
224 | |
225 | planner.compile (*this, key->ot); |
226 | |
227 | if (shaper->data_create) |
228 | { |
229 | data = shaper->data_create (this); |
230 | if (unlikely (!data)) |
231 | return false; |
232 | } |
233 | |
234 | return true; |
235 | } |
236 | |
237 | void |
238 | hb_ot_shape_plan_t::fini () |
239 | { |
240 | if (shaper->data_destroy) |
241 | shaper->data_destroy (const_cast<void *> (data)); |
242 | |
243 | map.fini (); |
244 | #ifndef HB_NO_AAT_SHAPE |
245 | aat_map.fini (); |
246 | #endif |
247 | } |
248 | |
249 | void |
250 | hb_ot_shape_plan_t::substitute (hb_font_t *font, |
251 | hb_buffer_t *buffer) const |
252 | { |
253 | #ifndef HB_NO_AAT_SHAPE |
254 | if (unlikely (apply_morx)) |
255 | hb_aat_layout_substitute (this, font, buffer); |
256 | else |
257 | #endif |
258 | map.substitute (this, font, buffer); |
259 | } |
260 | |
261 | void |
262 | hb_ot_shape_plan_t::position (hb_font_t *font, |
263 | hb_buffer_t *buffer) const |
264 | { |
265 | if (this->apply_gpos) |
266 | map.position (this, font, buffer); |
267 | #ifndef HB_NO_AAT_SHAPE |
268 | else if (this->apply_kerx) |
269 | hb_aat_layout_position (this, font, buffer); |
270 | #endif |
271 | #ifndef HB_NO_OT_KERN |
272 | else if (this->apply_kern) |
273 | hb_ot_layout_kern (this, font, buffer); |
274 | #endif |
275 | else |
276 | _hb_ot_shape_fallback_kern (this, font, buffer); |
277 | |
278 | #ifndef HB_NO_AAT_SHAPE |
279 | if (this->apply_trak) |
280 | hb_aat_layout_track (this, font, buffer); |
281 | #endif |
282 | } |
283 | |
284 | |
285 | static const hb_ot_map_feature_t |
286 | common_features[] = |
287 | { |
288 | {HB_TAG('a','b','v','m'), F_GLOBAL}, |
289 | {HB_TAG('b','l','w','m'), F_GLOBAL}, |
290 | {HB_TAG('c','c','m','p'), F_GLOBAL}, |
291 | {HB_TAG('l','o','c','l'), F_GLOBAL}, |
292 | {HB_TAG('m','a','r','k'), F_GLOBAL_MANUAL_JOINERS}, |
293 | {HB_TAG('m','k','m','k'), F_GLOBAL_MANUAL_JOINERS}, |
294 | {HB_TAG('r','l','i','g'), F_GLOBAL}, |
295 | }; |
296 | |
297 | |
298 | static const hb_ot_map_feature_t |
299 | horizontal_features[] = |
300 | { |
301 | {HB_TAG('c','a','l','t'), F_GLOBAL}, |
302 | {HB_TAG('c','l','i','g'), F_GLOBAL}, |
303 | {HB_TAG('c','u','r','s'), F_GLOBAL}, |
304 | {HB_TAG('d','i','s','t'), F_GLOBAL}, |
305 | {HB_TAG('k','e','r','n'), F_GLOBAL_HAS_FALLBACK}, |
306 | {HB_TAG('l','i','g','a'), F_GLOBAL}, |
307 | {HB_TAG('r','c','l','t'), F_GLOBAL}, |
308 | }; |
309 | |
310 | static void |
311 | hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, |
312 | const hb_feature_t *user_features, |
313 | unsigned int num_user_features) |
314 | { |
315 | hb_ot_map_builder_t *map = &planner->map; |
316 | |
317 | map->enable_feature (HB_TAG('r','v','r','n')); |
318 | map->add_gsub_pause (nullptr); |
319 | |
320 | switch (planner->props.direction) { |
321 | case HB_DIRECTION_LTR: |
322 | map->enable_feature (HB_TAG ('l','t','r','a')); |
323 | map->enable_feature (HB_TAG ('l','t','r','m')); |
324 | break; |
325 | case HB_DIRECTION_RTL: |
326 | map->enable_feature (HB_TAG ('r','t','l','a')); |
327 | map->add_feature (HB_TAG ('r','t','l','m')); |
328 | break; |
329 | case HB_DIRECTION_TTB: |
330 | case HB_DIRECTION_BTT: |
331 | case HB_DIRECTION_INVALID: |
332 | default: |
333 | break; |
334 | } |
335 | |
336 | #ifndef HB_NO_OT_SHAPE_FRACTIONS |
337 | /* Automatic fractions. */ |
338 | map->add_feature (HB_TAG ('f','r','a','c')); |
339 | map->add_feature (HB_TAG ('n','u','m','r')); |
340 | map->add_feature (HB_TAG ('d','n','o','m')); |
341 | #endif |
342 | |
343 | /* Random! */ |
344 | map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); |
345 | |
346 | #ifndef HB_NO_AAT_SHAPE |
347 | /* Tracking. We enable dummy feature here just to allow disabling |
348 | * AAT 'trak' table using features. |
349 | * https://github.com/harfbuzz/harfbuzz/issues/1303 */ |
350 | map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK); |
351 | #endif |
352 | |
353 | map->enable_feature (HB_TAG ('H','A','R','F')); |
354 | |
355 | if (planner->shaper->collect_features) |
356 | planner->shaper->collect_features (planner); |
357 | |
358 | map->enable_feature (HB_TAG ('B','U','Z','Z')); |
359 | |
360 | for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) |
361 | map->add_feature (common_features[i]); |
362 | |
363 | if (HB_DIRECTION_IS_HORIZONTAL (planner->props.direction)) |
364 | for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) |
365 | map->add_feature (horizontal_features[i]); |
366 | else |
367 | { |
368 | /* We really want to find a 'vert' feature if there's any in the font, no |
369 | * matter which script/langsys it is listed (or not) under. |
370 | * See various bugs referenced from: |
371 | * https://github.com/harfbuzz/harfbuzz/issues/63 */ |
372 | map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH); |
373 | } |
374 | |
375 | for (unsigned int i = 0; i < num_user_features; i++) |
376 | { |
377 | const hb_feature_t *feature = &user_features[i]; |
378 | map->add_feature (feature->tag, |
379 | (feature->start == HB_FEATURE_GLOBAL_START && |
380 | feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE, |
381 | feature->value); |
382 | } |
383 | |
384 | #ifndef HB_NO_AAT_SHAPE |
385 | if (planner->apply_morx) |
386 | { |
387 | hb_aat_map_builder_t *aat_map = &planner->aat_map; |
388 | for (unsigned int i = 0; i < num_user_features; i++) |
389 | { |
390 | const hb_feature_t *feature = &user_features[i]; |
391 | aat_map->add_feature (feature->tag, feature->value); |
392 | } |
393 | } |
394 | #endif |
395 | |
396 | if (planner->shaper->override_features) |
397 | planner->shaper->override_features (planner); |
398 | } |
399 | |
400 | |
401 | /* |
402 | * shaper face data |
403 | */ |
404 | |
405 | struct hb_ot_face_data_t {}; |
406 | |
407 | hb_ot_face_data_t * |
408 | _hb_ot_shaper_face_data_create (hb_face_t *face) |
409 | { |
410 | return (hb_ot_face_data_t *) HB_SHAPER_DATA_SUCCEEDED; |
411 | } |
412 | |
413 | void |
414 | _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data) |
415 | { |
416 | } |
417 | |
418 | |
419 | /* |
420 | * shaper font data |
421 | */ |
422 | |
423 | struct hb_ot_font_data_t {}; |
424 | |
425 | hb_ot_font_data_t * |
426 | _hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) |
427 | { |
428 | return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; |
429 | } |
430 | |
431 | void |
432 | _hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data HB_UNUSED) |
433 | { |
434 | } |
435 | |
436 | |
437 | /* |
438 | * shaper |
439 | */ |
440 | |
441 | struct hb_ot_shape_context_t |
442 | { |
443 | hb_ot_shape_plan_t *plan; |
444 | hb_font_t *font; |
445 | hb_face_t *face; |
446 | hb_buffer_t *buffer; |
447 | const hb_feature_t *user_features; |
448 | unsigned int num_user_features; |
449 | |
450 | /* Transient stuff */ |
451 | hb_direction_t target_direction; |
452 | }; |
453 | |
454 | |
455 | |
456 | /* Main shaper */ |
457 | |
458 | |
459 | /* Prepare */ |
460 | |
461 | static void |
462 | hb_set_unicode_props (hb_buffer_t *buffer) |
463 | { |
464 | /* Implement enough of Unicode Graphemes here that shaping |
465 | * in reverse-direction wouldn't break graphemes. Namely, |
466 | * we mark all marks and ZWJ and ZWJ,Extended_Pictographic |
467 | * sequences as continuations. The foreach_grapheme() |
468 | * macro uses this bit. |
469 | * |
470 | * https://www.unicode.org/reports/tr29/#Regex_Definitions |
471 | */ |
472 | unsigned int count = buffer->len; |
473 | hb_glyph_info_t *info = buffer->info; |
474 | for (unsigned int i = 0; i < count; i++) |
475 | { |
476 | _hb_glyph_info_set_unicode_props (&info[i], buffer); |
477 | |
478 | /* Marks are already set as continuation by the above line. |
479 | * Handle Emoji_Modifier and ZWJ-continuation. */ |
480 | if (unlikely (_hb_glyph_info_get_general_category (&info[i]) == HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL && |
481 | hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x1F3FBu, 0x1F3FFu))) |
482 | { |
483 | _hb_glyph_info_set_continuation (&info[i]); |
484 | } |
485 | #ifndef HB_NO_EMOJI_SEQUENCES |
486 | else if (unlikely (_hb_glyph_info_is_zwj (&info[i]))) |
487 | { |
488 | _hb_glyph_info_set_continuation (&info[i]); |
489 | if (i + 1 < count && |
490 | _hb_unicode_is_emoji_Extended_Pictographic (info[i + 1].codepoint)) |
491 | { |
492 | i++; |
493 | _hb_glyph_info_set_unicode_props (&info[i], buffer); |
494 | _hb_glyph_info_set_continuation (&info[i]); |
495 | } |
496 | } |
497 | #endif |
498 | /* Or part of the Other_Grapheme_Extend that is not marks. |
499 | * As of Unicode 11 that is just: |
500 | * |
501 | * 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER |
502 | * FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK |
503 | * E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG |
504 | * |
505 | * ZWNJ is special, we don't want to merge it as there's no need, and keeping |
506 | * it separate results in more granular clusters. Ignore Katakana for now. |
507 | * Tags are used for Emoji sub-region flag sequences: |
508 | * https://github.com/harfbuzz/harfbuzz/issues/1556 |
509 | */ |
510 | else if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 0xE0020u, 0xE007Fu))) |
511 | _hb_glyph_info_set_continuation (&info[i]); |
512 | } |
513 | } |
514 | |
515 | static void |
516 | hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) |
517 | { |
518 | if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) |
519 | return; |
520 | |
521 | if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || |
522 | buffer->context_len[0] || |
523 | !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) |
524 | return; |
525 | |
526 | if (!font->has_glyph (0x25CCu)) |
527 | return; |
528 | |
529 | hb_glyph_info_t dottedcircle = {0}; |
530 | dottedcircle.codepoint = 0x25CCu; |
531 | _hb_glyph_info_set_unicode_props (&dottedcircle, buffer); |
532 | |
533 | buffer->clear_output (); |
534 | |
535 | buffer->idx = 0; |
536 | hb_glyph_info_t info = dottedcircle; |
537 | info.cluster = buffer->cur().cluster; |
538 | info.mask = buffer->cur().mask; |
539 | buffer->output_info (info); |
540 | while (buffer->idx < buffer->len && buffer->successful) |
541 | buffer->next_glyph (); |
542 | buffer->swap_buffers (); |
543 | } |
544 | |
545 | static void |
546 | hb_form_clusters (hb_buffer_t *buffer) |
547 | { |
548 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) |
549 | return; |
550 | |
551 | if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) |
552 | foreach_grapheme (buffer, start, end) |
553 | buffer->merge_clusters (start, end); |
554 | else |
555 | foreach_grapheme (buffer, start, end) |
556 | buffer->unsafe_to_break (start, end); |
557 | } |
558 | |
559 | static void |
560 | hb_ensure_native_direction (hb_buffer_t *buffer) |
561 | { |
562 | hb_direction_t direction = buffer->props.direction; |
563 | hb_direction_t horiz_dir = hb_script_get_horizontal_direction (buffer->props.script); |
564 | |
565 | /* TODO vertical: |
566 | * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType |
567 | * Ogham fonts are supposed to be implemented BTT or not. Need to research that |
568 | * first. */ |
569 | if ((HB_DIRECTION_IS_HORIZONTAL (direction) && |
570 | direction != horiz_dir && horiz_dir != HB_DIRECTION_INVALID) || |
571 | (HB_DIRECTION_IS_VERTICAL (direction) && |
572 | direction != HB_DIRECTION_TTB)) |
573 | { |
574 | |
575 | if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) |
576 | foreach_grapheme (buffer, start, end) |
577 | { |
578 | buffer->merge_clusters (start, end); |
579 | buffer->reverse_range (start, end); |
580 | } |
581 | else |
582 | foreach_grapheme (buffer, start, end) |
583 | /* form_clusters() merged clusters already, we don't merge. */ |
584 | buffer->reverse_range (start, end); |
585 | |
586 | buffer->reverse (); |
587 | |
588 | buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); |
589 | } |
590 | } |
591 | |
592 | |
593 | /* |
594 | * Substitute |
595 | */ |
596 | |
597 | static inline void |
598 | hb_ot_mirror_chars (const hb_ot_shape_context_t *c) |
599 | { |
600 | if (HB_DIRECTION_IS_FORWARD (c->target_direction)) |
601 | return; |
602 | |
603 | hb_buffer_t *buffer = c->buffer; |
604 | hb_unicode_funcs_t *unicode = buffer->unicode; |
605 | hb_mask_t rtlm_mask = c->plan->rtlm_mask; |
606 | |
607 | unsigned int count = buffer->len; |
608 | hb_glyph_info_t *info = buffer->info; |
609 | for (unsigned int i = 0; i < count; i++) { |
610 | hb_codepoint_t codepoint = unicode->mirroring (info[i].codepoint); |
611 | if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint))) |
612 | info[i].mask |= rtlm_mask; |
613 | else |
614 | info[i].codepoint = codepoint; |
615 | } |
616 | } |
617 | |
618 | static inline void |
619 | hb_ot_shape_setup_masks_fraction (const hb_ot_shape_context_t *c) |
620 | { |
621 | #ifdef HB_NO_OT_SHAPE_FRACTIONS |
622 | return; |
623 | #endif |
624 | |
625 | if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || |
626 | !c->plan->has_frac) |
627 | return; |
628 | |
629 | hb_buffer_t *buffer = c->buffer; |
630 | |
631 | hb_mask_t pre_mask, post_mask; |
632 | if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) |
633 | { |
634 | pre_mask = c->plan->numr_mask | c->plan->frac_mask; |
635 | post_mask = c->plan->frac_mask | c->plan->dnom_mask; |
636 | } |
637 | else |
638 | { |
639 | pre_mask = c->plan->frac_mask | c->plan->dnom_mask; |
640 | post_mask = c->plan->numr_mask | c->plan->frac_mask; |
641 | } |
642 | |
643 | unsigned int count = buffer->len; |
644 | hb_glyph_info_t *info = buffer->info; |
645 | for (unsigned int i = 0; i < count; i++) |
646 | { |
647 | if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */ |
648 | { |
649 | unsigned int start = i, end = i + 1; |
650 | while (start && |
651 | _hb_glyph_info_get_general_category (&info[start - 1]) == |
652 | HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) |
653 | start--; |
654 | while (end < count && |
655 | _hb_glyph_info_get_general_category (&info[end]) == |
656 | HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) |
657 | end++; |
658 | |
659 | buffer->unsafe_to_break (start, end); |
660 | |
661 | for (unsigned int j = start; j < i; j++) |
662 | info[j].mask |= pre_mask; |
663 | info[i].mask |= c->plan->frac_mask; |
664 | for (unsigned int j = i + 1; j < end; j++) |
665 | info[j].mask |= post_mask; |
666 | |
667 | i = end - 1; |
668 | } |
669 | } |
670 | } |
671 | |
672 | static inline void |
673 | hb_ot_shape_initialize_masks (const hb_ot_shape_context_t *c) |
674 | { |
675 | hb_ot_map_t *map = &c->plan->map; |
676 | hb_buffer_t *buffer = c->buffer; |
677 | |
678 | hb_mask_t global_mask = map->get_global_mask (); |
679 | buffer->reset_masks (global_mask); |
680 | } |
681 | |
682 | static inline void |
683 | hb_ot_shape_setup_masks (const hb_ot_shape_context_t *c) |
684 | { |
685 | hb_ot_map_t *map = &c->plan->map; |
686 | hb_buffer_t *buffer = c->buffer; |
687 | |
688 | hb_ot_shape_setup_masks_fraction (c); |
689 | |
690 | if (c->plan->shaper->setup_masks) |
691 | c->plan->shaper->setup_masks (c->plan, buffer, c->font); |
692 | |
693 | for (unsigned int i = 0; i < c->num_user_features; i++) |
694 | { |
695 | const hb_feature_t *feature = &c->user_features[i]; |
696 | if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { |
697 | unsigned int shift; |
698 | hb_mask_t mask = map->get_mask (feature->tag, &shift); |
699 | buffer->set_masks (feature->value << shift, mask, feature->start, feature->end); |
700 | } |
701 | } |
702 | } |
703 | |
704 | static void |
705 | hb_ot_zero_width_default_ignorables (const hb_buffer_t *buffer) |
706 | { |
707 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || |
708 | (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES) || |
709 | (buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES)) |
710 | return; |
711 | |
712 | unsigned int count = buffer->len; |
713 | hb_glyph_info_t *info = buffer->info; |
714 | hb_glyph_position_t *pos = buffer->pos; |
715 | unsigned int i = 0; |
716 | for (i = 0; i < count; i++) |
717 | if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) |
718 | pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; |
719 | } |
720 | |
721 | static void |
722 | hb_ot_hide_default_ignorables (hb_buffer_t *buffer, |
723 | hb_font_t *font) |
724 | { |
725 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || |
726 | (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) |
727 | return; |
728 | |
729 | unsigned int count = buffer->len; |
730 | hb_glyph_info_t *info = buffer->info; |
731 | |
732 | hb_codepoint_t invisible = buffer->invisible; |
733 | if (!(buffer->flags & HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES) && |
734 | (invisible || font->get_nominal_glyph (' ', &invisible))) |
735 | { |
736 | /* Replace default-ignorables with a zero-advance invisible glyph. */ |
737 | for (unsigned int i = 0; i < count; i++) |
738 | { |
739 | if (_hb_glyph_info_is_default_ignorable (&info[i])) |
740 | info[i].codepoint = invisible; |
741 | } |
742 | } |
743 | else |
744 | hb_ot_layout_delete_glyphs_inplace (buffer, _hb_glyph_info_is_default_ignorable); |
745 | } |
746 | |
747 | |
748 | static inline void |
749 | hb_ot_map_glyphs_fast (hb_buffer_t *buffer) |
750 | { |
751 | /* Normalization process sets up glyph_index(), we just copy it. */ |
752 | unsigned int count = buffer->len; |
753 | hb_glyph_info_t *info = buffer->info; |
754 | for (unsigned int i = 0; i < count; i++) |
755 | info[i].codepoint = info[i].glyph_index(); |
756 | |
757 | buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; |
758 | } |
759 | |
760 | static inline void |
761 | hb_synthesize_glyph_classes (hb_buffer_t *buffer) |
762 | { |
763 | unsigned int count = buffer->len; |
764 | hb_glyph_info_t *info = buffer->info; |
765 | for (unsigned int i = 0; i < count; i++) |
766 | { |
767 | hb_ot_layout_glyph_props_flags_t klass; |
768 | |
769 | /* Never mark default-ignorables as marks. |
770 | * They won't get in the way of lookups anyway, |
771 | * but having them as mark will cause them to be skipped |
772 | * over if the lookup-flag says so, but at least for the |
773 | * Mongolian variation selectors, looks like Uniscribe |
774 | * marks them as non-mark. Some Mongolian fonts without |
775 | * GDEF rely on this. Another notable character that |
776 | * this applies to is COMBINING GRAPHEME JOINER. */ |
777 | klass = (_hb_glyph_info_get_general_category (&info[i]) != |
778 | HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK || |
779 | _hb_glyph_info_is_default_ignorable (&info[i])) ? |
780 | HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : |
781 | HB_OT_LAYOUT_GLYPH_PROPS_MARK; |
782 | _hb_glyph_info_set_glyph_props (&info[i], klass); |
783 | } |
784 | } |
785 | |
786 | static inline void |
787 | hb_ot_substitute_default (const hb_ot_shape_context_t *c) |
788 | { |
789 | hb_buffer_t *buffer = c->buffer; |
790 | |
791 | hb_ot_mirror_chars (c); |
792 | |
793 | HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); |
794 | |
795 | _hb_ot_shape_normalize (c->plan, buffer, c->font); |
796 | |
797 | hb_ot_shape_setup_masks (c); |
798 | |
799 | /* This is unfortunate to go here, but necessary... */ |
800 | if (c->plan->fallback_mark_positioning) |
801 | _hb_ot_shape_fallback_mark_position_recategorize_marks (c->plan, c->font, buffer); |
802 | |
803 | hb_ot_map_glyphs_fast (buffer); |
804 | |
805 | HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); |
806 | } |
807 | |
808 | static inline void |
809 | hb_ot_substitute_complex (const hb_ot_shape_context_t *c) |
810 | { |
811 | hb_buffer_t *buffer = c->buffer; |
812 | |
813 | hb_ot_layout_substitute_start (c->font, buffer); |
814 | |
815 | if (c->plan->fallback_glyph_classes) |
816 | hb_synthesize_glyph_classes (c->buffer); |
817 | |
818 | c->plan->substitute (c->font, buffer); |
819 | } |
820 | |
821 | static inline void |
822 | hb_ot_substitute_pre (const hb_ot_shape_context_t *c) |
823 | { |
824 | hb_ot_substitute_default (c); |
825 | |
826 | _hb_buffer_allocate_gsubgpos_vars (c->buffer); |
827 | |
828 | hb_ot_substitute_complex (c); |
829 | } |
830 | |
831 | static inline void |
832 | hb_ot_substitute_post (const hb_ot_shape_context_t *c) |
833 | { |
834 | hb_ot_hide_default_ignorables (c->buffer, c->font); |
835 | #ifndef HB_NO_AAT_SHAPE |
836 | if (c->plan->apply_morx) |
837 | hb_aat_layout_remove_deleted_glyphs (c->buffer); |
838 | #endif |
839 | |
840 | if (c->plan->shaper->postprocess_glyphs) |
841 | c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); |
842 | } |
843 | |
844 | |
845 | /* |
846 | * Position |
847 | */ |
848 | |
849 | static inline void |
850 | adjust_mark_offsets (hb_glyph_position_t *pos) |
851 | { |
852 | pos->x_offset -= pos->x_advance; |
853 | pos->y_offset -= pos->y_advance; |
854 | } |
855 | |
856 | static inline void |
857 | zero_mark_width (hb_glyph_position_t *pos) |
858 | { |
859 | pos->x_advance = 0; |
860 | pos->y_advance = 0; |
861 | } |
862 | |
863 | static inline void |
864 | zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) |
865 | { |
866 | unsigned int count = buffer->len; |
867 | hb_glyph_info_t *info = buffer->info; |
868 | for (unsigned int i = 0; i < count; i++) |
869 | if (_hb_glyph_info_is_mark (&info[i])) |
870 | { |
871 | if (adjust_offsets) |
872 | adjust_mark_offsets (&buffer->pos[i]); |
873 | zero_mark_width (&buffer->pos[i]); |
874 | } |
875 | } |
876 | |
877 | static inline void |
878 | hb_ot_position_default (const hb_ot_shape_context_t *c) |
879 | { |
880 | hb_direction_t direction = c->buffer->props.direction; |
881 | unsigned int count = c->buffer->len; |
882 | hb_glyph_info_t *info = c->buffer->info; |
883 | hb_glyph_position_t *pos = c->buffer->pos; |
884 | |
885 | if (HB_DIRECTION_IS_HORIZONTAL (direction)) |
886 | { |
887 | c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]), |
888 | &pos[0].x_advance, sizeof(pos[0])); |
889 | /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ |
890 | if (c->font->has_glyph_h_origin_func ()) |
891 | for (unsigned int i = 0; i < count; i++) |
892 | c->font->subtract_glyph_h_origin (info[i].codepoint, |
893 | &pos[i].x_offset, |
894 | &pos[i].y_offset); |
895 | } |
896 | else |
897 | { |
898 | c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]), |
899 | &pos[0].y_advance, sizeof(pos[0])); |
900 | for (unsigned int i = 0; i < count; i++) |
901 | { |
902 | c->font->subtract_glyph_v_origin (info[i].codepoint, |
903 | &pos[i].x_offset, |
904 | &pos[i].y_offset); |
905 | } |
906 | } |
907 | if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) |
908 | _hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer); |
909 | } |
910 | |
911 | static inline void |
912 | hb_ot_position_complex (const hb_ot_shape_context_t *c) |
913 | { |
914 | unsigned int count = c->buffer->len; |
915 | hb_glyph_info_t *info = c->buffer->info; |
916 | hb_glyph_position_t *pos = c->buffer->pos; |
917 | |
918 | /* If the font has no GPOS and direction is forward, then when |
919 | * zeroing mark widths, we shift the mark with it, such that the |
920 | * mark is positioned hanging over the previous glyph. When |
921 | * direction is backward we don't shift and it will end up |
922 | * hanging over the next glyph after the final reordering. |
923 | * |
924 | * Note: If fallback positinoing happens, we don't care about |
925 | * this as it will be overriden. |
926 | */ |
927 | bool adjust_offsets_when_zeroing = c->plan->adjust_mark_positioning_when_zeroing && |
928 | HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); |
929 | |
930 | /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ |
931 | |
932 | /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ |
933 | if (c->font->has_glyph_h_origin_func ()) |
934 | for (unsigned int i = 0; i < count; i++) |
935 | c->font->add_glyph_h_origin (info[i].codepoint, |
936 | &pos[i].x_offset, |
937 | &pos[i].y_offset); |
938 | |
939 | hb_ot_layout_position_start (c->font, c->buffer); |
940 | |
941 | if (c->plan->zero_marks) |
942 | switch (c->plan->shaper->zero_width_marks) |
943 | { |
944 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: |
945 | zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); |
946 | break; |
947 | |
948 | default: |
949 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: |
950 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: |
951 | break; |
952 | } |
953 | |
954 | c->plan->position (c->font, c->buffer); |
955 | |
956 | if (c->plan->zero_marks) |
957 | switch (c->plan->shaper->zero_width_marks) |
958 | { |
959 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: |
960 | zero_mark_widths_by_gdef (c->buffer, adjust_offsets_when_zeroing); |
961 | break; |
962 | |
963 | default: |
964 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: |
965 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: |
966 | break; |
967 | } |
968 | |
969 | /* Finish off. Has to follow a certain order. */ |
970 | hb_ot_layout_position_finish_advances (c->font, c->buffer); |
971 | hb_ot_zero_width_default_ignorables (c->buffer); |
972 | #ifndef HB_NO_AAT_SHAPE |
973 | if (c->plan->apply_morx) |
974 | hb_aat_layout_zero_width_deleted_glyphs (c->buffer); |
975 | #endif |
976 | hb_ot_layout_position_finish_offsets (c->font, c->buffer); |
977 | |
978 | /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ |
979 | if (c->font->has_glyph_h_origin_func ()) |
980 | for (unsigned int i = 0; i < count; i++) |
981 | c->font->subtract_glyph_h_origin (info[i].codepoint, |
982 | &pos[i].x_offset, |
983 | &pos[i].y_offset); |
984 | |
985 | if (c->plan->fallback_mark_positioning) |
986 | _hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer, |
987 | adjust_offsets_when_zeroing); |
988 | } |
989 | |
990 | static inline void |
991 | hb_ot_position (const hb_ot_shape_context_t *c) |
992 | { |
993 | c->buffer->clear_positions (); |
994 | |
995 | hb_ot_position_default (c); |
996 | |
997 | hb_ot_position_complex (c); |
998 | |
999 | if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) |
1000 | hb_buffer_reverse (c->buffer); |
1001 | |
1002 | _hb_buffer_deallocate_gsubgpos_vars (c->buffer); |
1003 | } |
1004 | |
1005 | static inline void |
1006 | hb_propagate_flags (hb_buffer_t *buffer) |
1007 | { |
1008 | /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. |
1009 | * Simplifies using them. */ |
1010 | |
1011 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK)) |
1012 | return; |
1013 | |
1014 | hb_glyph_info_t *info = buffer->info; |
1015 | |
1016 | foreach_cluster (buffer, start, end) |
1017 | { |
1018 | unsigned int mask = 0; |
1019 | for (unsigned int i = start; i < end; i++) |
1020 | if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) |
1021 | { |
1022 | mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; |
1023 | break; |
1024 | } |
1025 | if (mask) |
1026 | for (unsigned int i = start; i < end; i++) |
1027 | info[i].mask |= mask; |
1028 | } |
1029 | } |
1030 | |
1031 | /* Pull it all together! */ |
1032 | |
1033 | static void |
1034 | hb_ot_shape_internal (hb_ot_shape_context_t *c) |
1035 | { |
1036 | c->buffer->deallocate_var_all (); |
1037 | c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; |
1038 | if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR))) |
1039 | { |
1040 | c->buffer->max_len = hb_max (c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, |
1041 | (unsigned) HB_BUFFER_MAX_LEN_MIN); |
1042 | } |
1043 | if (likely (!hb_unsigned_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR))) |
1044 | { |
1045 | c->buffer->max_ops = hb_max (c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, |
1046 | (unsigned) HB_BUFFER_MAX_OPS_MIN); |
1047 | } |
1048 | |
1049 | /* Save the original direction, we use it later. */ |
1050 | c->target_direction = c->buffer->props.direction; |
1051 | |
1052 | _hb_buffer_allocate_unicode_vars (c->buffer); |
1053 | |
1054 | c->buffer->clear_output (); |
1055 | |
1056 | hb_ot_shape_initialize_masks (c); |
1057 | hb_set_unicode_props (c->buffer); |
1058 | hb_insert_dotted_circle (c->buffer, c->font); |
1059 | |
1060 | hb_form_clusters (c->buffer); |
1061 | |
1062 | hb_ensure_native_direction (c->buffer); |
1063 | |
1064 | if (c->plan->shaper->preprocess_text) |
1065 | c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); |
1066 | |
1067 | hb_ot_substitute_pre (c); |
1068 | hb_ot_position (c); |
1069 | hb_ot_substitute_post (c); |
1070 | |
1071 | hb_propagate_flags (c->buffer); |
1072 | |
1073 | _hb_buffer_deallocate_unicode_vars (c->buffer); |
1074 | |
1075 | c->buffer->props.direction = c->target_direction; |
1076 | |
1077 | c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; |
1078 | c->buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT; |
1079 | c->buffer->deallocate_var_all (); |
1080 | } |
1081 | |
1082 | |
1083 | hb_bool_t |
1084 | _hb_ot_shape (hb_shape_plan_t *shape_plan, |
1085 | hb_font_t *font, |
1086 | hb_buffer_t *buffer, |
1087 | const hb_feature_t *features, |
1088 | unsigned int num_features) |
1089 | { |
1090 | hb_ot_shape_context_t c = {&shape_plan->ot, font, font->face, buffer, features, num_features}; |
1091 | hb_ot_shape_internal (&c); |
1092 | |
1093 | return true; |
1094 | } |
1095 | |
1096 | |
1097 | /** |
1098 | * hb_ot_shape_plan_collect_lookups: |
1099 | * |
1100 | * Since: 0.9.7 |
1101 | **/ |
1102 | void |
1103 | hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, |
1104 | hb_tag_t table_tag, |
1105 | hb_set_t *lookup_indexes /* OUT */) |
1106 | { |
1107 | shape_plan->ot.collect_lookups (table_tag, lookup_indexes); |
1108 | } |
1109 | |
1110 | |
1111 | /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ |
1112 | static void |
1113 | add_char (hb_font_t *font, |
1114 | hb_unicode_funcs_t *unicode, |
1115 | hb_bool_t mirror, |
1116 | hb_codepoint_t u, |
1117 | hb_set_t *glyphs) |
1118 | { |
1119 | hb_codepoint_t glyph; |
1120 | if (font->get_nominal_glyph (u, &glyph)) |
1121 | glyphs->add (glyph); |
1122 | if (mirror) |
1123 | { |
1124 | hb_codepoint_t m = unicode->mirroring (u); |
1125 | if (m != u && font->get_nominal_glyph (m, &glyph)) |
1126 | glyphs->add (glyph); |
1127 | } |
1128 | } |
1129 | |
1130 | |
1131 | /** |
1132 | * hb_ot_shape_glyphs_closure: |
1133 | * |
1134 | * Since: 0.9.2 |
1135 | **/ |
1136 | void |
1137 | hb_ot_shape_glyphs_closure (hb_font_t *font, |
1138 | hb_buffer_t *buffer, |
1139 | const hb_feature_t *features, |
1140 | unsigned int num_features, |
1141 | hb_set_t *glyphs) |
1142 | { |
1143 | const char *shapers[] = {"ot" , nullptr}; |
1144 | hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, |
1145 | features, num_features, shapers); |
1146 | |
1147 | bool mirror = hb_script_get_horizontal_direction (buffer->props.script) == HB_DIRECTION_RTL; |
1148 | |
1149 | unsigned int count = buffer->len; |
1150 | hb_glyph_info_t *info = buffer->info; |
1151 | for (unsigned int i = 0; i < count; i++) |
1152 | add_char (font, buffer->unicode, mirror, info[i].codepoint, glyphs); |
1153 | |
1154 | hb_set_t *lookups = hb_set_create (); |
1155 | hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookups); |
1156 | hb_ot_layout_lookups_substitute_closure (font->face, lookups, glyphs); |
1157 | |
1158 | hb_set_destroy (lookups); |
1159 | |
1160 | hb_shape_plan_destroy (shape_plan); |
1161 | } |
1162 | |
1163 | |
1164 | #endif |
1165 | |