Index: third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
index ea6d85c1e6c1f282dc5c9bc392752aac76a4d17b..9870ba3da05376de77301c2fc7d3a65505570a94 100644 |
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-arabic.cc |
@@ -57,68 +57,41 @@ enum { |
static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat) |
{ |
- if (likely (hb_in_range<hb_codepoint_t> (u, JOINING_TABLE_FIRST, JOINING_TABLE_LAST))) { |
- unsigned int j_type = joining_table[u - JOINING_TABLE_FIRST]; |
- if (likely (j_type != JOINING_TYPE_X)) |
- return j_type; |
- } |
- |
- /* Mongolian joining data is not in ArabicJoining.txt yet. */ |
- if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1800, 0x18AF))) |
- { |
- if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1880, 0x1886))) |
- return JOINING_TYPE_U; |
- |
- /* All letters, SIBE SYLLABLE BOUNDARY MARKER, and NIRUGU are D */ |
- if ((FLAG(gen_cat) & (FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) | |
- FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER))) |
- || u == 0x1807 || u == 0x180A) |
- return JOINING_TYPE_D; |
- } |
- |
- /* 'Phags-pa joining data is not in ArabicJoining.txt yet. */ |
- if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xA840, 0xA872))) |
- { |
- if (unlikely (u == 0xA872)) |
- return JOINING_TYPE_L; |
- |
- return JOINING_TYPE_D; |
- } |
- |
- if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D))) |
- { |
- return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C; |
- } |
- |
- return (FLAG(gen_cat) & (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT))) ? |
- JOINING_TYPE_T : JOINING_TYPE_U; |
+ unsigned int j_type = joining_type(u); |
+ if (likely (j_type != JOINING_TYPE_X)) |
+ return j_type; |
+ |
+ return (FLAG(gen_cat) & |
+ (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | |
+ FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | |
+ FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT)) |
+ ) ? JOINING_TYPE_T : JOINING_TYPE_U; |
} |
+#define FEATURE_IS_SYRIAC(tag) hb_in_range<unsigned char> ((unsigned char) (tag), '2', '3') |
+ |
static const hb_tag_t arabic_features[] = |
{ |
- HB_TAG('i','n','i','t'), |
- HB_TAG('m','e','d','i'), |
- HB_TAG('f','i','n','a'), |
HB_TAG('i','s','o','l'), |
- /* Syriac */ |
- HB_TAG('m','e','d','2'), |
+ HB_TAG('f','i','n','a'), |
HB_TAG('f','i','n','2'), |
HB_TAG('f','i','n','3'), |
+ HB_TAG('m','e','d','i'), |
+ HB_TAG('m','e','d','2'), |
+ HB_TAG('i','n','i','t'), |
HB_TAG_NONE |
}; |
/* Same order as the feature array */ |
enum { |
- INIT, |
- MEDI, |
- FINA, |
ISOL, |
- |
- /* Syriac */ |
- MED2, |
+ FINA, |
FIN2, |
FIN3, |
+ MEDI, |
+ MED2, |
+ INIT, |
NONE, |
@@ -171,14 +144,23 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) |
{ |
hb_ot_map_builder_t *map = &plan->map; |
- /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together, |
- * then rlig and calt each in their own stage. This makes IranNastaliq's ALLAH |
- * ligature work correctly. It's unfortunate though... |
+ /* We apply features according to the Arabic spec, with pauses |
+ * in between most. |
* |
- * This also makes Arial Bold in Windows7 work. See: |
+ * The pause between init/medi/... and rlig is required. See eg: |
* https://bugzilla.mozilla.org/show_bug.cgi?id=644184 |
* |
- * TODO: Add test cases for these two. |
+ * The pauses between init/medi/... themselves are not necessarily |
+ * needed as only one of those features is applied to any character. |
+ * The only difference it makes is when fonts have contextual |
+ * substitutions. We now follow the order of the spec, which makes |
+ * for better experience if that's what Uniscribe is doing. |
+ * |
+ * At least for Arabic, looks like Uniscribe has a pause between |
+ * rlig and calt. Otherwise the IranNastaliq's ALLAH ligature won't |
+ * work. However, testing shows that rlig and calt are applied |
+ * together for Mongolian in Uniscribe. As such, we only add a |
+ * pause for Arabic, not other scripts. |
*/ |
map->add_gsub_pause (nuke_joiners); |
@@ -189,16 +171,28 @@ collect_features_arabic (hb_ot_shape_planner_t *plan) |
map->add_gsub_pause (NULL); |
for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) |
- map->add_feature (arabic_features[i], 1, i < 4 ? F_HAS_FALLBACK : F_NONE); /* The first four features have fallback. */ |
- |
- map->add_gsub_pause (NULL); |
+ { |
+ bool has_fallback = plan->props.script == HB_SCRIPT_ARABIC && !FEATURE_IS_SYRIAC (arabic_features[i]); |
+ map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_NONE); |
+ map->add_gsub_pause (NULL); |
+ } |
map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK); |
- map->add_gsub_pause (arabic_fallback_shape); |
+ if (plan->props.script == HB_SCRIPT_ARABIC) |
+ map->add_gsub_pause (arabic_fallback_shape); |
map->add_global_bool_feature (HB_TAG('c','a','l','t')); |
map->add_gsub_pause (NULL); |
+ /* The spec includes 'cswh'. Earlier versions of Windows |
+ * used to enable this by default, but testing suggests |
+ * that Windows 8 and later do not enable it by default, |
+ * and spec now says 'Off by default'. |
+ * We disabled this in ae23c24c32. |
+ * Note that IranNastaliq uses this feature extensively |
+ * to fixup broken glyph sequences. Oh well... |
+ * Test case: U+0643,U+0640,U+0631. */ |
+ //map->add_global_bool_feature (HB_TAG('c','s','w','h')); |
map->add_global_bool_feature (HB_TAG('m','s','e','t')); |
} |
@@ -228,8 +222,9 @@ data_create_arabic (const hb_ot_shape_plan_t *plan) |
arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC; |
for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) { |
arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]); |
- if (i < 4) |
- arabic_plan->do_fallback = arabic_plan->do_fallback && plan->map.needs_fallback (arabic_features[i]); |
+ arabic_plan->do_fallback = arabic_plan->do_fallback && |
+ !FEATURE_IS_SYRIAC (arabic_features[i]) && |
+ plan->map.needs_fallback (arabic_features[i]); |
} |
return arabic_plan; |
@@ -249,10 +244,9 @@ static void |
arabic_joining (hb_buffer_t *buffer) |
{ |
unsigned int count = buffer->len; |
+ hb_glyph_info_t *info = buffer->info; |
unsigned int prev = (unsigned int) -1, state = 0; |
- HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); |
- |
/* Check pre-context */ |
if (!(buffer->flags & HB_BUFFER_FLAG_BOT)) |
for (unsigned int i = 0; i < buffer->context_len[0]; i++) |
@@ -269,20 +263,19 @@ arabic_joining (hb_buffer_t *buffer) |
for (unsigned int i = 0; i < count; i++) |
{ |
- unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i])); |
+ unsigned int this_type = get_joining_type (info[i].codepoint, _hb_glyph_info_get_general_category (&info[i])); |
if (unlikely (this_type == JOINING_TYPE_T)) { |
- buffer->info[i].arabic_shaping_action() = NONE; |
+ info[i].arabic_shaping_action() = NONE; |
continue; |
} |
const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; |
if (entry->prev_action != NONE && prev != (unsigned int) -1) |
- for (; prev < i; prev++) |
- buffer->info[prev].arabic_shaping_action() = entry->prev_action; |
+ info[prev].arabic_shaping_action() = entry->prev_action; |
- buffer->info[i].arabic_shaping_action() = entry->curr_action; |
+ info[i].arabic_shaping_action() = entry->curr_action; |
prev = i; |
state = entry->next_state; |
@@ -298,12 +291,20 @@ arabic_joining (hb_buffer_t *buffer) |
const arabic_state_table_entry *entry = &arabic_state_table[state][this_type]; |
if (entry->prev_action != NONE && prev != (unsigned int) -1) |
- buffer->info[prev].arabic_shaping_action() = entry->prev_action; |
+ info[prev].arabic_shaping_action() = entry->prev_action; |
break; |
} |
+} |
- |
- HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |
+static void |
+mongolian_variation_selectors (hb_buffer_t *buffer) |
+{ |
+ /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */ |
+ unsigned int count = buffer->len; |
+ hb_glyph_info_t *info = buffer->info; |
+ for (unsigned int i = 1; i < count; i++) |
+ if (unlikely (hb_in_range (info[i].codepoint, 0x180Bu, 0x180Du))) |
+ info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action(); |
} |
static void |
@@ -311,12 +312,20 @@ setup_masks_arabic (const hb_ot_shape_plan_t *plan, |
hb_buffer_t *buffer, |
hb_font_t *font HB_UNUSED) |
{ |
+ HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action); |
+ |
const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data; |
arabic_joining (buffer); |
+ if (plan->props.script == HB_SCRIPT_MONGOLIAN) |
+ mongolian_variation_selectors (buffer); |
+ |
unsigned int count = buffer->len; |
+ hb_glyph_info_t *info = buffer->info; |
for (unsigned int i = 0; i < count; i++) |
- buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()]; |
+ info[i].mask |= arabic_plan->mask_array[info[i].arabic_shaping_action()]; |
+ |
+ HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action); |
} |
@@ -326,9 +335,10 @@ nuke_joiners (const hb_ot_shape_plan_t *plan HB_UNUSED, |
hb_buffer_t *buffer) |
{ |
unsigned int count = buffer->len; |
+ hb_glyph_info_t *info = buffer->info; |
for (unsigned int i = 0; i < count; i++) |
- if (_hb_glyph_info_is_zwj (&buffer->info[i])) |
- _hb_glyph_info_flip_joiners (&buffer->info[i]); |
+ if (_hb_glyph_info_is_zwj (&info[i])) |
+ _hb_glyph_info_flip_joiners (&info[i]); |
} |
static void |