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-private.hh" |
|
28 #include "hb-debug.hh" |
27 #include "hb-ot-shape-complex-arabic-private.hh" |
29 #include "hb-ot-shape-complex-arabic-private.hh" |
28 #include "hb-ot-shape-private.hh" |
30 #include "hb-ot-shape-private.hh" |
29 |
|
30 |
|
31 #ifndef HB_DEBUG_ARABIC |
|
32 #define HB_DEBUG_ARABIC (HB_DEBUG+0) |
|
33 #endif |
|
34 |
31 |
35 |
32 |
36 /* buffer var allocations */ |
33 /* buffer var allocations */ |
37 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ |
34 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */ |
38 |
35 |
39 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0 |
36 #define HB_BUFFER_SCRATCH_FLAG_ARABIC_HAS_STCH HB_BUFFER_SCRATCH_FLAG_COMPLEX0 |
40 |
37 |
41 /* See: |
38 /* See: |
42 * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */ |
39 * https://github.com/behdad/harfbuzz/commit/6e6f82b6f3dde0fc6c3c7d991d9ec6cfff57823d#commitcomment-14248516 */ |
43 #define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ |
40 #define HB_ARABIC_GENERAL_CATEGORY_IS_WORD(gen_cat) \ |
44 (FLAG_SAFE (gen_cat) & \ |
41 (FLAG_UNSAFE (gen_cat) & \ |
45 (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ |
42 (FLAG (HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED) | \ |
46 FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ |
43 FLAG (HB_UNICODE_GENERAL_CATEGORY_PRIVATE_USE) | \ |
47 /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \ |
44 /*FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |*/ \ |
48 FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ |
45 FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) | \ |
49 FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ |
46 FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | \ |
197 * At least for Arabic, looks like Uniscribe has a pause between |
194 * At least for Arabic, looks like Uniscribe has a pause between |
198 * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't |
195 * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't |
199 * work. However, testing shows that rlig and calt are applied |
196 * work. However, testing shows that rlig and calt are applied |
200 * together for Mongolian in Uniscribe. As such, we only add a |
197 * together for Mongolian in Uniscribe. As such, we only add a |
201 * pause for Arabic, not other scripts. |
198 * pause for Arabic, not other scripts. |
|
199 * |
|
200 * A pause after calt is required to make KFGQPC Uthmanic Script HAFS |
|
201 * work correctly. See https://github.com/behdad/harfbuzz/issues/505 |
202 */ |
202 */ |
203 |
203 |
204 map->add_gsub_pause (nuke_joiners); |
204 map->add_gsub_pause (nuke_joiners); |
205 |
205 |
206 map->add_global_bool_feature (HB_TAG('s','t','c','h')); |
206 map->add_global_bool_feature (HB_TAG('s','t','c','h')); |
207 map->add_gsub_pause (record_stch); |
207 map->add_gsub_pause (record_stch); |
208 |
208 |
209 map->add_global_bool_feature (HB_TAG('c','c','m','p')); |
209 map->add_global_bool_feature (HB_TAG('c','c','m','p')); |
210 map->add_global_bool_feature (HB_TAG('l','o','c','l')); |
210 map->add_global_bool_feature (HB_TAG('l','o','c','l')); |
211 |
211 |
212 map->add_gsub_pause (NULL); |
212 map->add_gsub_pause (nullptr); |
213 |
213 |
214 for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) |
214 for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) |
215 { |
215 { |
216 bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]); |
216 bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]); |
217 map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_NONE); |
217 map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_NONE); |
218 map->add_gsub_pause (NULL); |
218 map->add_gsub_pause (nullptr); |
219 } |
219 } |
220 |
220 |
221 map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK); |
221 map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK); |
222 if (plan->props.script == HB_SCRIPT_ARABIC) |
222 if (plan->props.script == HB_SCRIPT_ARABIC) |
223 map->add_gsub_pause (arabic_fallback_shape); |
223 map->add_gsub_pause (arabic_fallback_shape); |
224 |
224 |
|
225 /* No pause after rclt. See 98460779bae19e4d64d29461ff154b3527bf8420. */ |
|
226 map->add_global_bool_feature (HB_TAG('r','c','l','t')); |
225 map->add_global_bool_feature (HB_TAG('c','a','l','t')); |
227 map->add_global_bool_feature (HB_TAG('c','a','l','t')); |
|
228 map->add_gsub_pause (nullptr); |
226 |
229 |
227 /* The spec includes 'cswh'. Earlier versions of Windows |
230 /* The spec includes 'cswh'. Earlier versions of Windows |
228 * used to enable this by default, but testing suggests |
231 * used to enable this by default, but testing suggests |
229 * that Windows 8 and later do not enable it by default, |
232 * that Windows 8 and later do not enable it by default, |
230 * and spec now says 'Off by default'. |
233 * and spec now says 'Off by default'. |
231 * We disabled this in ae23c24c32. |
234 * We disabled this in ae23c24c32. |
232 * Note that IranNastaliq uses this feature extensively |
235 * Note that IranNastaliq uses this feature extensively |
233 * to fixup broken glyph sequences. Oh well... |
236 * to fixup broken glyph sequences. Oh well... |
234 * Test case: U+0643,U+0640,U+0631. */ |
237 * Test case: U+0643,U+0640,U+0631. */ |
235 //map->add_gsub_pause (NULL); |
|
236 //map->add_global_bool_feature (HB_TAG('c','s','w','h')); |
238 //map->add_global_bool_feature (HB_TAG('c','s','w','h')); |
237 map->add_global_bool_feature (HB_TAG('m','s','e','t')); |
239 map->add_global_bool_feature (HB_TAG('m','s','e','t')); |
238 } |
240 } |
239 |
241 |
240 #include "hb-ot-shape-complex-arabic-fallback.hh" |
242 #include "hb-ot-shape-complex-arabic-fallback.hh" |
522 context--; |
527 context--; |
523 w_total += pos[context].x_advance; |
528 w_total += pos[context].x_advance; |
524 } |
529 } |
525 i++; // Don't touch i again. |
530 i++; // Don't touch i again. |
526 |
531 |
527 DEBUG_MSG (ARABIC, NULL, "%s stretch at (%d,%d,%d)", |
532 DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%d,%d,%d)", |
528 step == MEASURE ? "measuring" : "cutting", context, start, end); |
533 step == MEASURE ? "measuring" : "cutting", context, start, end); |
529 DEBUG_MSG (ARABIC, NULL, "rest of word: count=%d width %d", start - context, w_total); |
534 DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%d width %d", start - context, w_total); |
530 DEBUG_MSG (ARABIC, NULL, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); |
535 DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); |
531 DEBUG_MSG (ARABIC, NULL, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); |
536 DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); |
532 |
537 |
533 /* Number of additional times to repeat each repeating tile. */ |
538 /* Number of additional times to repeat each repeating tile. */ |
534 int n_copies = 0; |
539 int n_copies = 0; |
535 |
540 |
536 hb_position_t w_remaining = w_total - w_fixed; |
541 hb_position_t w_remaining = w_total - w_fixed; |
538 n_copies = (sign * w_remaining) / (sign * w_repeating) - 1; |
543 n_copies = (sign * w_remaining) / (sign * w_repeating) - 1; |
539 |
544 |
540 /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */ |
545 /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */ |
541 hb_position_t extra_repeat_overlap = 0; |
546 hb_position_t extra_repeat_overlap = 0; |
542 hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1); |
547 hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1); |
543 if (shortfall > 0) |
548 if (shortfall > 0 && n_repeating > 0) |
544 { |
549 { |
545 ++n_copies; |
550 ++n_copies; |
546 hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining; |
551 hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining; |
547 if (excess > 0) |
552 if (excess > 0) |
548 extra_repeat_overlap = excess / (n_copies * n_repeating); |
553 extra_repeat_overlap = excess / (n_copies * n_repeating); |
549 } |
554 } |
550 |
555 |
551 if (step == MEASURE) |
556 if (step == MEASURE) |
552 { |
557 { |
553 extra_glyphs_needed += n_copies * n_repeating; |
558 extra_glyphs_needed += n_copies * n_repeating; |
554 DEBUG_MSG (ARABIC, NULL, "will add extra %d copies of repeating tiles", n_copies); |
559 DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies); |
555 } |
560 } |
556 else |
561 else |
557 { |
562 { |
|
563 buffer->unsafe_to_break (context, end); |
558 hb_position_t x_offset = 0; |
564 hb_position_t x_offset = 0; |
559 for (unsigned int k = end; k > start; k--) |
565 for (unsigned int k = end; k > start; k--) |
560 { |
566 { |
561 hb_position_t width = font->get_glyph_h_advance (info[k - 1].codepoint); |
567 hb_position_t width = font->get_glyph_h_advance (info[k - 1].codepoint); |
562 |
568 |
563 unsigned int repeat = 1; |
569 unsigned int repeat = 1; |
564 if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) |
570 if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) |
565 repeat += n_copies; |
571 repeat += n_copies; |
566 |
572 |
567 DEBUG_MSG (ARABIC, NULL, "appending %d copies of glyph %d; j=%d", |
573 DEBUG_MSG (ARABIC, nullptr, "appending %d copies of glyph %d; j=%d", |
568 repeat, info[k - 1].codepoint, j); |
574 repeat, info[k - 1].codepoint, j); |
569 for (unsigned int n = 0; n < repeat; n++) |
575 for (unsigned int n = 0; n < repeat; n++) |
570 { |
576 { |
571 x_offset -= width; |
577 x_offset -= width; |
572 if (n > 0) |
578 if (n > 0) |
603 apply_stch (plan, buffer, font); |
609 apply_stch (plan, buffer, font); |
604 |
610 |
605 HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |
611 HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |
606 } |
612 } |
607 |
613 |
|
614 /* http://www.unicode.org/reports/tr53/tr53-1.pdf */ |
|
615 |
|
616 static hb_codepoint_t |
|
617 modifier_combining_marks[] = |
|
618 { |
|
619 0x0654u, /* ARABIC HAMZA ABOVE */ |
|
620 0x0655u, /* ARABIC HAMZA BELOW */ |
|
621 0x0658u, /* ARABIC MARK NOON GHUNNA */ |
|
622 0x06DCu, /* ARABIC SMALL HIGH SEEN */ |
|
623 0x06E3u, /* ARABIC SMALL LOW SEEN */ |
|
624 0x06E7u, /* ARABIC SMALL HIGH YEH */ |
|
625 0x06E8u, /* ARABIC SMALL HIGH NOON */ |
|
626 0x08F3u, /* ARABIC SMALL HIGH WAW */ |
|
627 }; |
|
628 |
|
629 static inline bool |
|
630 info_is_mcm (const hb_glyph_info_t &info) |
|
631 { |
|
632 hb_codepoint_t u = info.codepoint; |
|
633 for (unsigned int i = 0; i < ARRAY_LENGTH (modifier_combining_marks); i++) |
|
634 if (u == modifier_combining_marks[i]) |
|
635 return true; |
|
636 return false; |
|
637 } |
|
638 |
|
639 static void |
|
640 reorder_marks_arabic (const hb_ot_shape_plan_t *plan, |
|
641 hb_buffer_t *buffer, |
|
642 unsigned int start, |
|
643 unsigned int end) |
|
644 { |
|
645 hb_glyph_info_t *info = buffer->info; |
|
646 |
|
647 unsigned int i = start; |
|
648 for (unsigned int cc = 220; cc <= 230; cc += 10) |
|
649 { |
|
650 DEBUG_MSG (ARABIC, buffer, "Looking for %d's starting at %d\n", cc, i); |
|
651 while (i < end && info_cc(info[i]) < cc) |
|
652 i++; |
|
653 DEBUG_MSG (ARABIC, buffer, "Looking for %d's stopped at %d\n", cc, i); |
|
654 |
|
655 if (i == end) |
|
656 break; |
|
657 |
|
658 if (info_cc(info[i]) > cc) |
|
659 continue; |
|
660 |
|
661 /* Technically we should also check "info_cc(info[j]) == cc" |
|
662 * in the following loop. But not doing it is safe; we might |
|
663 * end up moving all the 220 MCMs and 230 MCMs together in one |
|
664 * move and be done. */ |
|
665 unsigned int j = i; |
|
666 while (j < end && info_is_mcm (info[j])) |
|
667 j++; |
|
668 DEBUG_MSG (ARABIC, buffer, "Found %d's from %d to %d\n", cc, i, j); |
|
669 |
|
670 if (i == j) |
|
671 continue; |
|
672 |
|
673 /* Shift it! */ |
|
674 DEBUG_MSG (ARABIC, buffer, "Shifting %d's: %d %d\n", cc, i, j); |
|
675 hb_glyph_info_t temp[HB_OT_SHAPE_COMPLEX_MAX_COMBINING_MARKS]; |
|
676 assert (j - i <= ARRAY_LENGTH (temp)); |
|
677 buffer->merge_clusters (start, j); |
|
678 memmove (temp, &info[i], (j - i) * sizeof (hb_glyph_info_t)); |
|
679 memmove (&info[start + j - i], &info[start], (i - start) * sizeof (hb_glyph_info_t)); |
|
680 memmove (&info[start], temp, (j - i) * sizeof (hb_glyph_info_t)); |
|
681 |
|
682 start += j - i; |
|
683 |
|
684 i = j; |
|
685 } |
|
686 } |
|
687 |
608 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = |
688 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic = |
609 { |
689 { |
610 "arabic", |
|
611 collect_features_arabic, |
690 collect_features_arabic, |
612 NULL, /* override_features */ |
691 nullptr, /* override_features */ |
613 data_create_arabic, |
692 data_create_arabic, |
614 data_destroy_arabic, |
693 data_destroy_arabic, |
615 NULL, /* preprocess_text */ |
694 nullptr, /* preprocess_text */ |
616 postprocess_glyphs_arabic, |
695 postprocess_glyphs_arabic, |
617 HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, |
696 HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT, |
618 NULL, /* decompose */ |
697 nullptr, /* decompose */ |
619 NULL, /* compose */ |
698 nullptr, /* compose */ |
620 setup_masks_arabic, |
699 setup_masks_arabic, |
621 NULL, /* disable_otl */ |
700 nullptr, /* disable_otl */ |
|
701 reorder_marks_arabic, |
622 HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, |
702 HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE, |
623 true, /* fallback_position */ |
703 true, /* fallback_position */ |
624 }; |
704 }; |