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 | #define HB_SHAPER ot |
30 | #define hb_ot_shaper_face_data_t hb_ot_layout_t |
31 | #define hb_ot_shaper_shape_plan_data_t hb_ot_shape_plan_t |
32 | #include "hb-shaper-impl-private.hh" |
33 | |
34 | #include "hb-ot-shape-private.hh" |
35 | #include "hb-ot-shape-complex-private.hh" |
36 | #include "hb-ot-shape-fallback-private.hh" |
37 | #include "hb-ot-shape-normalize-private.hh" |
38 | |
39 | #include "hb-ot-layout-private.hh" |
40 | #include "hb-unicode-private.hh" |
41 | #include "hb-set-private.hh" |
42 | |
43 | |
44 | static hb_tag_t common_features[] = { |
45 | HB_TAG('c','c','m','p'), |
46 | HB_TAG('l','o','c','l'), |
47 | HB_TAG('m','a','r','k'), |
48 | HB_TAG('m','k','m','k'), |
49 | HB_TAG('r','l','i','g'), |
50 | }; |
51 | |
52 | |
53 | static hb_tag_t horizontal_features[] = { |
54 | HB_TAG('c','a','l','t'), |
55 | HB_TAG('c','l','i','g'), |
56 | HB_TAG('c','u','r','s'), |
57 | HB_TAG('k','e','r','n'), |
58 | HB_TAG('l','i','g','a'), |
59 | HB_TAG('r','c','l','t'), |
60 | }; |
61 | |
62 | |
63 | |
64 | static void |
65 | hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner, |
66 | const hb_segment_properties_t *props, |
67 | const hb_feature_t *user_features, |
68 | unsigned int num_user_features) |
69 | { |
70 | hb_ot_map_builder_t *map = &planner->map; |
71 | |
72 | map->add_global_bool_feature (HB_TAG('r','v','r','n')); |
73 | map->add_gsub_pause (pause_func: nullptr); |
74 | |
75 | switch (props->direction) { |
76 | case HB_DIRECTION_LTR: |
77 | map->add_global_bool_feature (HB_TAG ('l','t','r','a')); |
78 | map->add_global_bool_feature (HB_TAG ('l','t','r','m')); |
79 | break; |
80 | case HB_DIRECTION_RTL: |
81 | map->add_global_bool_feature (HB_TAG ('r','t','l','a')); |
82 | map->add_feature (HB_TAG ('r','t','l','m'), value: 1, flags: F_NONE); |
83 | break; |
84 | case HB_DIRECTION_TTB: |
85 | case HB_DIRECTION_BTT: |
86 | case HB_DIRECTION_INVALID: |
87 | default: |
88 | break; |
89 | } |
90 | |
91 | map->add_feature (HB_TAG ('f','r','a','c'), value: 1, flags: F_NONE); |
92 | map->add_feature (HB_TAG ('n','u','m','r'), value: 1, flags: F_NONE); |
93 | map->add_feature (HB_TAG ('d','n','o','m'), value: 1, flags: F_NONE); |
94 | |
95 | if (planner->shaper->collect_features) |
96 | planner->shaper->collect_features (planner); |
97 | |
98 | for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++) |
99 | map->add_global_bool_feature (tag: common_features[i]); |
100 | |
101 | if (HB_DIRECTION_IS_HORIZONTAL (props->direction)) |
102 | for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++) |
103 | map->add_feature (tag: horizontal_features[i], value: 1, flags: F_GLOBAL | |
104 | (horizontal_features[i] == HB_TAG('k','e','r','n') ? |
105 | F_HAS_FALLBACK : F_NONE)); |
106 | else |
107 | { |
108 | /* We really want to find a 'vert' feature if there's any in the font, no |
109 | * matter which script/langsys it is listed (or not) under. |
110 | * See various bugs referenced from: |
111 | * https://github.com/harfbuzz/harfbuzz/issues/63 */ |
112 | map->add_feature (HB_TAG ('v','e','r','t'), value: 1, flags: F_GLOBAL | F_GLOBAL_SEARCH); |
113 | } |
114 | |
115 | if (planner->shaper->override_features) |
116 | planner->shaper->override_features (planner); |
117 | |
118 | for (unsigned int i = 0; i < num_user_features; i++) { |
119 | const hb_feature_t *feature = &user_features[i]; |
120 | map->add_feature (tag: feature->tag, value: feature->value, |
121 | flags: (feature->start == 0 && feature->end == (unsigned int) -1) ? |
122 | F_GLOBAL : F_NONE); |
123 | } |
124 | } |
125 | |
126 | |
127 | /* |
128 | * shaper face data |
129 | */ |
130 | |
131 | HB_SHAPER_DATA_ENSURE_DEFINE(ot, face) |
132 | |
133 | hb_ot_shaper_face_data_t * |
134 | _hb_ot_shaper_face_data_create (hb_face_t *face) |
135 | { |
136 | return _hb_ot_layout_create (face); |
137 | } |
138 | |
139 | void |
140 | _hb_ot_shaper_face_data_destroy (hb_ot_shaper_face_data_t *data) |
141 | { |
142 | _hb_ot_layout_destroy (layout: data); |
143 | } |
144 | |
145 | |
146 | /* |
147 | * shaper font data |
148 | */ |
149 | |
150 | HB_SHAPER_DATA_ENSURE_DEFINE(ot, font) |
151 | |
152 | struct hb_ot_shaper_font_data_t {}; |
153 | |
154 | hb_ot_shaper_font_data_t * |
155 | _hb_ot_shaper_font_data_create (hb_font_t *font HB_UNUSED) |
156 | { |
157 | return (hb_ot_shaper_font_data_t *) HB_SHAPER_DATA_SUCCEEDED; |
158 | } |
159 | |
160 | void |
161 | _hb_ot_shaper_font_data_destroy (hb_ot_shaper_font_data_t *data) |
162 | { |
163 | } |
164 | |
165 | |
166 | /* |
167 | * shaper shape_plan data |
168 | */ |
169 | |
170 | hb_ot_shaper_shape_plan_data_t * |
171 | _hb_ot_shaper_shape_plan_data_create (hb_shape_plan_t *shape_plan, |
172 | const hb_feature_t *user_features, |
173 | unsigned int num_user_features, |
174 | const int *coords, |
175 | unsigned int num_coords) |
176 | { |
177 | hb_ot_shape_plan_t *plan = (hb_ot_shape_plan_t *) calloc (nmemb: 1, size: sizeof (hb_ot_shape_plan_t)); |
178 | if (unlikely (!plan)) |
179 | return nullptr; |
180 | |
181 | hb_ot_shape_planner_t planner (shape_plan); |
182 | |
183 | planner.shaper = hb_ot_shape_complex_categorize (planner: &planner); |
184 | |
185 | hb_ot_shape_collect_features (planner: &planner, props: &shape_plan->props, |
186 | user_features, num_user_features); |
187 | |
188 | planner.compile (plan&: *plan, coords, num_coords); |
189 | |
190 | if (plan->shaper->data_create) { |
191 | plan->data = plan->shaper->data_create (plan); |
192 | if (unlikely (!plan->data)) |
193 | return nullptr; |
194 | } |
195 | |
196 | return plan; |
197 | } |
198 | |
199 | void |
200 | _hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *plan) |
201 | { |
202 | if (plan->shaper->data_destroy) |
203 | plan->shaper->data_destroy (const_cast<void *> (plan->data)); |
204 | |
205 | plan->finish (); |
206 | |
207 | free (ptr: plan); |
208 | } |
209 | |
210 | |
211 | /* |
212 | * shaper |
213 | */ |
214 | |
215 | struct hb_ot_shape_context_t |
216 | { |
217 | hb_ot_shape_plan_t *plan; |
218 | hb_font_t *font; |
219 | hb_face_t *face; |
220 | hb_buffer_t *buffer; |
221 | const hb_feature_t *user_features; |
222 | unsigned int num_user_features; |
223 | |
224 | /* Transient stuff */ |
225 | bool fallback_positioning; |
226 | bool fallback_glyph_classes; |
227 | hb_direction_t target_direction; |
228 | }; |
229 | |
230 | |
231 | |
232 | /* Main shaper */ |
233 | |
234 | |
235 | /* Prepare */ |
236 | |
237 | static void |
238 | hb_set_unicode_props (hb_buffer_t *buffer) |
239 | { |
240 | unsigned int count = buffer->len; |
241 | hb_glyph_info_t *info = buffer->info; |
242 | for (unsigned int i = 0; i < count; i++) |
243 | _hb_glyph_info_set_unicode_props (info: &info[i], buffer); |
244 | } |
245 | |
246 | static void |
247 | hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) |
248 | { |
249 | if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || |
250 | buffer->context_len[0] || |
251 | _hb_glyph_info_get_general_category (info: &buffer->info[0]) != |
252 | HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) |
253 | return; |
254 | |
255 | if (!font->has_glyph (unicode: 0x25CCu)) |
256 | return; |
257 | |
258 | hb_glyph_info_t dottedcircle = {.codepoint: 0}; |
259 | dottedcircle.codepoint = 0x25CCu; |
260 | _hb_glyph_info_set_unicode_props (info: &dottedcircle, buffer); |
261 | |
262 | buffer->clear_output (); |
263 | |
264 | buffer->idx = 0; |
265 | hb_glyph_info_t info = dottedcircle; |
266 | info.cluster = buffer->cur().cluster; |
267 | info.mask = buffer->cur().mask; |
268 | buffer->output_info (glyph_info: info); |
269 | while (buffer->idx < buffer->len && !buffer->in_error) |
270 | buffer->next_glyph (); |
271 | |
272 | buffer->swap_buffers (); |
273 | } |
274 | |
275 | static void |
276 | hb_form_clusters (hb_buffer_t *buffer) |
277 | { |
278 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII)) |
279 | return; |
280 | |
281 | /* Loop duplicated in hb_ensure_native_direction(), and in _hb-coretext.cc */ |
282 | unsigned int base = 0; |
283 | unsigned int count = buffer->len; |
284 | hb_glyph_info_t *info = buffer->info; |
285 | for (unsigned int i = 1; i < count; i++) |
286 | { |
287 | if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])) && |
288 | !_hb_glyph_info_is_joiner (&info[i]))) |
289 | { |
290 | if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) |
291 | buffer->merge_clusters (start: base, end: i); |
292 | else |
293 | buffer->unsafe_to_break (start: base, end: i); |
294 | base = i; |
295 | } |
296 | } |
297 | if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES) |
298 | buffer->merge_clusters (start: base, end: count); |
299 | else |
300 | buffer->unsafe_to_break (start: base, end: count); |
301 | } |
302 | |
303 | static void |
304 | hb_ensure_native_direction (hb_buffer_t *buffer) |
305 | { |
306 | hb_direction_t direction = buffer->props.direction; |
307 | |
308 | /* TODO vertical: |
309 | * The only BTT vertical script is Ogham, but it's not clear to me whether OpenType |
310 | * Ogham fonts are supposed to be implemented BTT or not. Need to research that |
311 | * first. */ |
312 | if ((HB_DIRECTION_IS_HORIZONTAL (direction) && direction != hb_script_get_horizontal_direction (script: buffer->props.script)) || |
313 | (HB_DIRECTION_IS_VERTICAL (direction) && direction != HB_DIRECTION_TTB)) |
314 | { |
315 | /* Same loop as hb_form_clusters(). |
316 | * Since form_clusters() merged clusters already, we don't merge. */ |
317 | unsigned int base = 0; |
318 | unsigned int count = buffer->len; |
319 | hb_glyph_info_t *info = buffer->info; |
320 | for (unsigned int i = 1; i < count; i++) |
321 | { |
322 | if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) |
323 | { |
324 | if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) |
325 | buffer->merge_clusters (start: base, end: i); |
326 | buffer->reverse_range (start: base, end: i); |
327 | |
328 | base = i; |
329 | } |
330 | } |
331 | if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) |
332 | buffer->merge_clusters (start: base, end: count); |
333 | buffer->reverse_range (start: base, end: count); |
334 | |
335 | buffer->reverse (); |
336 | |
337 | buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); |
338 | } |
339 | } |
340 | |
341 | |
342 | /* Substitute */ |
343 | |
344 | static inline void |
345 | hb_ot_mirror_chars (hb_ot_shape_context_t *c) |
346 | { |
347 | if (HB_DIRECTION_IS_FORWARD (c->target_direction)) |
348 | return; |
349 | |
350 | hb_buffer_t *buffer = c->buffer; |
351 | hb_unicode_funcs_t *unicode = buffer->unicode; |
352 | hb_mask_t rtlm_mask = c->plan->rtlm_mask; |
353 | |
354 | unsigned int count = buffer->len; |
355 | hb_glyph_info_t *info = buffer->info; |
356 | for (unsigned int i = 0; i < count; i++) { |
357 | hb_codepoint_t codepoint = unicode->mirroring (unicode: info[i].codepoint); |
358 | if (likely (codepoint == info[i].codepoint || !c->font->has_glyph (codepoint))) |
359 | info[i].mask |= rtlm_mask; |
360 | else |
361 | info[i].codepoint = codepoint; |
362 | } |
363 | } |
364 | |
365 | static inline void |
366 | hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c) |
367 | { |
368 | if (!(c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_NON_ASCII) || |
369 | !c->plan->has_frac) |
370 | return; |
371 | |
372 | hb_buffer_t *buffer = c->buffer; |
373 | |
374 | hb_mask_t pre_mask, post_mask; |
375 | if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) |
376 | { |
377 | pre_mask = c->plan->numr_mask | c->plan->frac_mask; |
378 | post_mask = c->plan->frac_mask | c->plan->dnom_mask; |
379 | } |
380 | else |
381 | { |
382 | pre_mask = c->plan->frac_mask | c->plan->dnom_mask; |
383 | post_mask = c->plan->numr_mask | c->plan->frac_mask; |
384 | } |
385 | |
386 | unsigned int count = buffer->len; |
387 | hb_glyph_info_t *info = buffer->info; |
388 | for (unsigned int i = 0; i < count; i++) |
389 | { |
390 | if (info[i].codepoint == 0x2044u) /* FRACTION SLASH */ |
391 | { |
392 | unsigned int start = i, end = i + 1; |
393 | while (start && |
394 | _hb_glyph_info_get_general_category (info: &info[start - 1]) == |
395 | HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) |
396 | start--; |
397 | while (end < count && |
398 | _hb_glyph_info_get_general_category (info: &info[end]) == |
399 | HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) |
400 | end++; |
401 | |
402 | buffer->unsafe_to_break (start, end); |
403 | |
404 | for (unsigned int j = start; j < i; j++) |
405 | info[j].mask |= pre_mask; |
406 | info[i].mask |= c->plan->frac_mask; |
407 | for (unsigned int j = i + 1; j < end; j++) |
408 | info[j].mask |= post_mask; |
409 | |
410 | i = end - 1; |
411 | } |
412 | } |
413 | } |
414 | |
415 | static inline void |
416 | hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c) |
417 | { |
418 | hb_ot_map_t *map = &c->plan->map; |
419 | hb_buffer_t *buffer = c->buffer; |
420 | |
421 | hb_mask_t global_mask = map->get_global_mask (); |
422 | buffer->reset_masks (mask: global_mask); |
423 | } |
424 | |
425 | static inline void |
426 | hb_ot_shape_setup_masks (hb_ot_shape_context_t *c) |
427 | { |
428 | hb_ot_map_t *map = &c->plan->map; |
429 | hb_buffer_t *buffer = c->buffer; |
430 | |
431 | hb_ot_shape_setup_masks_fraction (c); |
432 | |
433 | if (c->plan->shaper->setup_masks) |
434 | c->plan->shaper->setup_masks (c->plan, buffer, c->font); |
435 | |
436 | for (unsigned int i = 0; i < c->num_user_features; i++) |
437 | { |
438 | const hb_feature_t *feature = &c->user_features[i]; |
439 | if (!(feature->start == 0 && feature->end == (unsigned int)-1)) { |
440 | unsigned int shift; |
441 | hb_mask_t mask = map->get_mask (feature_tag: feature->tag, shift: &shift); |
442 | buffer->set_masks (value: feature->value << shift, mask, cluster_start: feature->start, cluster_end: feature->end); |
443 | } |
444 | } |
445 | } |
446 | |
447 | static void |
448 | hb_ot_zero_width_default_ignorables (hb_ot_shape_context_t *c) |
449 | { |
450 | hb_buffer_t *buffer = c->buffer; |
451 | |
452 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || |
453 | (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) |
454 | return; |
455 | |
456 | unsigned int count = buffer->len; |
457 | hb_glyph_info_t *info = buffer->info; |
458 | hb_glyph_position_t *pos = buffer->pos; |
459 | unsigned int i = 0; |
460 | for (i = 0; i < count; i++) |
461 | if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) |
462 | pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0; |
463 | } |
464 | |
465 | static void |
466 | hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c) |
467 | { |
468 | hb_buffer_t *buffer = c->buffer; |
469 | |
470 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_DEFAULT_IGNORABLES) || |
471 | (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)) |
472 | return; |
473 | |
474 | unsigned int count = buffer->len; |
475 | hb_glyph_info_t *info = buffer->info; |
476 | hb_glyph_position_t *pos = buffer->pos; |
477 | unsigned int i = 0; |
478 | for (i = 0; i < count; i++) |
479 | { |
480 | if (unlikely (_hb_glyph_info_is_default_ignorable (&info[i]))) |
481 | break; |
482 | } |
483 | |
484 | /* No default-ignorables found; return. */ |
485 | if (i == count) |
486 | return; |
487 | |
488 | hb_codepoint_t space; |
489 | if (c->font->get_nominal_glyph (unicode: ' ', glyph: &space)) |
490 | { |
491 | /* Replace default-ignorables with a zero-advance space glyph. */ |
492 | for (/*continue*/; i < count; i++) |
493 | { |
494 | if (_hb_glyph_info_is_default_ignorable (info: &info[i])) |
495 | info[i].codepoint = space; |
496 | } |
497 | } |
498 | else |
499 | { |
500 | /* Merge clusters and delete default-ignorables. |
501 | * NOTE! We can't use out-buffer as we have positioning data. */ |
502 | unsigned int j = i; |
503 | for (; i < count; i++) |
504 | { |
505 | if (_hb_glyph_info_is_default_ignorable (info: &info[i])) |
506 | { |
507 | /* Merge clusters. |
508 | * Same logic as buffer->delete_glyph(), but for in-place removal. */ |
509 | |
510 | unsigned int cluster = info[i].cluster; |
511 | if (i + 1 < count && cluster == info[i + 1].cluster) |
512 | continue; /* Cluster survives; do nothing. */ |
513 | |
514 | if (j) |
515 | { |
516 | /* Merge cluster backward. */ |
517 | if (cluster < info[j - 1].cluster) |
518 | { |
519 | unsigned int mask = info[i].mask; |
520 | unsigned int old_cluster = info[j - 1].cluster; |
521 | for (unsigned k = j; k && info[k - 1].cluster == old_cluster; k--) |
522 | buffer->set_cluster (info&: info[k - 1], cluster, mask); |
523 | } |
524 | continue; |
525 | } |
526 | |
527 | if (i + 1 < count) |
528 | buffer->merge_clusters (start: i, end: i + 2); /* Merge cluster forward. */ |
529 | |
530 | continue; |
531 | } |
532 | |
533 | if (j != i) |
534 | { |
535 | info[j] = info[i]; |
536 | pos[j] = pos[i]; |
537 | } |
538 | j++; |
539 | } |
540 | buffer->len = j; |
541 | } |
542 | } |
543 | |
544 | |
545 | static inline void |
546 | hb_ot_map_glyphs_fast (hb_buffer_t *buffer) |
547 | { |
548 | /* Normalization process sets up glyph_index(), we just copy it. */ |
549 | unsigned int count = buffer->len; |
550 | hb_glyph_info_t *info = buffer->info; |
551 | for (unsigned int i = 0; i < count; i++) |
552 | info[i].codepoint = info[i].glyph_index(); |
553 | |
554 | buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS; |
555 | } |
556 | |
557 | static inline void |
558 | hb_synthesize_glyph_classes (hb_ot_shape_context_t *c) |
559 | { |
560 | unsigned int count = c->buffer->len; |
561 | hb_glyph_info_t *info = c->buffer->info; |
562 | for (unsigned int i = 0; i < count; i++) |
563 | { |
564 | hb_ot_layout_glyph_props_flags_t klass; |
565 | |
566 | /* Never mark default-ignorables as marks. |
567 | * They won't get in the way of lookups anyway, |
568 | * but having them as mark will cause them to be skipped |
569 | * over if the lookup-flag says so, but at least for the |
570 | * Mongolian variation selectors, looks like Uniscribe |
571 | * marks them as non-mark. Some Mongolian fonts without |
572 | * GDEF rely on this. Another notable character that |
573 | * this applies to is COMBINING GRAPHEME JOINER. */ |
574 | klass = (_hb_glyph_info_get_general_category (info: &info[i]) != |
575 | HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK || |
576 | _hb_glyph_info_is_default_ignorable (info: &info[i])) ? |
577 | HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : |
578 | HB_OT_LAYOUT_GLYPH_PROPS_MARK; |
579 | _hb_glyph_info_set_glyph_props (info: &info[i], props: klass); |
580 | } |
581 | } |
582 | |
583 | static inline void |
584 | hb_ot_substitute_default (hb_ot_shape_context_t *c) |
585 | { |
586 | hb_buffer_t *buffer = c->buffer; |
587 | |
588 | hb_ot_mirror_chars (c); |
589 | |
590 | HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index); |
591 | |
592 | _hb_ot_shape_normalize (shaper: c->plan, buffer, font: c->font); |
593 | |
594 | hb_ot_shape_setup_masks (c); |
595 | |
596 | /* This is unfortunate to go here, but necessary... */ |
597 | if (c->fallback_positioning) |
598 | _hb_ot_shape_fallback_position_recategorize_marks (plan: c->plan, font: c->font, buffer); |
599 | |
600 | hb_ot_map_glyphs_fast (buffer); |
601 | |
602 | HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_index); |
603 | } |
604 | |
605 | static inline void |
606 | hb_ot_substitute_complex (hb_ot_shape_context_t *c) |
607 | { |
608 | hb_buffer_t *buffer = c->buffer; |
609 | |
610 | hb_ot_layout_substitute_start (font: c->font, buffer); |
611 | |
612 | if (!hb_ot_layout_has_glyph_classes (face: c->face)) |
613 | hb_synthesize_glyph_classes (c); |
614 | |
615 | c->plan->substitute (font: c->font, buffer); |
616 | |
617 | return; |
618 | } |
619 | |
620 | static inline void |
621 | hb_ot_substitute (hb_ot_shape_context_t *c) |
622 | { |
623 | hb_ot_substitute_default (c); |
624 | |
625 | _hb_buffer_allocate_gsubgpos_vars (buffer: c->buffer); |
626 | |
627 | hb_ot_substitute_complex (c); |
628 | } |
629 | |
630 | /* Position */ |
631 | |
632 | static inline void |
633 | adjust_mark_offsets (hb_glyph_position_t *pos) |
634 | { |
635 | pos->x_offset -= pos->x_advance; |
636 | pos->y_offset -= pos->y_advance; |
637 | } |
638 | |
639 | static inline void |
640 | zero_mark_width (hb_glyph_position_t *pos) |
641 | { |
642 | pos->x_advance = 0; |
643 | pos->y_advance = 0; |
644 | } |
645 | |
646 | static inline void |
647 | zero_mark_widths_by_gdef (hb_buffer_t *buffer, bool adjust_offsets) |
648 | { |
649 | unsigned int count = buffer->len; |
650 | hb_glyph_info_t *info = buffer->info; |
651 | for (unsigned int i = 0; i < count; i++) |
652 | if (_hb_glyph_info_is_mark (info: &info[i])) |
653 | { |
654 | if (adjust_offsets) |
655 | adjust_mark_offsets (pos: &buffer->pos[i]); |
656 | zero_mark_width (pos: &buffer->pos[i]); |
657 | } |
658 | } |
659 | |
660 | static inline void |
661 | hb_ot_position_default (hb_ot_shape_context_t *c) |
662 | { |
663 | hb_direction_t direction = c->buffer->props.direction; |
664 | unsigned int count = c->buffer->len; |
665 | hb_glyph_info_t *info = c->buffer->info; |
666 | hb_glyph_position_t *pos = c->buffer->pos; |
667 | |
668 | if (HB_DIRECTION_IS_HORIZONTAL (direction)) |
669 | { |
670 | for (unsigned int i = 0; i < count; i++) |
671 | pos[i].x_advance = c->font->get_glyph_h_advance (glyph: info[i].codepoint); |
672 | /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ |
673 | if (c->font->has_glyph_h_origin_func ()) |
674 | for (unsigned int i = 0; i < count; i++) |
675 | c->font->subtract_glyph_h_origin (glyph: info[i].codepoint, |
676 | x: &pos[i].x_offset, |
677 | y: &pos[i].y_offset); |
678 | } |
679 | else |
680 | { |
681 | for (unsigned int i = 0; i < count; i++) |
682 | { |
683 | pos[i].y_advance = c->font->get_glyph_v_advance (glyph: info[i].codepoint); |
684 | c->font->subtract_glyph_v_origin (glyph: info[i].codepoint, |
685 | x: &pos[i].x_offset, |
686 | y: &pos[i].y_offset); |
687 | } |
688 | } |
689 | if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK) |
690 | _hb_ot_shape_fallback_spaces (plan: c->plan, font: c->font, buffer: c->buffer); |
691 | } |
692 | |
693 | static inline void |
694 | hb_ot_position_complex (hb_ot_shape_context_t *c) |
695 | { |
696 | unsigned int count = c->buffer->len; |
697 | hb_glyph_info_t *info = c->buffer->info; |
698 | hb_glyph_position_t *pos = c->buffer->pos; |
699 | |
700 | /* If the font has no GPOS, AND, no fallback positioning will |
701 | * happen, AND, direction is forward, then when zeroing mark |
702 | * widths, we shift the mark with it, such that the mark |
703 | * is positioned hanging over the previous glyph. When |
704 | * direction is backward we don't shift and it will end up |
705 | * hanging over the next glyph after the final reordering. |
706 | * If fallback positinoing happens or GPOS is present, we don't |
707 | * care. |
708 | */ |
709 | bool adjust_offsets_when_zeroing = c->fallback_positioning && |
710 | !c->plan->shaper->fallback_position && |
711 | HB_DIRECTION_IS_FORWARD (c->buffer->props.direction); |
712 | |
713 | /* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */ |
714 | |
715 | /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ |
716 | if (c->font->has_glyph_h_origin_func ()) |
717 | for (unsigned int i = 0; i < count; i++) |
718 | c->font->add_glyph_h_origin (glyph: info[i].codepoint, |
719 | x: &pos[i].x_offset, |
720 | y: &pos[i].y_offset); |
721 | |
722 | hb_ot_layout_position_start (font: c->font, buffer: c->buffer); |
723 | |
724 | switch (c->plan->shaper->zero_width_marks) |
725 | { |
726 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: |
727 | zero_mark_widths_by_gdef (buffer: c->buffer, adjust_offsets: adjust_offsets_when_zeroing); |
728 | break; |
729 | |
730 | default: |
731 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: |
732 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: |
733 | break; |
734 | } |
735 | |
736 | if (likely (!c->fallback_positioning)) |
737 | c->plan->position (font: c->font, buffer: c->buffer); |
738 | |
739 | switch (c->plan->shaper->zero_width_marks) |
740 | { |
741 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: |
742 | zero_mark_widths_by_gdef (buffer: c->buffer, adjust_offsets: adjust_offsets_when_zeroing); |
743 | break; |
744 | |
745 | default: |
746 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: |
747 | case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: |
748 | break; |
749 | } |
750 | |
751 | /* Finishing off GPOS has to follow a certain order. */ |
752 | hb_ot_layout_position_finish_advances (font: c->font, buffer: c->buffer); |
753 | hb_ot_zero_width_default_ignorables (c); |
754 | hb_ot_layout_position_finish_offsets (font: c->font, buffer: c->buffer); |
755 | |
756 | /* The nil glyph_h_origin() func returns 0, so no need to apply it. */ |
757 | if (c->font->has_glyph_h_origin_func ()) |
758 | for (unsigned int i = 0; i < count; i++) |
759 | c->font->subtract_glyph_h_origin (glyph: info[i].codepoint, |
760 | x: &pos[i].x_offset, |
761 | y: &pos[i].y_offset); |
762 | } |
763 | |
764 | static inline void |
765 | hb_ot_position (hb_ot_shape_context_t *c) |
766 | { |
767 | c->buffer->clear_positions (); |
768 | |
769 | hb_ot_position_default (c); |
770 | |
771 | hb_ot_position_complex (c); |
772 | |
773 | if (c->fallback_positioning && c->plan->shaper->fallback_position) |
774 | _hb_ot_shape_fallback_position (plan: c->plan, font: c->font, buffer: c->buffer); |
775 | |
776 | if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) |
777 | hb_buffer_reverse (buffer: c->buffer); |
778 | |
779 | /* Visual fallback goes here. */ |
780 | |
781 | if (c->fallback_positioning) |
782 | _hb_ot_shape_fallback_kern (plan: c->plan, font: c->font, buffer: c->buffer); |
783 | |
784 | _hb_buffer_deallocate_gsubgpos_vars (buffer: c->buffer); |
785 | } |
786 | |
787 | static inline void |
788 | hb_propagate_flags (hb_buffer_t *buffer) |
789 | { |
790 | /* Propagate cluster-level glyph flags to be the same on all cluster glyphs. |
791 | * Simplifies using them. */ |
792 | |
793 | if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_UNSAFE_TO_BREAK)) |
794 | return; |
795 | |
796 | hb_glyph_info_t *info = buffer->info; |
797 | |
798 | foreach_cluster (buffer, start, end) |
799 | { |
800 | unsigned int mask = 0; |
801 | for (unsigned int i = start; i < end; i++) |
802 | if (info[i].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) |
803 | { |
804 | mask = HB_GLYPH_FLAG_UNSAFE_TO_BREAK; |
805 | break; |
806 | } |
807 | if (mask) |
808 | for (unsigned int i = start; i < end; i++) |
809 | info[i].mask |= mask; |
810 | } |
811 | } |
812 | |
813 | /* Pull it all together! */ |
814 | |
815 | static void |
816 | hb_ot_shape_internal (hb_ot_shape_context_t *c) |
817 | { |
818 | c->buffer->deallocate_var_all (); |
819 | c->buffer->scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; |
820 | if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_LEN_FACTOR))) |
821 | { |
822 | c->buffer->max_len = MAX (a: c->buffer->len * HB_BUFFER_MAX_LEN_FACTOR, |
823 | b: (unsigned) HB_BUFFER_MAX_LEN_MIN); |
824 | } |
825 | if (likely (!_hb_unsigned_int_mul_overflows (c->buffer->len, HB_BUFFER_MAX_OPS_FACTOR))) |
826 | { |
827 | c->buffer->max_ops = MAX (a: c->buffer->len * HB_BUFFER_MAX_OPS_FACTOR, |
828 | b: (unsigned) HB_BUFFER_MAX_OPS_MIN); |
829 | } |
830 | |
831 | bool disable_otl = c->plan->shaper->disable_otl && c->plan->shaper->disable_otl (c->plan); |
832 | //c->fallback_substitute = disable_otl || !hb_ot_layout_has_substitution (c->face); |
833 | c->fallback_positioning = disable_otl || !hb_ot_layout_has_positioning (face: c->face); |
834 | c->fallback_glyph_classes = disable_otl || !hb_ot_layout_has_glyph_classes (face: c->face); |
835 | |
836 | /* Save the original direction, we use it later. */ |
837 | c->target_direction = c->buffer->props.direction; |
838 | |
839 | _hb_buffer_allocate_unicode_vars (buffer: c->buffer); |
840 | |
841 | c->buffer->clear_output (); |
842 | |
843 | hb_ot_shape_initialize_masks (c); |
844 | hb_set_unicode_props (buffer: c->buffer); |
845 | hb_insert_dotted_circle (buffer: c->buffer, font: c->font); |
846 | |
847 | hb_form_clusters (buffer: c->buffer); |
848 | |
849 | hb_ensure_native_direction (buffer: c->buffer); |
850 | |
851 | if (c->plan->shaper->preprocess_text) |
852 | c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font); |
853 | |
854 | hb_ot_substitute (c); |
855 | hb_ot_position (c); |
856 | |
857 | hb_ot_hide_default_ignorables (c); |
858 | |
859 | if (c->plan->shaper->postprocess_glyphs) |
860 | c->plan->shaper->postprocess_glyphs (c->plan, c->buffer, c->font); |
861 | |
862 | hb_propagate_flags (buffer: c->buffer); |
863 | |
864 | _hb_buffer_deallocate_unicode_vars (buffer: c->buffer); |
865 | |
866 | c->buffer->props.direction = c->target_direction; |
867 | |
868 | c->buffer->max_len = HB_BUFFER_MAX_LEN_DEFAULT; |
869 | c->buffer->max_ops = HB_BUFFER_MAX_OPS_DEFAULT; |
870 | c->buffer->deallocate_var_all (); |
871 | } |
872 | |
873 | |
874 | hb_bool_t |
875 | _hb_ot_shape (hb_shape_plan_t *shape_plan, |
876 | hb_font_t *font, |
877 | hb_buffer_t *buffer, |
878 | const hb_feature_t *features, |
879 | unsigned int num_features) |
880 | { |
881 | hb_ot_shape_context_t c = {HB_SHAPER_DATA_GET (shape_plan), .font: font, .face: font->face, .buffer: buffer, .user_features: features, .num_user_features: num_features}; |
882 | hb_ot_shape_internal (c: &c); |
883 | |
884 | return true; |
885 | } |
886 | |
887 | |
888 | /** |
889 | * hb_ot_shape_plan_collect_lookups: |
890 | * |
891 | * Since: 0.9.7 |
892 | **/ |
893 | void |
894 | hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan, |
895 | hb_tag_t table_tag, |
896 | hb_set_t *lookup_indexes /* OUT */) |
897 | { |
898 | /* XXX Does the first part always succeed? */ |
899 | HB_SHAPER_DATA_GET (shape_plan)->collect_lookups (table_tag, lookups: lookup_indexes); |
900 | } |
901 | |
902 | |
903 | /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ |
904 | static void |
905 | add_char (hb_font_t *font, |
906 | hb_unicode_funcs_t *unicode, |
907 | hb_bool_t mirror, |
908 | hb_codepoint_t u, |
909 | hb_set_t *glyphs) |
910 | { |
911 | hb_codepoint_t glyph; |
912 | if (font->get_nominal_glyph (unicode: u, glyph: &glyph)) |
913 | glyphs->add (g: glyph); |
914 | if (mirror) |
915 | { |
916 | hb_codepoint_t m = unicode->mirroring (unicode: u); |
917 | if (m != u && font->get_nominal_glyph (unicode: m, glyph: &glyph)) |
918 | glyphs->add (g: glyph); |
919 | } |
920 | } |
921 | |
922 | |
923 | /** |
924 | * hb_ot_shape_glyphs_closure: |
925 | * |
926 | * Since: 0.9.2 |
927 | **/ |
928 | void |
929 | hb_ot_shape_glyphs_closure (hb_font_t *font, |
930 | hb_buffer_t *buffer, |
931 | const hb_feature_t *features, |
932 | unsigned int num_features, |
933 | hb_set_t *glyphs) |
934 | { |
935 | hb_ot_shape_plan_t plan; |
936 | |
937 | const char *shapers[] = {"ot" , nullptr}; |
938 | hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (face: font->face, props: &buffer->props, |
939 | user_features: features, num_user_features: num_features, shaper_list: shapers); |
940 | |
941 | bool mirror = hb_script_get_horizontal_direction (script: buffer->props.script) == HB_DIRECTION_RTL; |
942 | |
943 | unsigned int count = buffer->len; |
944 | hb_glyph_info_t *info = buffer->info; |
945 | for (unsigned int i = 0; i < count; i++) |
946 | add_char (font, unicode: buffer->unicode, mirror, u: info[i].codepoint, glyphs); |
947 | |
948 | hb_set_t *lookups = hb_set_create (); |
949 | hb_ot_shape_plan_collect_lookups (shape_plan, HB_OT_TAG_GSUB, lookup_indexes: lookups); |
950 | |
951 | /* And find transitive closure. */ |
952 | hb_set_t *copy = hb_set_create (); |
953 | do { |
954 | copy->set (glyphs); |
955 | for (hb_codepoint_t lookup_index = -1; hb_set_next (set: lookups, codepoint: &lookup_index);) |
956 | hb_ot_layout_lookup_substitute_closure (face: font->face, lookup_index, glyphs); |
957 | } while (!copy->is_equal (other: glyphs)); |
958 | hb_set_destroy (set: copy); |
959 | |
960 | hb_set_destroy (set: lookups); |
961 | |
962 | hb_shape_plan_destroy (shape_plan); |
963 | } |
964 | |