Index: third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic.cc |
diff --git a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic.cc b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic.cc |
index eb1e0be9defec9fe889967910650a7cbf8c04f4a..9edefe305dc8e8a04553034466460f1e01db46af 100644 |
--- a/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic.cc |
+++ b/third_party/harfbuzz-ng/src/hb-ot-shape-complex-indic.cc |
@@ -128,14 +128,6 @@ static const hb_codepoint_t ra_chars[] = { |
0x179A, /* Khmer */ /* No Reph, Visual Repha */ |
}; |
-static inline indic_position_t |
-consonant_position (hb_codepoint_t u) |
-{ |
- if ((u & ~0x007F) == 0x1780) |
- return POS_BELOW_C; /* In Khmer coeng model, post and below forms should not be reordered. */ |
- return POS_BASE_C; /* Will recategorize later based on font lookups. */ |
-} |
- |
static inline bool |
is_ra (hb_codepoint_t u) |
{ |
@@ -149,7 +141,7 @@ static inline bool |
is_one_of (const hb_glyph_info_t &info, unsigned int flags) |
{ |
/* If it ligated, all bets are off. */ |
- if (is_a_ligature (info)) return false; |
+ if (_hb_glyph_info_ligated (&info)) return false; |
return !!(FLAG (info.indic_category()) & flags); |
} |
@@ -160,12 +152,14 @@ is_joiner (const hb_glyph_info_t &info) |
return is_one_of (info, JOINER_FLAGS); |
} |
+#define MEDIAL_FLAGS (FLAG (OT_CM) | FLAG (OT_CM2)) |
+ |
/* Note: |
* |
* We treat Vowels and placeholders as if they were consonants. This is safe because Vowels |
* cannot happen in a consonant syllable. The plus side however is, we can call the |
* consonant syllable logic from the vowel syllable function and get it all right! */ |
-#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CM) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)) |
+#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)) |
static inline bool |
is_consonant (const hb_glyph_info_t &info) |
{ |
@@ -194,15 +188,15 @@ set_indic_properties (hb_glyph_info_t &info) |
/* The spec says U+0952 is OT_A. However, testing shows that Uniscribe |
- * treats U+0951..U+0952 all as OT_VD. |
+ * treats U+0951..U+0954 all behave similarly. |
* TESTS: |
* U+092E,U+0947,U+0952 |
* U+092E,U+0952,U+0947 |
* U+092E,U+0947,U+0951 |
* U+092E,U+0951,U+0947 |
- * */ |
+ */ |
if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0951, 0x0954))) |
- cat = OT_VD; |
+ cat = OT_A; |
if (unlikely (u == 0x17D1)) |
cat = OT_X; |
@@ -220,7 +214,10 @@ set_indic_properties (hb_glyph_info_t &info) |
else if (unlikely (u == 0x200C)) cat = OT_ZWNJ; |
else if (unlikely (u == 0x200D)) cat = OT_ZWJ; |
else if (unlikely (u == 0x25CC)) cat = OT_DOTTEDCIRCLE; |
- else if (unlikely (u == 0x0A71)) cat = OT_SM; /* GURMUKHI ADDAK. More like consonant medial. like 0A75. */ |
+ else if (unlikely (u == 0x0A71)) cat = OT_SM; /* GURMUKHI ADDAK. Move it to the end. */ |
+ else if (unlikely (u == 0xA982)) cat = OT_SM; /* Javanese repha. */ |
+ else if (unlikely (u == 0xA9BE)) cat = OT_CM2; /* Javanese medial ya. */ |
+ else if (unlikely (u == 0xA9BD)) { cat = OT_M; pos = POS_POST_C; } /* Javanese vocalic r. */ |
if (cat == OT_Repha) { |
/* There are two kinds of characters marked as Repha: |
@@ -241,7 +238,7 @@ set_indic_properties (hb_glyph_info_t &info) |
if ((FLAG (cat) & CONSONANT_FLAGS)) |
{ |
- pos = consonant_position (u); |
+ pos = POS_BASE_C; |
if (is_ra (u)) |
cat = OT_Ra; |
} |
@@ -249,7 +246,7 @@ set_indic_properties (hb_glyph_info_t &info) |
{ |
pos = matra_position (u, pos); |
} |
- else if (cat == OT_SM || cat == OT_VD) |
+ else if ((FLAG (cat) & (FLAG (OT_SM) | FLAG (OT_VD) | FLAG (OT_A) | FLAG (OT_Avag)))) |
{ |
pos = POS_SMVD; |
} |
@@ -277,16 +274,16 @@ set_indic_properties (hb_glyph_info_t &info) |
enum base_position_t { |
BASE_POS_FIRST, |
+ BASE_POS_LAST_SINHALA, |
BASE_POS_LAST |
}; |
enum reph_position_t { |
- REPH_POS_DEFAULT = POS_BEFORE_POST, |
- |
REPH_POS_AFTER_MAIN = POS_AFTER_MAIN, |
REPH_POS_BEFORE_SUB = POS_BEFORE_SUB, |
REPH_POS_AFTER_SUB = POS_AFTER_SUB, |
REPH_POS_BEFORE_POST = POS_BEFORE_POST, |
- REPH_POS_AFTER_POST = POS_AFTER_POST |
+ REPH_POS_AFTER_POST = POS_AFTER_POST, |
+ REPH_POS_DONT_CARE = POS_RA_TO_BECOME_REPH |
}; |
enum reph_mode_t { |
REPH_MODE_IMPLICIT, /* Reph formed out of initial Ra,H sequence. */ |
@@ -294,6 +291,15 @@ enum reph_mode_t { |
REPH_MODE_VIS_REPHA, /* Encoded Repha character, no reordering needed. */ |
REPH_MODE_LOG_REPHA /* Encoded Repha character, needs reordering. */ |
}; |
+enum blwf_mode_t { |
+ BLWF_MODE_PRE_AND_POST, /* Below-forms feature applied to pre-base and post-base. */ |
+ BLWF_MODE_POST_ONLY /* Below-forms feature applied to post-base only. */ |
+}; |
+enum pref_len_t { |
+ PREF_LEN_1 = 1, |
+ PREF_LEN_2 = 2, |
+ PREF_LEN_DONT_CARE = PREF_LEN_2 |
+}; |
struct indic_config_t |
{ |
hb_script_t script; |
@@ -302,23 +308,27 @@ struct indic_config_t |
base_position_t base_pos; |
reph_position_t reph_pos; |
reph_mode_t reph_mode; |
+ blwf_mode_t blwf_mode; |
+ pref_len_t pref_len; |
}; |
static const indic_config_t indic_configs[] = |
{ |
/* Default. Should be first. */ |
- {HB_SCRIPT_INVALID, false, 0,BASE_POS_LAST, REPH_POS_DEFAULT, REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_DEVANAGARI,true, 0x094D,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_BENGALI, true, 0x09CD,BASE_POS_LAST, REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_GURMUKHI, true, 0x0A4D,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_GUJARATI, true, 0x0ACD,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_ORIYA, true, 0x0B4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_TAMIL, true, 0x0BCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_TELUGU, true, 0x0C4D,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT}, |
- {HB_SCRIPT_KANNADA, true, 0x0CCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT}, |
- {HB_SCRIPT_MALAYALAM, true, 0x0D4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA}, |
- {HB_SCRIPT_SINHALA, false,0x0DCA,BASE_POS_FIRST,REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT}, |
- {HB_SCRIPT_KHMER, false,0x17D2,BASE_POS_FIRST,REPH_POS_DEFAULT, REPH_MODE_VIS_REPHA}, |
+ {HB_SCRIPT_INVALID, false, 0,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_1}, |
+ {HB_SCRIPT_DEVANAGARI,true, 0x094D,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, |
+ {HB_SCRIPT_BENGALI, true, 0x09CD,BASE_POS_LAST, REPH_POS_AFTER_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, |
+ {HB_SCRIPT_GURMUKHI, true, 0x0A4D,BASE_POS_LAST, REPH_POS_BEFORE_SUB, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, |
+ {HB_SCRIPT_GUJARATI, true, 0x0ACD,BASE_POS_LAST, REPH_POS_BEFORE_POST,REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, |
+ {HB_SCRIPT_ORIYA, true, 0x0B4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, |
+ {HB_SCRIPT_TAMIL, true, 0x0BCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_2}, |
+ {HB_SCRIPT_TELUGU, true, 0x0C4D,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_EXPLICIT, BLWF_MODE_POST_ONLY, PREF_LEN_2}, |
+ {HB_SCRIPT_KANNADA, true, 0x0CCD,BASE_POS_LAST, REPH_POS_AFTER_POST, REPH_MODE_IMPLICIT, BLWF_MODE_POST_ONLY, PREF_LEN_2}, |
+ {HB_SCRIPT_MALAYALAM, true, 0x0D4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA,BLWF_MODE_PRE_AND_POST, PREF_LEN_2}, |
+ {HB_SCRIPT_SINHALA, false,0x0DCA,BASE_POS_LAST_SINHALA, |
+ REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT, BLWF_MODE_PRE_AND_POST, PREF_LEN_DONT_CARE}, |
+ {HB_SCRIPT_KHMER, false,0x17D2,BASE_POS_FIRST,REPH_POS_DONT_CARE, REPH_MODE_VIS_REPHA,BLWF_MODE_PRE_AND_POST, PREF_LEN_2}, |
+ {HB_SCRIPT_JAVANESE, false,0xA9C0,BASE_POS_FIRST,REPH_POS_DONT_CARE, REPH_MODE_VIS_REPHA,BLWF_MODE_PRE_AND_POST, PREF_LEN_1}, |
}; |
@@ -345,15 +355,17 @@ indic_features[] = |
{HB_TAG('r','k','r','f'), F_GLOBAL}, |
{HB_TAG('p','r','e','f'), F_NONE}, |
{HB_TAG('b','l','w','f'), F_NONE}, |
- {HB_TAG('h','a','l','f'), F_NONE}, |
{HB_TAG('a','b','v','f'), F_NONE}, |
+ {HB_TAG('h','a','l','f'), F_NONE}, |
{HB_TAG('p','s','t','f'), F_NONE}, |
- {HB_TAG('c','f','a','r'), F_NONE}, |
{HB_TAG('v','a','t','u'), F_GLOBAL}, |
{HB_TAG('c','j','c','t'), F_GLOBAL}, |
+ {HB_TAG('c','f','a','r'), F_NONE}, |
/* |
* Other features. |
* These features are applied all at once, after final_reordering. |
+ * Default Bengali font in Windows for example has intermixed |
+ * lookups for init,pres,abvs,blws features. |
*/ |
{HB_TAG('i','n','i','t'), F_NONE}, |
{HB_TAG('p','r','e','s'), F_GLOBAL}, |
@@ -377,12 +389,12 @@ enum { |
_RKRF, |
PREF, |
BLWF, |
- HALF, |
ABVF, |
+ HALF, |
PSTF, |
- CFAR, |
_VATU, |
_CJCT, |
+ CFAR, |
INIT, |
_PRES, |
@@ -410,6 +422,10 @@ static void |
final_reordering (const hb_ot_shape_plan_t *plan, |
hb_font_t *font, |
hb_buffer_t *buffer); |
+static void |
+clear_syllables (const hb_ot_shape_plan_t *plan, |
+ hb_font_t *font, |
+ hb_buffer_t *buffer); |
static void |
collect_features_indic (hb_ot_shape_planner_t *plan) |
@@ -435,14 +451,26 @@ collect_features_indic (hb_ot_shape_planner_t *plan) |
for (; i < INDIC_NUM_FEATURES; i++) { |
map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ); |
} |
+ |
+ map->add_global_bool_feature (HB_TAG('c','a','l','t')); |
+ map->add_global_bool_feature (HB_TAG('c','l','i','g')); |
+ |
+ map->add_gsub_pause (clear_syllables); |
} |
static void |
override_features_indic (hb_ot_shape_planner_t *plan) |
{ |
- /* Uniscribe does not apply 'kern'. */ |
+ /* Uniscribe does not apply 'kern' in Khmer. */ |
if (hb_options ().uniscribe_bug_compatible) |
- plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL); |
+ { |
+ switch ((hb_tag_t) plan->props.script) |
+ { |
+ case HB_SCRIPT_KHMER: |
+ plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL); |
+ break; |
+ } |
+ } |
plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL); |
} |
@@ -450,8 +478,9 @@ override_features_indic (hb_ot_shape_planner_t *plan) |
struct would_substitute_feature_t |
{ |
- inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag) |
+ inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_) |
{ |
+ zero_context = zero_context_; |
map->get_stage_lookups (0/*GSUB*/, |
map->get_feature_stage (0/*GSUB*/, feature_tag), |
&lookups, &count); |
@@ -459,7 +488,6 @@ struct would_substitute_feature_t |
inline bool would_substitute (const hb_codepoint_t *glyphs, |
unsigned int glyphs_count, |
- bool zero_context, |
hb_face_t *face) const |
{ |
for (unsigned int i = 0; i < count; i++) |
@@ -471,6 +499,7 @@ struct would_substitute_feature_t |
private: |
const hb_ot_map_t::lookup_map_t *lookups; |
unsigned int count; |
+ bool zero_context; |
}; |
struct indic_shape_plan_t |
@@ -526,10 +555,13 @@ data_create_indic (const hb_ot_shape_plan_t *plan) |
indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FF) != '2'); |
indic_plan->virama_glyph = (hb_codepoint_t) -1; |
- indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f')); |
- indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f')); |
- indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f')); |
- indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f')); |
+ /* Use zero-context would_substitute() matching for new-spec of the main |
+ * Indic scripts, but not for old-spec or scripts with one spec only. */ |
+ bool zero_context = indic_plan->config->has_old_spec || !indic_plan->is_old_spec; |
+ indic_plan->rphf.init (&plan->map, HB_TAG('r','p','h','f'), zero_context); |
+ indic_plan->pref.init (&plan->map, HB_TAG('p','r','e','f'), zero_context); |
+ indic_plan->blwf.init (&plan->map, HB_TAG('b','l','w','f'), zero_context); |
+ indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'), zero_context); |
for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++) |
indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ? |
@@ -546,7 +578,8 @@ data_destroy_indic (void *data) |
static indic_position_t |
consonant_position_from_face (const indic_shape_plan_t *indic_plan, |
- const hb_codepoint_t glyphs[2], |
+ const hb_codepoint_t consonant, |
+ const hb_codepoint_t virama, |
hb_face_t *face) |
{ |
/* For old-spec, the order of glyphs is Consonant,Virama, |
@@ -559,16 +592,19 @@ consonant_position_from_face (const indic_shape_plan_t *indic_plan, |
* 930,94D in 'blwf', not the expected 94D,930 (with new-spec |
* table). As such, we simply match both sequences. Seems |
* to work. */ |
- bool zero_context = indic_plan->is_old_spec ? false : true; |
- hb_codepoint_t glyphs_r[2] = {glyphs[1], glyphs[0]}; |
- if (indic_plan->pref.would_substitute (glyphs , 2, zero_context, face) || |
- indic_plan->pref.would_substitute (glyphs_r, 2, zero_context, face)) |
- return POS_POST_C; |
- if (indic_plan->blwf.would_substitute (glyphs , 2, zero_context, face) || |
- indic_plan->blwf.would_substitute (glyphs_r, 2, zero_context, face)) |
+ hb_codepoint_t glyphs[3] = {virama, consonant, virama}; |
+ if (indic_plan->blwf.would_substitute (glyphs , 2, face) || |
+ indic_plan->blwf.would_substitute (glyphs+1, 2, face)) |
return POS_BELOW_C; |
- if (indic_plan->pstf.would_substitute (glyphs , 2, zero_context, face) || |
- indic_plan->pstf.would_substitute (glyphs_r, 2, zero_context, face)) |
+ if (indic_plan->pstf.would_substitute (glyphs , 2, face) || |
+ indic_plan->pstf.would_substitute (glyphs+1, 2, face)) |
+ return POS_POST_C; |
+ unsigned int pref_len = indic_plan->config->pref_len; |
+ if ((pref_len == PREF_LEN_2 && |
+ (indic_plan->pref.would_substitute (glyphs , 2, face) || |
+ indic_plan->pref.would_substitute (glyphs+1, 2, face))) |
+ || (pref_len == PREF_LEN_1 && |
+ indic_plan->pref.would_substitute (glyphs+1, 1, face))) |
return POS_POST_C; |
return POS_BASE_C; |
} |
@@ -578,6 +614,7 @@ enum syllable_type_t { |
consonant_syllable, |
vowel_syllable, |
standalone_cluster, |
+ avagraha_cluster, |
broken_cluster, |
non_indic_cluster, |
}; |
@@ -627,15 +664,18 @@ update_consonant_positions (const hb_ot_shape_plan_t *plan, |
{ |
const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; |
- hb_codepoint_t glyphs[2]; |
- if (indic_plan->get_virama_glyph (font, &glyphs[0])) |
+ if (indic_plan->config->base_pos != BASE_POS_LAST) |
+ return; |
+ |
+ hb_codepoint_t virama; |
+ if (indic_plan->get_virama_glyph (font, &virama)) |
{ |
hb_face_t *face = font->face; |
unsigned int count = buffer->len; |
for (unsigned int i = 0; i < count; i++) |
if (buffer->info[i].indic_position() == POS_BASE_C) { |
- glyphs[1] = buffer->info[i].codepoint; |
- buffer->info[i].indic_position() = consonant_position_from_face (indic_plan, glyphs, face); |
+ hb_codepoint_t consonant = buffer->info[i].codepoint; |
+ buffer->info[i].indic_position() = consonant_position_from_face (indic_plan, consonant, virama, face); |
} |
} |
} |
@@ -676,7 +716,8 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
* and has more than one consonant, Ra is excluded from candidates for |
* base consonants. */ |
unsigned int limit = start; |
- if (indic_plan->mask_array[RPHF] && |
+ if (indic_plan->config->reph_pos != REPH_POS_DONT_CARE && |
+ indic_plan->mask_array[RPHF] && |
start + 3 <= end && |
( |
(indic_plan->config->reph_mode == REPH_MODE_IMPLICIT && !is_joiner (info[start + 2])) || |
@@ -685,7 +726,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
{ |
/* See if it matches the 'rphf' feature. */ |
hb_codepoint_t glyphs[2] = {info[start].codepoint, info[start + 1].codepoint}; |
- if (indic_plan->rphf.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true, face)) |
+ if (indic_plan->rphf.would_substitute (glyphs, ARRAY_LENGTH (glyphs), face)) |
{ |
limit += 2; |
while (limit < end && is_joiner (info[limit])) |
@@ -757,9 +798,12 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
} |
break; |
- case BASE_POS_FIRST: |
+ case BASE_POS_LAST_SINHALA: |
{ |
- /* In scripts without half forms (eg. Khmer), the first consonant is always the base. */ |
+ /* Sinhala base positioning is slightly different from main Indic, in that: |
+ * 1. It's ZWJ behavior is different, |
+ * 2. We don't need to look into the font for consonant positions. |
+ */ |
if (!has_reph) |
base = limit; |
@@ -767,7 +811,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
/* Find the last base consonant that is not blocked by ZWJ. If there is |
* a ZWJ right before a base consonant, that would request a subjoined form. */ |
for (unsigned int i = limit; i < end; i++) |
- if (is_consonant (info[i]) && info[i].indic_position() == POS_BASE_C) |
+ if (is_consonant (info[i])) |
{ |
if (limit < i && info[i - 1].indic_category() == OT_ZWJ) |
break; |
@@ -777,7 +821,23 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
/* Mark all subsequent consonants as below. */ |
for (unsigned int i = base + 1; i < end; i++) |
- if (is_consonant (info[i]) && info[i].indic_position() == POS_BASE_C) |
+ if (is_consonant (info[i])) |
+ info[i].indic_position() = POS_BELOW_C; |
+ } |
+ break; |
+ |
+ case BASE_POS_FIRST: |
+ { |
+ /* The first consonant is always the base. */ |
+ |
+ assert (indic_plan->config->reph_mode == REPH_MODE_VIS_REPHA); |
+ assert (!has_reph); |
+ |
+ base = start; |
+ |
+ /* Mark all subsequent consonants as below. */ |
+ for (unsigned int i = base + 1; i < end; i++) |
+ if (is_consonant (info[i])) |
info[i].indic_position() = POS_BELOW_C; |
} |
break; |
@@ -876,7 +936,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
indic_position_t last_pos = POS_START; |
for (unsigned int i = start; i < end; i++) |
{ |
- if ((FLAG (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | HALANT_OR_COENG_FLAGS))) |
+ if ((FLAG (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | HALANT_OR_COENG_FLAGS))) |
{ |
info[i].indic_position() = last_pos; |
if (unlikely (info[i].indic_category() == OT_H && |
@@ -902,33 +962,68 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
} |
} |
} |
- /* Re-attach ZWJ, ZWNJ, and halant to next char, for after-base consonants. */ |
+ /* For post-base consonants let them own anything before them |
+ * since the last consonant or matra. */ |
{ |
- unsigned int last_halant = end; |
+ unsigned int last = base; |
for (unsigned int i = base + 1; i < end; i++) |
- if (is_halant_or_coeng (info[i])) |
- last_halant = i; |
- else if (is_consonant (info[i])) { |
- for (unsigned int j = last_halant; j < i; j++) |
- if (info[j].indic_position() != POS_SMVD) |
+ if (is_consonant (info[i])) |
+ { |
+ for (unsigned int j = last + 1; j < i; j++) |
+ if (info[j].indic_position() < POS_SMVD) |
info[j].indic_position() = info[i].indic_position(); |
- } |
+ last = i; |
+ } else if (info[i].indic_category() == OT_M) |
+ last = i; |
} |
+ |
{ |
- /* Things are out-of-control for post base positions, they may shuffle |
- * around like crazy, so merge clusters. For pre-base stuff, we handle |
- * cluster issues in final reordering. */ |
- buffer->merge_clusters (base, end); |
+ /* Use syllable() for sort accounting temporarily. */ |
+ unsigned int syllable = info[start].syllable(); |
+ for (unsigned int i = start; i < end; i++) |
+ info[i].syllable() = i - start; |
+ |
/* Sit tight, rock 'n roll! */ |
hb_bubble_sort (info + start, end - start, compare_indic_order); |
/* Find base again */ |
base = end; |
for (unsigned int i = start; i < end; i++) |
- if (info[i].indic_position() == POS_BASE_C) { |
- base = i; |
+ if (info[i].indic_position() == POS_BASE_C) |
+ { |
+ base = i; |
break; |
} |
+ /* Things are out-of-control for post base positions, they may shuffle |
+ * around like crazy. In old-spec mode, we move halants around, so in |
+ * that case merge all clusters after base. Otherwise, check the sort |
+ * order and merge as needed. |
+ * For pre-base stuff, we handle cluster issues in final reordering. */ |
+ if (indic_plan->is_old_spec || end - base > 127) |
+ buffer->merge_clusters (base, end); |
+ else |
+ { |
+ /* Note! syllable() is a one-byte field. */ |
+ for (unsigned int i = base; i < end; i++) |
+ if (info[i].syllable() != 255) |
+ { |
+ unsigned int max = i; |
+ unsigned int j = start + info[i].syllable(); |
+ while (j != i) |
+ { |
+ max = MAX (max, j); |
+ unsigned int next = start + info[j].syllable(); |
+ info[j].syllable() = 255; /* So we don't process j later again. */ |
+ j = next; |
+ } |
+ if (i != max) |
+ buffer->merge_clusters (i, max + 1); |
+ } |
+ } |
+ |
+ /* Put syllable back in. */ |
+ for (unsigned int i = start; i < end; i++) |
+ info[i].syllable() = syllable; |
} |
/* Setup masks now */ |
@@ -942,6 +1037,9 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
/* Pre-base */ |
mask = indic_plan->mask_array[HALF]; |
+ if (!indic_plan->is_old_spec && |
+ indic_plan->config->blwf_mode == BLWF_MODE_PRE_AND_POST) |
+ mask |= indic_plan->mask_array[BLWF]; |
for (unsigned int i = start; i < base; i++) |
info[i].mask |= mask; |
/* Base */ |
@@ -986,15 +1084,19 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
} |
} |
- if (indic_plan->mask_array[PREF] && base + 2 < end) |
+ unsigned int pref_len = indic_plan->config->pref_len; |
+ if (indic_plan->mask_array[PREF] && base + pref_len < end) |
{ |
+ assert (1 <= pref_len && pref_len <= 2); |
/* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */ |
- for (unsigned int i = base + 1; i + 1 < end; i++) { |
- hb_codepoint_t glyphs[2] = {info[i].codepoint, info[i + 1].codepoint}; |
- if (indic_plan->pref.would_substitute (glyphs, ARRAY_LENGTH (glyphs), true, face)) |
+ for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) { |
+ hb_codepoint_t glyphs[2]; |
+ for (unsigned int j = 0; j < pref_len; j++) |
+ glyphs[j] = info[i + j].codepoint; |
+ if (indic_plan->pref.would_substitute (glyphs, pref_len, face)) |
{ |
- info[i++].mask |= indic_plan->mask_array[PREF]; |
- info[i++].mask |= indic_plan->mask_array[PREF]; |
+ for (unsigned int j = 0; j < pref_len; j++) |
+ info[i++].mask |= indic_plan->mask_array[PREF]; |
/* Mark the subsequent stuff with 'cfar'. Used in Khmer. |
* Read the feature spec. |
@@ -1002,8 +1104,9 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, |
* U+1784,U+17D2,U+179A,U+17D2,U+1782 |
* U+1784,U+17D2,U+1782,U+17D2,U+179A |
*/ |
- for (; i < end; i++) |
- info[i].mask |= indic_plan->mask_array[CFAR]; |
+ if (indic_plan->mask_array[CFAR]) |
+ for (; i < end; i++) |
+ info[i].mask |= indic_plan->mask_array[CFAR]; |
break; |
} |
@@ -1074,6 +1177,16 @@ initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan, |
} |
static void |
+initial_reordering_avagraha_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, |
+ hb_face_t *face HB_UNUSED, |
+ hb_buffer_t *buffer HB_UNUSED, |
+ unsigned int start HB_UNUSED, unsigned int end HB_UNUSED) |
+{ |
+ /* Nothing to do right now. If we ever switch to using the output |
+ * buffer in the reordering process, we'd need to next_glyph() here. */ |
+} |
+ |
+static void |
initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED, |
hb_face_t *face HB_UNUSED, |
hb_buffer_t *buffer HB_UNUSED, |
@@ -1095,6 +1208,7 @@ initial_reordering_syllable (const hb_ot_shape_plan_t *plan, |
case consonant_syllable: initial_reordering_consonant_syllable (plan, face, buffer, start, end); return; |
case vowel_syllable: initial_reordering_vowel_syllable (plan, face, buffer, start, end); return; |
case standalone_cluster: initial_reordering_standalone_cluster (plan, face, buffer, start, end); return; |
+ case avagraha_cluster: initial_reordering_avagraha_cluster (plan, face, buffer, start, end); return; |
case broken_cluster: initial_reordering_broken_cluster (plan, face, buffer, start, end); return; |
case non_indic_cluster: initial_reordering_non_indic_cluster (plan, face, buffer, start, end); return; |
} |
@@ -1264,9 +1378,9 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
info[new_pos] = tmp; |
if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */ |
base--; |
+ buffer->merge_clusters (new_pos, MIN (end, base + 1)); |
new_pos--; |
} |
- buffer->merge_clusters (new_pos, MIN (end, base + 1)); |
} else { |
for (unsigned int i = start; i < base; i++) |
if (info[i].indic_position () == POS_PRE_M) { |
@@ -1286,17 +1400,24 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
* before post-base consonant forms, and after post-base consonant forms. |
*/ |
- /* If there's anything after the Ra that has the REPH pos, it ought to be halant. |
- * Which means that the font has failed to ligate the Reph. In which case, we |
- * shouldn't move. */ |
+ /* Two cases: |
+ * |
+ * - If repha is encoded as a sequence of characters (Ra,H or Ra,H,ZWJ), then |
+ * we should only move it if the sequence ligated to the repha form. |
+ * |
+ * - If repha is encoded separately and in the logical position, we should only |
+ * move it if it did NOT ligate. If it ligated, it's probably the font trying |
+ * to make it work without the reordering. |
+ */ |
if (start + 1 < end && |
info[start].indic_position() == POS_RA_TO_BECOME_REPH && |
- info[start + 1].indic_position() != POS_RA_TO_BECOME_REPH) |
+ ((info[start].indic_category() == OT_Repha) ^ |
+ _hb_glyph_info_ligated (&info[start]))) |
{ |
unsigned int new_reph_pos; |
reph_position_t reph_pos = indic_plan->config->reph_pos; |
- /* XXX Figure out old behavior too */ |
+ assert (reph_pos != REPH_POS_DONT_CARE); |
/* 1. If reph should be positioned after post-base consonant forms, |
* proceed to step 5. |
@@ -1338,7 +1459,6 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
if (reph_pos == REPH_POS_AFTER_MAIN) |
{ |
new_reph_pos = base; |
- /* XXX Skip potential pre-base reordering Ra. */ |
while (new_reph_pos + 1 < end && info[new_reph_pos + 1].indic_position() <= POS_AFTER_MAIN) |
new_reph_pos++; |
if (new_reph_pos < end) |
@@ -1411,8 +1531,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
reph_move: |
{ |
- /* Yay, one big cluster! Merge before moving. */ |
- buffer->merge_clusters (start, end); |
+ buffer->merge_clusters (start, new_reph_pos + 1); |
/* Move */ |
hb_glyph_info_t reph = info[start]; |
@@ -1432,6 +1551,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
if (indic_plan->mask_array[PREF] && base + 1 < end) /* Otherwise there can't be any pre-base reordering Ra. */ |
{ |
+ unsigned int pref_len = indic_plan->config->pref_len; |
for (unsigned int i = base + 1; i < end; i++) |
if ((info[i].mask & indic_plan->mask_array[PREF]) != 0) |
{ |
@@ -1439,7 +1559,13 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
* of the <pref> feature. (Note that a font may shape a Ra consonant with |
* the feature generally but block it in certain contexts.) |
*/ |
- if (i + 1 == end || (info[i + 1].mask & indic_plan->mask_array[PREF]) == 0) |
+ /* Note: We just check that something got substituted. We don't check that |
+ * the <pref> feature actually did it... |
+ * |
+ * If pref len is longer than one, then only reorder if it ligated. If |
+ * pref len is one, only reorder if it didn't ligate with other things. */ |
+ if (_hb_glyph_info_substituted (&info[i]) && |
+ ((pref_len == 1) ^ _hb_glyph_info_ligated (&info[i]))) |
{ |
/* |
* 2. Try to find a target position the same way as for pre-base matra. |
@@ -1460,7 +1586,7 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
!(is_one_of (info[new_pos - 1], FLAG(OT_M) | HALANT_OR_COENG_FLAGS))) |
new_pos--; |
- /* In Khmer coeng model, a V,Ra can go *after* matras. If it goes after a |
+ /* In Khmer coeng model, a H,Ra can go *after* matras. If it goes after a |
* split matra, it should be reordered to *before* the left part of such matra. */ |
if (new_pos > start && info[new_pos - 1].indic_category() == OT_M) |
{ |
@@ -1510,11 +1636,20 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, |
*/ |
if (hb_options ().uniscribe_bug_compatible) |
{ |
- /* Uniscribe merges the entire cluster. |
- * This means, half forms are submerged into the main consonants cluster. |
- * This is unnecessary, and makes cursor positioning harder, but that's what |
- * Uniscribe does. */ |
- buffer->merge_clusters (start, end); |
+ switch ((hb_tag_t) plan->props.script) |
+ { |
+ case HB_SCRIPT_TAMIL: |
+ case HB_SCRIPT_SINHALA: |
+ break; |
+ |
+ default: |
+ /* Uniscribe merges the entire cluster... Except for Tamil & Sinhala. |
+ * This means, half forms are submerged into the main consonants cluster. |
+ * This is unnecessary, and makes cursor positioning harder, but that's what |
+ * Uniscribe does. */ |
+ buffer->merge_clusters (start, end); |
+ break; |
+ } |
} |
} |
@@ -1538,15 +1673,23 @@ final_reordering (const hb_ot_shape_plan_t *plan, |
} |
final_reordering_syllable (plan, buffer, last, count); |
- /* Zero syllables now... */ |
- for (unsigned int i = 0; i < count; i++) |
- info[i].syllable() = 0; |
- |
HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category); |
HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position); |
} |
+static void |
+clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED, |
+ hb_font_t *font HB_UNUSED, |
+ hb_buffer_t *buffer) |
+{ |
+ hb_glyph_info_t *info = buffer->info; |
+ unsigned int count = buffer->len; |
+ for (unsigned int i = 0; i < count; i++) |
+ info[i].syllable() = 0; |
+} |
+ |
+ |
static hb_ot_shape_normalization_mode_t |
normalization_preference_indic (const hb_segment_properties_t *props HB_UNUSED) |
{ |
@@ -1626,7 +1769,7 @@ decompose_indic (const hb_ot_shape_normalize_context_t *c, |
if (hb_options ().uniscribe_bug_compatible || |
(c->font->get_glyph (ab, 0, &glyph) && |
- indic_plan->pstf.would_substitute (&glyph, 1, true, c->font->face))) |
+ indic_plan->pstf.would_substitute (&glyph, 1, c->font->face))) |
{ |
/* Ok, safe to use Uniscribe-style decomposition. */ |
*a = 0x0DD9; |