OLD | NEW |
1 /* | 1 /* |
2 * Copyright © 2010,2012 Google, Inc. | 2 * Copyright © 2010,2012 Google, Inc. |
3 * | 3 * |
4 * This is part of HarfBuzz, a text shaping library. | 4 * This is part of HarfBuzz, a text shaping library. |
5 * | 5 * |
6 * Permission is hereby granted, without written agreement and without | 6 * Permission is hereby granted, without written agreement and without |
7 * license or royalty fees, to use, copy, modify, and distribute this | 7 * license or royalty fees, to use, copy, modify, and distribute this |
8 * software and its documentation for any purpose, provided that the | 8 * software and its documentation for any purpose, provided that the |
9 * above copyright notice and the following two paragraphs appear in | 9 * above copyright notice and the following two paragraphs appear in |
10 * all copies of this software. | 10 * all copies of this software. |
(...skipping 10 matching lines...) Expand all Loading... |
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 * | 23 * |
24 * Google Author(s): Behdad Esfahbod | 24 * Google Author(s): Behdad Esfahbod |
25 */ | 25 */ |
26 | 26 |
27 #include "hb-ot-shape-complex-arabic-private.hh" | 27 #include "hb-ot-shape-complex-arabic-private.hh" |
28 #include "hb-ot-shape-private.hh" | 28 #include "hb-ot-shape-private.hh" |
29 | 29 |
30 | 30 |
| 31 #ifndef HB_DEBUG_ARABIC |
| 32 #define HB_DEBUG_ARABIC (HB_DEBUG+0) |
| 33 #endif |
| 34 |
| 35 |
31 /* buffer var allocations */ | 36 /* buffer var allocations */ |
32 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ | 37 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ |
33 | 38 |
| 39 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0 |
| 40 |
| 41 /* See: |
| 42 * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff5
7823d#commitcomment-14248516 */ |
| 43 #define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ |
| 44 (FLAG_SAFE (gen_cat) & \ |
| 45 (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ |
| 46 FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ |
| 47 /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \ |
| 48 FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ |
| 49 FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ |
| 50 /*FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |*/ \ |
| 51 /*FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |*/ \ |
| 52 FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \ |
| 53 FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \ |
| 54 FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | \ |
| 55 FLAG (HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) | \ |
| 56 FLAG (HB_UNICODE_GENERAL_CATEGORY_LETTER_NUMBER) | \ |
| 57 FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_NUMBER) | \ |
| 58 FLAG (HB_UNICODE_GENERAL_CATEGORY_CURRENCY_SYMBOL) | \ |
| 59 FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_SYMBOL) | \ |
| 60 FLAG (HB_UNICODE_GENERAL_CATEGORY_MATH_SYMBOL) | \ |
| 61 FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_SYMBOL))) |
| 62 |
34 | 63 |
35 /* | 64 /* |
36 * Joining types: | 65 * Joining types: |
37 */ | 66 */ |
38 | 67 |
39 /* | 68 /* |
40 * Bits used in the joining tables | 69 * Bits used in the joining tables |
41 */ | 70 */ |
42 enum hb_arabic_joining_type_t { | 71 enum hb_arabic_joining_type_t { |
43 JOINING_TYPE_U = 0, | 72 JOINING_TYPE_U = 0, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
77 HB_TAG('f','i','n','2'), | 106 HB_TAG('f','i','n','2'), |
78 HB_TAG('f','i','n','3'), | 107 HB_TAG('f','i','n','3'), |
79 HB_TAG('m','e','d','i'), | 108 HB_TAG('m','e','d','i'), |
80 HB_TAG('m','e','d','2'), | 109 HB_TAG('m','e','d','2'), |
81 HB_TAG('i','n','i','t'), | 110 HB_TAG('i','n','i','t'), |
82 HB_TAG_NONE | 111 HB_TAG_NONE |
83 }; | 112 }; |
84 | 113 |
85 | 114 |
86 /* Same order as the feature array */ | 115 /* Same order as the feature array */ |
87 enum { | 116 enum arabic_action_t { |
88 ISOL, | 117 ISOL, |
89 FINA, | 118 FINA, |
90 FIN2, | 119 FIN2, |
91 FIN3, | 120 FIN3, |
92 MEDI, | 121 MEDI, |
93 MED2, | 122 MED2, |
94 INIT, | 123 INIT, |
95 | 124 |
96 NONE, | 125 NONE, |
97 | 126 |
98 ARABIC_NUM_FEATURES = NONE | 127 ARABIC_NUM_FEATURES = NONE, |
| 128 |
| 129 /* We abuse the same byte for other things... */ |
| 130 STCH_FIXED, |
| 131 STCH_REPEATING, |
99 }; | 132 }; |
100 | 133 |
101 static const struct arabic_state_table_entry { | 134 static const struct arabic_state_table_entry { |
102 uint8_t prev_action; | 135 uint8_t prev_action; |
103 uint8_t curr_action; | 136 uint8_t curr_action; |
104 uint16_t next_state; | 137 uint16_t next_state; |
105 } arabic_state_table[][NUM_STATE_MACHINE_COLS] = | 138 } arabic_state_table[][NUM_STATE_MACHINE_COLS] = |
106 { | 139 { |
107 /* jt_U, jt_L, jt_R, jt_D, jg_ALAPH,
jg_DALATH_RISH */ | 140 /* jt_U, jt_L, jt_R, jt_D, jg_ALAPH,
jg_DALATH_RISH */ |
108 | 141 |
(...skipping 24 matching lines...) Expand all Loading... |
133 nuke_joiners (const hb_ot_shape_plan_t *plan, | 166 nuke_joiners (const hb_ot_shape_plan_t *plan, |
134 hb_font_t *font, | 167 hb_font_t *font, |
135 hb_buffer_t *buffer); | 168 hb_buffer_t *buffer); |
136 | 169 |
137 static void | 170 static void |
138 arabic_fallback_shape (const hb_ot_shape_plan_t *plan, | 171 arabic_fallback_shape (const hb_ot_shape_plan_t *plan, |
139 hb_font_t *font, | 172 hb_font_t *font, |
140 hb_buffer_t *buffer); | 173 hb_buffer_t *buffer); |
141 | 174 |
142 static void | 175 static void |
| 176 record_stch (const hb_ot_shape_plan_t *plan, |
| 177 hb_font_t *font, |
| 178 hb_buffer_t *buffer); |
| 179 |
| 180 static void |
143 collect_features_arabic (hb_ot_shape_planner_t *plan) | 181 collect_features_arabic (hb_ot_shape_planner_t *plan) |
144 { | 182 { |
145 hb_ot_map_builder_t *map = &plan->map; | 183 hb_ot_map_builder_t *map = &plan->map; |
146 | 184 |
147 /* We apply features according to the Arabic spec, with pauses | 185 /* We apply features according to the Arabic spec, with pauses |
148 * in between most. | 186 * in between most. |
149 * | 187 * |
150 * The pause between init/medi/... and rlig is required. See eg: | 188 * The pause between init/medi/... and rlig is required. See eg: |
151 * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 | 189 * https://bugzilla.mozilla.org/show_bug.cgi?id=644184 |
152 * | 190 * |
153 * The pauses between init/medi/... themselves are not necessarily | 191 * The pauses between init/medi/... themselves are not necessarily |
154 * needed as only one of those features is applied to any character. | 192 * needed as only one of those features is applied to any character. |
155 * The only difference it makes is when fonts have contextual | 193 * The only difference it makes is when fonts have contextual |
156 * substitutions. We now follow the order of the spec, which makes | 194 * substitutions. We now follow the order of the spec, which makes |
157 * for better experience if that's what Uniscribe is doing. | 195 * for better experience if that's what Uniscribe is doing. |
158 * | 196 * |
159 * At least for Arabic, looks like Uniscribe has a pause between | 197 * At least for Arabic, looks like Uniscribe has a pause between |
160 * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't | 198 * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't |
161 * work. However, testing shows that rlig and calt are applied | 199 * work. However, testing shows that rlig and calt are applied |
162 * together for Mongolian in Uniscribe. As such, we only add a | 200 * together for Mongolian in Uniscribe. As such, we only add a |
163 * pause for Arabic, not other scripts. | 201 * pause for Arabic, not other scripts. |
164 */ | 202 */ |
165 | 203 |
166 map->add_gsub_pause (nuke_joiners); | 204 map->add_gsub_pause (nuke_joiners); |
167 | 205 |
| 206 map->add_global_bool_feature (HB_TAG('s','t','c','h')); |
| 207 map->add_gsub_pause (record_stch); |
| 208 |
168 map->add_global_bool_feature (HB_TAG('c','c','m','p')); | 209 map->add_global_bool_feature (HB_TAG('c','c','m','p')); |
169 map->add_global_bool_feature (HB_TAG('l','o','c','l')); | 210 map->add_global_bool_feature (HB_TAG('l','o','c','l')); |
170 | 211 |
171 map->add_gsub_pause (NULL); | 212 map->add_gsub_pause (NULL); |
172 | 213 |
173 for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) | 214 for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) |
174 { | 215 { |
175 bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SY
RIAC (arabic_features[i]); | 216 bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SY
RIAC (arabic_features[i]); |
176 map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_N
ONE); | 217 map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_N
ONE); |
177 map->add_gsub_pause (NULL); | 218 map->add_gsub_pause (NULL); |
(...skipping 23 matching lines...) Expand all Loading... |
201 struct arabic_shape_plan_t | 242 struct arabic_shape_plan_t |
202 { | 243 { |
203 ASSERT_POD (); | 244 ASSERT_POD (); |
204 | 245 |
205 /* The "+ 1" in the next array is to accommodate for the "NONE" command, | 246 /* The "+ 1" in the next array is to accommodate for the "NONE" command, |
206 * which is not an OpenType feature, but this simplifies the code by not | 247 * which is not an OpenType feature, but this simplifies the code by not |
207 * having to do a "if (... < NONE) ..." and just rely on the fact that | 248 * having to do a "if (... < NONE) ..." and just rely on the fact that |
208 * mask_array[NONE] == 0. */ | 249 * mask_array[NONE] == 0. */ |
209 hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; | 250 hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1]; |
210 | 251 |
211 bool do_fallback; | |
212 arabic_fallback_plan_t *fallback_plan; | 252 arabic_fallback_plan_t *fallback_plan; |
| 253 |
| 254 unsigned int do_fallback : 1; |
| 255 unsigned int has_stch : 1; |
213 }; | 256 }; |
214 | 257 |
215 void * | 258 void * |
216 data_create_arabic (const hb_ot_shape_plan_t *plan) | 259 data_create_arabic (const hb_ot_shape_plan_t *plan) |
217 { | 260 { |
218 arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (
arabic_shape_plan_t)); | 261 arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (
arabic_shape_plan_t)); |
219 if (unlikely (!arabic_plan)) | 262 if (unlikely (!arabic_plan)) |
220 return NULL; | 263 return NULL; |
221 | 264 |
222 arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; | 265 arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; |
| 266 arabic_plan->has_stch = !!plan->map.get_1_mask (HB_TAG ('s','t','c','h')); |
223 for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { | 267 for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { |
224 arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); | 268 arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); |
225 arabic_plan->do_fallback = arabic_plan->do_fallback && | 269 arabic_plan->do_fallback = arabic_plan->do_fallback && |
226 (FEATURE_IS_SYRIAC (arabic_features[i]) || | 270 (FEATURE_IS_SYRIAC (arabic_features[i]) || |
227 plan->map.needs_fallback (arabic_features[i])); | 271 plan->map.needs_fallback (arabic_features[i])); |
228 } | 272 } |
229 | 273 |
230 return arabic_plan; | 274 return arabic_plan; |
231 } | 275 } |
232 | 276 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
313 HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); | 357 HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); |
314 | 358 |
315 arabic_joining (buffer); | 359 arabic_joining (buffer); |
316 if (script == HB_SCRIPT_MONGOLIAN) | 360 if (script == HB_SCRIPT_MONGOLIAN) |
317 mongolian_variation_selectors (buffer); | 361 mongolian_variation_selectors (buffer); |
318 | 362 |
319 unsigned int count = buffer->len; | 363 unsigned int count = buffer->len; |
320 hb_glyph_info_t *info = buffer->info; | 364 hb_glyph_info_t *info = buffer->info; |
321 for (unsigned int i = 0; i < count; i++) | 365 for (unsigned int i = 0; i < count; i++) |
322 info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; | 366 info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; |
323 | |
324 HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); | |
325 } | 367 } |
326 | 368 |
327 static void | 369 static void |
328 setup_masks_arabic (const hb_ot_shape_plan_t *plan, | 370 setup_masks_arabic (const hb_ot_shape_plan_t *plan, |
329 hb_buffer_t *buffer, | 371 hb_buffer_t *buffer, |
330 hb_font_t *font HB_UNUSED) | 372 hb_font_t *font HB_UNUSED) |
331 { | 373 { |
332 const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->d
ata; | 374 const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->d
ata; |
333 setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); | 375 setup_masks_arabic_plan (arabic_plan, buffer, plan->props.script); |
334 } | 376 } |
(...skipping 29 matching lines...) Expand all Loading... |
364 fallback_plan = arabic_fallback_plan_create (plan, font); | 406 fallback_plan = arabic_fallback_plan_create (plan, font); |
365 if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (a
rabic_plan))->fallback_plan, NULL, fallback_plan))) { | 407 if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (a
rabic_plan))->fallback_plan, NULL, fallback_plan))) { |
366 arabic_fallback_plan_destroy (fallback_plan); | 408 arabic_fallback_plan_destroy (fallback_plan); |
367 goto retry; | 409 goto retry; |
368 } | 410 } |
369 } | 411 } |
370 | 412 |
371 arabic_fallback_plan_shape (fallback_plan, font, buffer); | 413 arabic_fallback_plan_shape (fallback_plan, font, buffer); |
372 } | 414 } |
373 | 415 |
| 416 /* |
| 417 * Stretch feature: "stch". |
| 418 * See example here: |
| 419 * https://www.microsoft.com/typography/OpenTypeDev/syriac/intro.htm |
| 420 * We implement this in a generic way, such that the Arabic subtending |
| 421 * marks can use it as well. |
| 422 */ |
| 423 |
| 424 static void |
| 425 record_stch (const hb_ot_shape_plan_t *plan, |
| 426 hb_font_t *font, |
| 427 hb_buffer_t *buffer) |
| 428 { |
| 429 const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->d
ata; |
| 430 if (!arabic_plan->has_stch) |
| 431 return; |
| 432 |
| 433 /* 'stch' feature was just applied. Look for anything that multiplied, |
| 434 * and record it for stch treatment later. Note that rtlm, frac, etc |
| 435 * are applied before stch, but we assume that they didn't result in |
| 436 * anything multiplying into 5 pieces, so it's safe-ish... */ |
| 437 |
| 438 unsigned int count = buffer->len; |
| 439 hb_glyph_info_t *info = buffer->info; |
| 440 for (unsigned int i = 0; i < count; i++) |
| 441 if (unlikely (_hb_glyph_info_multiplied (&info[i]))) |
| 442 { |
| 443 unsigned int comp = _hb_glyph_info_get_lig_comp (&info[i]); |
| 444 info[i].arabic_shaping_action() = comp % 2 ? STCH_REPEATING : STCH_FIXED; |
| 445 buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH; |
| 446 } |
| 447 } |
| 448 |
| 449 static void |
| 450 apply_stch (const hb_ot_shape_plan_t *plan, |
| 451 hb_buffer_t *buffer, |
| 452 hb_font_t *font) |
| 453 { |
| 454 if (likely (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH))
) |
| 455 return; |
| 456 |
| 457 /* The Arabic shaper currently always processes in RTL mode, so we should |
| 458 * stretch / position the stretched pieces to the left / preceding glyphs. */ |
| 459 |
| 460 /* We do a two pass implementation: |
| 461 * First pass calculates the exact number of extra glyphs we need, |
| 462 * We then enlarge buffer to have that much room, |
| 463 * Second pass applies the stretch, copying things to the end of buffer. |
| 464 */ |
| 465 |
| 466 /* 30 = 2048 / 70. |
| 467 * https://www.microsoft.com/typography/cursivescriptguidelines.mspx */ |
| 468 hb_position_t overlap = font->x_scale / 30; |
| 469 DEBUG_MSG (ARABIC, NULL, "overlap for stretching is %d", overlap); |
| 470 int sign = font->x_scale < 0 ? -1 : +1; |
| 471 unsigned int extra_glyphs_needed = 0; // Set during MEASURE, used during CUT |
| 472 typedef enum { MEASURE, CUT } step_t; |
| 473 |
| 474 for (step_t step = MEASURE; step <= CUT; step = (step_t) (step + 1)) |
| 475 { |
| 476 unsigned int count = buffer->len; |
| 477 hb_glyph_info_t *info = buffer->info; |
| 478 hb_glyph_position_t *pos = buffer->pos; |
| 479 unsigned int new_len = count + extra_glyphs_needed; // write head during CUT |
| 480 unsigned int j = new_len; |
| 481 for (unsigned int i = count; i; i--) |
| 482 { |
| 483 if (!hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FIXE
D, STCH_REPEATING)) |
| 484 { |
| 485 if (step == CUT) |
| 486 { |
| 487 --j; |
| 488 info[j] = info[i - 1]; |
| 489 pos[j] = pos[i - 1]; |
| 490 } |
| 491 continue; |
| 492 } |
| 493 |
| 494 /* Yay, justification! */ |
| 495 |
| 496 hb_position_t w_total = 0; // Total to be filled |
| 497 hb_position_t w_fixed = 0; // Sum of fixed tiles |
| 498 hb_position_t w_repeating = 0; // Sum of repeating tiles |
| 499 int n_fixed = 0; |
| 500 int n_repeating = 0; |
| 501 |
| 502 unsigned int end = i; |
| 503 while (i && |
| 504 hb_in_range<unsigned> (info[i - 1].arabic_shaping_action(), STCH_FI
XED, STCH_REPEATING)) |
| 505 { |
| 506 i--; |
| 507 hb_glyph_extents_t extents; |
| 508 if (!font->get_glyph_extents (info[i].codepoint, &extents)) |
| 509 extents.width = 0; |
| 510 extents.width -= overlap; |
| 511 if (info[i].arabic_shaping_action() == STCH_FIXED) |
| 512 { |
| 513 w_fixed += extents.width; |
| 514 n_fixed++; |
| 515 } |
| 516 else |
| 517 { |
| 518 w_repeating += extents.width; |
| 519 n_repeating++; |
| 520 } |
| 521 } |
| 522 unsigned int start = i; |
| 523 unsigned int context = i; |
| 524 while (context && |
| 525 !hb_in_range<unsigned> (info[context - 1].arabic_shaping_action(),
STCH_FIXED, STCH_REPEATING) && |
| 526 (_hb_glyph_info_is_default_ignorable (&info[context - 1]) || |
| 527 HB_ARABIC_GENERAL_CATEGORY_IS_WORD (_hb_glyph_info_get_general_cat
egory (&info[context - 1])))) |
| 528 { |
| 529 context--; |
| 530 w_total += pos[context].x_advance; |
| 531 } |
| 532 i++; // Don't touch i again. |
| 533 |
| 534 DEBUG_MSG (ARABIC, NULL, "%s stretch at (%d,%d,%d)", |
| 535 step == MEASURE ? "measuring" : "cutting", context, start, end)
; |
| 536 DEBUG_MSG (ARABIC, NULL, "rest of word: count=%d width %d", start - con
text, w_total); |
| 537 DEBUG_MSG (ARABIC, NULL, "fixed tiles: count=%d width=%d", n_fixed, w_
fixed); |
| 538 DEBUG_MSG (ARABIC, NULL, "repeating tiles: count=%d width=%d", n_repeating
, w_repeating); |
| 539 |
| 540 /* Number of additional times to repeat each repeating tile. */ |
| 541 int n_copies = 0; |
| 542 |
| 543 hb_position_t w_remaining = w_total - w_fixed - overlap; |
| 544 if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) |
| 545 n_copies = (sign * w_remaining + sign * w_repeating / 4) / (sign * w_rep
eating) - 1; |
| 546 |
| 547 if (step == MEASURE) |
| 548 { |
| 549 extra_glyphs_needed += n_copies * n_repeating; |
| 550 DEBUG_MSG (ARABIC, NULL, "will add extra %d copies of repeating tiles",
n_copies); |
| 551 } |
| 552 else |
| 553 { |
| 554 hb_position_t x_offset = -overlap; |
| 555 for (unsigned int k = end; k > start; k--) |
| 556 { |
| 557 hb_glyph_extents_t extents; |
| 558 if (!font->get_glyph_extents (info[k - 1].codepoint, &extents)) |
| 559 extents.width = 0; |
| 560 extents.width -= overlap; |
| 561 |
| 562 unsigned int repeat = 1; |
| 563 if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) |
| 564 repeat += n_copies; |
| 565 |
| 566 DEBUG_MSG (ARABIC, NULL, "appending %d copies of glyph %d; j=%d", |
| 567 repeat, info[k - 1].codepoint, j); |
| 568 for (unsigned int n = 0; n < repeat; n++) |
| 569 { |
| 570 x_offset -= extents.width; |
| 571 pos[k - 1].x_offset = x_offset; |
| 572 /* Append copy. */ |
| 573 --j; |
| 574 info[j] = info[k - 1]; |
| 575 pos[j] = pos[k - 1]; |
| 576 } |
| 577 } |
| 578 } |
| 579 } |
| 580 |
| 581 if (step == MEASURE) |
| 582 { |
| 583 if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) |
| 584 break; |
| 585 } |
| 586 else |
| 587 { |
| 588 assert (j == 0); |
| 589 buffer->len = new_len; |
| 590 } |
| 591 } |
| 592 } |
| 593 |
| 594 |
| 595 static void |
| 596 postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan, |
| 597 hb_buffer_t *buffer, |
| 598 hb_font_t *font) |
| 599 { |
| 600 apply_stch (plan, buffer, font); |
| 601 |
| 602 HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |
| 603 } |
374 | 604 |
375 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = | 605 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = |
376 { | 606 { |
377 "arabic", | 607 "arabic", |
378 collect_features_arabic, | 608 collect_features_arabic, |
379 NULL, /* override_features */ | 609 NULL, /* override_features */ |
380 data_create_arabic, | 610 data_create_arabic, |
381 data_destroy_arabic, | 611 data_destroy_arabic, |
382 NULL, /* preprocess_text_arabic */ | 612 NULL, /* preprocess_text */ |
| 613 postprocess_glyphs_arabic, |
383 HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, | 614 HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, |
384 NULL, /* decompose */ | 615 NULL, /* decompose */ |
385 NULL, /* compose */ | 616 NULL, /* compose */ |
386 setup_masks_arabic, | 617 setup_masks_arabic, |
387 HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, | 618 HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, |
388 true, /* fallback_position */ | 619 true, /* fallback_position */ |
389 }; | 620 }; |
OLD | NEW |