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