diff options
Diffstat (limited to 'text')
| -rw-r--r-- | text/shape-build.c | 64 | ||||
| -rw-r--r-- | text/shape.c | 194 | ||||
| -rw-r--r-- | text/shape.h | 9 |
3 files changed, 234 insertions, 33 deletions
diff --git a/text/shape-build.c b/text/shape-build.c index 559517e..166ad22 100644 --- a/text/shape-build.c +++ b/text/shape-build.c @@ -331,13 +331,12 @@ static int categorize(uint32_t script, hb_tag_t gsub_script) { if (script == TAG("Arab")) return VEGA_SHAPER_ARABIC; if (script == TAG("Hebr")) return VEGA_SHAPER_HEBREW; if (script == TAG("Hang")) return VEGA_SHAPER_HANGUL; - if (script == TAG("Syrc") || script == TAG("Thai") || script == TAG("Laoo") || script == TAG("Khmr") || - script == TAG("Qaag")) - return -1; + if (script == TAG("Khmr")) return VEGA_SHAPER_KHMER; + if (script == TAG("Syrc") || script == TAG("Thai") || script == TAG("Laoo") || script == TAG("Qaag")) return -1; if (listed(indic_scripts, sizeof(indic_scripts) / sizeof(*indic_scripts), script)) return dflt ? VEGA_SHAPER_DEFAULT : (gsub_script & 0xFF) == '3' ? -1 : VEGA_SHAPER_INDIC; if (listed(use_scripts, sizeof(use_scripts) / sizeof(*use_scripts), script)) return dflt ? VEGA_SHAPER_DEFAULT : -1; - if (script == TAG("Mymr")) return dflt || gsub_script == TAG("mymr") ? VEGA_SHAPER_DEFAULT : -1; + if (script == TAG("Mymr")) return dflt || gsub_script == TAG("mymr") ? VEGA_SHAPER_DEFAULT : VEGA_SHAPER_MYANMAR; return VEGA_SHAPER_DEFAULT; } @@ -515,6 +514,37 @@ static bool plan_for(hb_face_t *face, uint32_t script_tag, int direction, Plan * add_gsub_pause(m, VEGA_PAUSE_FINAL_REORDERING); for (int i = 11; i < 17; ++i) add_feature(m, TAG(indic_features[i].tag), indic_features[i].flags, 1); } + /* hb-ot-shaper-khmer.cc: syllables and their reordering before any + * lookup; the basic features together, within syllables; the rest. */ + static const char *const khmer_basic[] = {"pref", "blwf", "abvf", "pstf", "cfar"}; + static const char *const khmer_other[] = {"pres", "abvs", "blws", "psts"}; + if (shaper == VEGA_SHAPER_KHMER) { + m->is_simple = false; + add_gsub_pause(m, VEGA_PAUSE_SYLLABLES); + add_gsub_pause(m, VEGA_PAUSE_INITIAL_REORDERING); + enable_feature(m, TAG("locl"), F_PER_SYLLABLE, 1); + enable_feature(m, TAG("ccmp"), F_PER_SYLLABLE, 1); + for (int i = 0; i < 5; ++i) add_feature(m, TAG(khmer_basic[i]), F_MANUAL_ZWNJ | F_MANUAL_ZWJ | F_PER_SYLLABLE, 1); + add_gsub_pause(m, VEGA_PAUSE_CLEAR_SYLLABLES); + for (int i = 0; i < 4; ++i) add_feature(m, TAG(khmer_other[i]), F_GLOBAL | F_MANUAL_ZWNJ | F_MANUAL_ZWJ, 1); + } + /* hb-ot-shaper-myanmar.cc: syllables; locl and ccmp; the reordering; + * the basic features one stage each; the rest. */ + static const char *const myanmar_basic[] = {"rphf", "pref", "blwf", "pstf"}; + static const char *const myanmar_other[] = {"pres", "abvs", "blws", "psts"}; + if (shaper == VEGA_SHAPER_MYANMAR) { + m->is_simple = false; + add_gsub_pause(m, VEGA_PAUSE_SYLLABLES); + enable_feature(m, TAG("locl"), F_PER_SYLLABLE, 1); + enable_feature(m, TAG("ccmp"), F_PER_SYLLABLE, 1); + add_gsub_pause(m, VEGA_PAUSE_INITIAL_REORDERING); + for (int i = 0; i < 4; ++i) { + enable_feature(m, TAG(myanmar_basic[i]), F_MANUAL_ZWJ | F_PER_SYLLABLE, 1); + add_gsub_pause(m, 0); + } + add_gsub_pause(m, VEGA_PAUSE_CLEAR_SYLLABLES); + for (int i = 0; i < 4; ++i) enable_feature(m, TAG(myanmar_other[i]), F_MANUAL_ZWJ, 1); + } if (shaper == VEGA_SHAPER_HANGUL) { m->is_simple = false; add_feature(m, TAG("ljmo"), F_NONE, 1); @@ -535,6 +565,11 @@ static bool plan_for(hb_face_t *face, uint32_t script_tag, int direction, Plan * add_feature(m, TAG("liga"), F_GLOBAL, 0); add_gsub_pause(m, VEGA_PAUSE_CLEAR_SYLLABLES); } + /* Khmer asks for clig, and turns liga off. */ + if (shaper == VEGA_SHAPER_KHMER) { + enable_feature(m, TAG("clig"), F_NONE, 1); + add_feature(m, TAG("liga"), F_GLOBAL, 0); + } if (!compile_map(m)) FAIL("out of memory, or more than %d stages", MAX_STAGES); /* hb_ot_shape_planner_t::compile */ @@ -545,9 +580,10 @@ static bool plan_for(hb_face_t *face, uint32_t script_tag, int direction, Plan * bool apply_kern = (!has_gpos_kern || !apply_gpos) && kern_table.data; if (apply_kern && (kern_table.other || kern_table.cross)) FAIL("%s: the font's kern table has more than pairs (state machines, classes, cross-stream kerning)", name); - /* Hangul's and Indic's shapers neither zero marks nor place them - * without GPOS. */ - bool script_marks = shaper != VEGA_SHAPER_HANGUL && shaper != VEGA_SHAPER_INDIC; + /* Hangul's, Indic's and Khmer's shapers zero no marks (Myanmar's does, + * before GPOS); none of these places marks without GPOS. */ + bool zeroes = shaper != VEGA_SHAPER_HANGUL && shaper != VEGA_SHAPER_INDIC && shaper != VEGA_SHAPER_KHMER; + bool script_marks = zeroes && shaper != VEGA_SHAPER_MYANMAR; bool adjust = !apply_gpos && (!apply_kern || !kern_table.cross), fallback_marks = adjust && script_marks; if (shaper == VEGA_SHAPER_ARABIC) { if (one_mask(m, "stch")) FAIL("%s: the font stretches (stch)", name); @@ -563,7 +599,7 @@ static bool plan_for(hb_face_t *face, uint32_t script_tag, int direction, Plan * uint32_t frac = one_mask(m, "frac"), numr = one_mask(m, "numr"), dnom = one_mask(m, "dnom"); *plan = (Plan){script_tag, direction, native == HB_DIRECTION_LTR ? VEGA_SHAPE_LTR : native == HB_DIRECTION_RTL ? VEGA_SHAPE_RTL : 0, shaper, - (apply_gpos ? VEGA_PLAN_GPOS : 0) | (script_marks ? VEGA_PLAN_ZERO_MARKS : 0) | + (apply_gpos ? VEGA_PLAN_GPOS : 0) | (zeroes ? VEGA_PLAN_ZERO_MARKS : 0) | (hb_ot_layout_has_glyph_classes(face) ? 0 : VEGA_PLAN_GUESS_CLASSES) | (one_mask(m, "mark") ? VEGA_PLAN_GPOS_MARK : 0) | (frac || (numr && dnom) ? VEGA_PLAN_FRACTIONS : 0) | (apply_kern ? VEGA_PLAN_KERN : 0) | (fallback_marks ? VEGA_PLAN_FALLBACK_MARKS : 0) | @@ -589,6 +625,8 @@ static bool plan_for(hb_face_t *face, uint32_t script_tag, int direction, Plan * for (int i = 0; i < VEGA_PLAN_STAGES; ++i) plan->stages[i] = stage_of(m, staged[i]); if ((m->chosen_script[0] & 0xFF) != '2') plan->shaper_flags |= VEGA_INDIC_OLD_SPEC; } + if (shaper == VEGA_SHAPER_KHMER) + for (int i = 0; i < 5; ++i) plan->masks[VEGA_MASK_KHMER_PREF + i] = one_mask(m, khmer_basic[i]); free(m); return true; fail: @@ -937,7 +975,8 @@ void vega_shaping_check(hb_font_t *font, const VegaShaping *s, uint32_t script, * punctuation), and those whose glyphs some substitution starts from. */ Pool letters = {0}, marks = {0}, others = {0}, hot = {0}, jamo_pools[3] = {{0}}, indic[INDIC_POOLS] = {{0}}; bool hangul = script == HB_TAG('H', 'a', 'n', 'g'); - bool syllabic = listed(indic_scripts, sizeof(indic_scripts) / sizeof(*indic_scripts), script); + bool syllabic = listed(indic_scripts, sizeof(indic_scripts) / sizeof(*indic_scripts), script) || + script == HB_TAG('K', 'h', 'm', 'r') || script == HB_TAG('M', 'y', 'm', 'r'); hb_set_t *inputs = hb_set_create(); uint32_t common = HB_TAG('Z', 'y', 'y', 'y'), inherited = HB_TAG('Z', 'i', 'n', 'h'); for (unsigned t = 0; t < s->lookup_count[0]; ++t) @@ -958,8 +997,11 @@ void vega_shaping_check(hb_font_t *font, const VegaShaping *s, uint32_t script, case VEGA_OT_RA: pool = INDIC_RA; break; case VEGA_OT_H: pool = INDIC_HALANTS; break; case VEGA_OT_N: pool = INDIC_NUKTAS; break; - case VEGA_OT_M: case VEGA_OT_MPST: pool = INDIC_MATRAS; break; - case VEGA_OT_SM: case VEGA_OT_A: pool = INDIC_MODIFIERS; break; + case VEGA_OT_M: case VEGA_OT_MPST: case VEGA_OT_VABV: case VEGA_OT_VBLW: case VEGA_OT_VPRE: + case VEGA_OT_VPST: pool = INDIC_MATRAS; break; + case VEGA_OT_SM: case VEGA_OT_A: case VEGA_OT_SMPST: case VEGA_OT_ROBATIC: case VEGA_OT_XGROUP: + case VEGA_OT_YGROUP: case VEGA_OT_AS: case VEGA_OT_MH: case VEGA_OT_MR: case VEGA_OT_MW: case VEGA_OT_MY: + case VEGA_OT_ML: case VEGA_OT_PT: case VEGA_OT_VS: pool = INDIC_MODIFIERS; break; case VEGA_OT_V: pool = INDIC_VOWELS; break; } if (pool >= 0) pool_add(&indic[pool], code); diff --git a/text/shape.c b/text/shape.c index 3443b0b..214d73b 100644 --- a/text/shape.c +++ b/text/shape.c @@ -227,7 +227,7 @@ bool vega_shaping_open(VegaShaping *s, const unsigned char *bytes, size_t size, for (uint32_t i = 0; i < s->plan_count; ++i) { const unsigned char *p = s->plans + (size_t)i * VEGA_PLAN_BYTES; uint32_t ranges[2][2] = {{le32(p + 80), le32(p + 84)}, {le32(p + 88), le32(p + 92)}}; - if ((p[4] != VEGA_SHAPE_LTR && p[4] != VEGA_SHAPE_RTL) || p[5] > VEGA_SHAPE_RTL || p[6] > VEGA_SHAPER_INDIC) + if ((p[4] != VEGA_SHAPE_LTR && p[4] != VEGA_SHAPE_RTL) || p[5] > VEGA_SHAPE_RTL || p[6] > VEGA_SHAPER_MYANMAR) REFUSE("plan %u", i); if ((p[7] & VEGA_PLAN_FALLBACK_MARKS) && !s->extents) REFUSE("plan %u: marks placed without extents", i); for (int table = 0; table < 2; ++table) { @@ -823,6 +823,7 @@ static bool compose(const Normalizer *c, uint32_t a, uint32_t b, uint32_t *ab) { switch (c->plan->shaper) { case VEGA_SHAPER_HEBREW: return compose_hebrew(c, a, b, ab); case VEGA_SHAPER_INDIC: return compose_indic(a, b, ab); + case VEGA_SHAPER_KHMER: return !is_mark_category(vega_category(a)) && compose_unicode(a, b, ab); default: return compose_unicode(a, b, ab); } } @@ -845,6 +846,13 @@ static void next_char(VegaShaper *b, uint32_t glyph) { static bool decompose_char(const Normalizer *c, uint32_t ab, uint32_t *a, uint32_t *b) { if (c->plan->shaper == VEGA_SHAPER_INDIC && (ab == 0x0931 || ab == 0x09DC || ab == 0x09DD || ab == 0x0B94)) return false; + /* Khmer's split matras, which Unicode does not decompose */ + if (c->plan->shaper == VEGA_SHAPER_KHMER && + (ab == 0x17BE || ab == 0x17BF || ab == 0x17C0 || ab == 0x17C4 || ab == 0x17C5)) { + *a = 0x17C1; + *b = ab; + return true; + } return decompose_unicode(ab, a, b); } @@ -1026,7 +1034,9 @@ static void normalize(const VegaShaping *s, const Plan *plan, VegaShaper *b) { * none: what the font has stays, and nothing is composed again; * Indic's decomposes what decomposes even when the font has it whole. */ const bool none = plan->shaper == VEGA_SHAPER_HANGUL; - const bool always_short_circuit = none, might_short_circuit = none || plan->shaper != VEGA_SHAPER_INDIC; + const bool syllabic = plan->shaper == VEGA_SHAPER_INDIC || plan->shaper == VEGA_SHAPER_KHMER || + plan->shaper == VEGA_SHAPER_MYANMAR; + const bool always_short_circuit = none, might_short_circuit = none || !syllabic; bool all_simple = true; clear_output(b); size_t count = b->len; @@ -2574,11 +2584,12 @@ static size_t next_syllable(const VegaShaper *b, size_t start) { /* A dotted circle at the start of each broken syllable (after a repha that * starts it), for its marks to sit on; false when none went in. */ static bool insert_dotted_circles(const VegaShaping *s, VegaShaper *b, unsigned broken, unsigned category, int repha, - unsigned position) { + int position) { uint32_t glyph; if (!(b->scratch & HAS_BROKEN_SYLLABLE) || !nominal(s, 0x25CC, &glyph)) return false; /* All of it zero but these: HarfBuzz's is a fresh glyph_info. */ - Info circle = {.codepoint = glyph, .uprops = VEGA_CC, .position = (uint8_t)position, .category = (uint8_t)category}; + Info circle = {.codepoint = glyph, .uprops = VEGA_CC, .position = (uint8_t)(position >= 0 ? position : 0), + .category = (uint8_t)category}; clear_output(b); unsigned last = 0; while (b->idx < b->len && b->successful) { @@ -3186,15 +3197,153 @@ static void indic_final_reordering(const Indic *d, VegaShaper *b) { } } +/* ---- Khmer (hb-ot-shaper-khmer.cc): a coeng and Ro moved before the base + * and masked for pref, what follows them for cfar; a left matra moved + * first; the post-base features on all but the first glyph. */ + +static void setup_masks_category(VegaShaper *b) { + for (size_t i = 0; i < b->len; ++i) b->info[i].category = vega_indic_categories(b->info[i].codepoint) & 0xFF; +} + +static void khmer_reorder_syllable(const Plan *plan, VegaShaper *b, size_t start, size_t end) { + Info *info = b->info; + const uint32_t *masks = plan->masks; + uint32_t post = masks[VEGA_MASK_KHMER_BLWF] | masks[VEGA_MASK_KHMER_ABVF] | masks[VEGA_MASK_KHMER_PSTF]; + for (size_t i = start + 1; i < end; ++i) info[i].mask |= post; + unsigned coengs = 0; + for (size_t i = start + 1; i < end; ++i) + if (info[i].category == VEGA_OT_H && coengs <= 2 && i + 1 < end) { + coengs++; + if (info[i + 1].category == VEGA_OT_RA) { + info[i].mask |= masks[VEGA_MASK_KHMER_PREF]; + info[i + 1].mask |= masks[VEGA_MASK_KHMER_PREF]; + merge_clusters(b, start, i + 2); + Info t0 = info[i], t1 = info[i + 1]; + memmove(&info[start + 2], &info[start], (i - start) * sizeof(Info)); + info[start] = t0; + info[start + 1] = t1; + if (masks[VEGA_MASK_KHMER_CFAR]) + for (size_t j = i + 2; j < end; ++j) info[j].mask |= masks[VEGA_MASK_KHMER_CFAR]; + coengs = 2; + } + } else if (info[i].category == VEGA_OT_VPRE) { + merge_clusters(b, start, i + 1); + Info t = info[i]; + memmove(&info[start + 1], &info[start], (i - start) * sizeof(Info)); + info[start] = t; + } +} + +static bool khmer_reorder(const VegaShaping *s, const Plan *plan, VegaShaper *b) { + bool changed = insert_dotted_circles(s, b, 1, VEGA_OT_DOTTEDCIRCLE, -1, -1); + for (size_t start = 0, end; start < b->len; start = end) { + end = next_syllable(b, start); + if ((b->info[start].syllable & 0x0F) <= 1) khmer_reorder_syllable(plan, b, start, end); + } + return changed; +} + +/* ---- Myanmar (hb-ot-shaper-myanmar.cc): the base (after a kinzi), each + * glyph given its place around it, and the syllable sorted by place. */ + +#define CONSONANT_FLAGS_MYANMAR \ + (FLAG(VEGA_OT_C) | FLAG(VEGA_OT_CS) | FLAG(VEGA_OT_RA) | FLAG(VEGA_OT_V) | FLAG(VEGA_OT_PLACEHOLDER) | \ + FLAG(VEGA_OT_DOTTEDCIRCLE)) + +/* hb_buffer_t::sort by position: stable; a glyph that moves back merges + * the clusters it passes. */ +static void sort_by_position(VegaShaper *b, size_t start, size_t end) { + for (size_t i = start + 1; i < end; ++i) { + size_t j = i; + while (j > start && b->info[j - 1].position > b->info[i].position) j--; + if (i == j) continue; + merge_clusters(b, j, i + 1); + Info t = b->info[i]; + memmove(&b->info[j + 1], &b->info[j], (i - j) * sizeof(Info)); + b->info[j] = t; + } +} + +static void myanmar_reorder_syllable(VegaShaper *b, size_t start, size_t end) { + Info *info = b->info; + size_t base = end, limit = start; + bool has_reph = false; + if (start + 3 <= end && info[start].category == VEGA_OT_RA && info[start + 1].category == VEGA_OT_AS && + info[start + 2].category == VEGA_OT_H) { + limit += 3; + base = start; + has_reph = true; + } + if (!has_reph) base = limit; + for (size_t i = limit; i < end; ++i) + if (is_one_of(&info[i], CONSONANT_FLAGS_MYANMAR)) { + base = i; + break; + } + size_t i = start; + for (; i < start + (has_reph ? 3 : 0); ++i) info[i].position = VEGA_POS_AFTER_MAIN; + for (; i < base; ++i) info[i].position = VEGA_POS_PRE_C; + if (i < end) info[i++].position = VEGA_POS_BASE_C; + uint8_t pos = VEGA_POS_AFTER_MAIN; + for (; i < end; ++i) { + unsigned c = info[i].category; + if (c == VEGA_OT_MR) info[i].position = VEGA_POS_PRE_C; /* pre-base reordering */ + else if (c == VEGA_OT_VPRE) info[i].position = VEGA_POS_PRE_M; /* left matra */ + else if (c == VEGA_OT_VS) info[i].position = info[i - 1].position; + else if (pos == VEGA_POS_AFTER_MAIN && c == VEGA_OT_VBLW) info[i].position = pos = VEGA_POS_BELOW_C; + else if (pos == VEGA_POS_BELOW_C && c == VEGA_OT_A) info[i].position = VEGA_POS_BEFORE_SUB; + else if (pos == VEGA_POS_BELOW_C && c == VEGA_OT_VBLW) info[i].position = pos; + else if (pos == VEGA_POS_BELOW_C) info[i].position = pos = VEGA_POS_AFTER_SUB; + else info[i].position = pos; + } + sort_by_position(b, start, end); + /* A run of left matras back in their order (variation selectors after + * theirs). */ + size_t first_left_matra = end, last_left_matra = end; + for (size_t k = start; k < end; ++k) + if (info[k].position == VEGA_POS_PRE_M) { + if (first_left_matra == end) first_left_matra = k; + last_left_matra = k; + } + if (first_left_matra < last_left_matra) { + reverse_range(b, first_left_matra, last_left_matra + 1, false); + size_t k = first_left_matra; + for (size_t j = k; j <= last_left_matra; ++j) + if (info[j].category == VEGA_OT_VPRE) { + reverse_range(b, k, j + 1, false); + k = j + 1; + } + } +} + +static bool myanmar_reorder(const VegaShaping *s, VegaShaper *b) { + bool changed = insert_dotted_circles(s, b, 1, VEGA_OT_DOTTEDCIRCLE, -1, -1); + for (size_t start = 0, end; start < b->len; start = end) { + end = next_syllable(b, start); + if ((b->info[start].syllable & 0x0F) <= 1) myanmar_reorder_syllable(b, start, end); + } + return changed; +} + /* The shaper's part of the work at a pause in GSUB; whether it changed the * buffer. */ static bool shaper_pause(const VegaShaping *s, const Plan *plan, VegaShaper *b, unsigned pause) { - if (plan->shaper != VEGA_SHAPER_INDIC) return false; - Indic d = indic_of(s, plan); - switch (pause) { - case VEGA_PAUSE_SYLLABLES: find_syllables(b, &vega_indic_machine); return false; - case VEGA_PAUSE_INITIAL_REORDERING: return indic_initial_reordering(&d, b); - case VEGA_PAUSE_FINAL_REORDERING: indic_final_reordering(&d, b); return false; + switch (plan->shaper) { + case VEGA_SHAPER_INDIC: { + Indic d = indic_of(s, plan); + switch (pause) { + case VEGA_PAUSE_SYLLABLES: find_syllables(b, &vega_indic_machine); return false; + case VEGA_PAUSE_INITIAL_REORDERING: return indic_initial_reordering(&d, b); + case VEGA_PAUSE_FINAL_REORDERING: indic_final_reordering(&d, b); return false; + default: return false; + } + } + case VEGA_SHAPER_KHMER: + if (pause == VEGA_PAUSE_SYLLABLES) find_syllables(b, &vega_khmer_machine); + return pause == VEGA_PAUSE_INITIAL_REORDERING && khmer_reorder(s, plan, b); + case VEGA_SHAPER_MYANMAR: + if (pause == VEGA_PAUSE_SYLLABLES) find_syllables(b, &vega_myanmar_machine); + return pause == VEGA_PAUSE_INITIAL_REORDERING && myanmar_reorder(s, b); default: return false; } } @@ -3256,6 +3405,17 @@ static void propagate_attachment_offsets(Position *pos, size_t len, size_t i, in } } +static void zero_mark_widths(VegaShaper *b, bool adjust) { + for (size_t i = 0; i < b->len; ++i) + if (b->info[i].gprops & MARK) { + if (adjust) { + b->pos[i].x_offset -= b->pos[i].x_advance; + b->pos[i].y_offset -= b->pos[i].y_advance; + } + b->pos[i].x_advance = b->pos[i].y_advance = 0; + } +} + static void position(const VegaShaping *s, const Plan *plan, VegaShaper *b) { b->have_output = false; b->out_len = 0; @@ -3263,17 +3423,12 @@ static void position(const VegaShaping *s, const Plan *plan, VegaShaper *b) { for (size_t i = 0; i < b->len; ++i) b->pos[i].x_advance = advance_of(s, b->info[i].codepoint); if (b->scratch & HAS_SPACE_FALLBACK) fallback_spaces(s, b); bool adjust = (plan->flags & VEGA_PLAN_ADJUST_ZEROING) && b->direction == VEGA_SHAPE_LTR; + /* Marks' advances go by GDEF: before GPOS (Myanmar's shaper) or after. */ + bool early = plan->shaper == VEGA_SHAPER_MYANMAR; + if ((plan->flags & VEGA_PLAN_ZERO_MARKS) && early) zero_mark_widths(b, adjust); if (plan->flags & VEGA_PLAN_GPOS) apply_table(s, plan, b, 1); if (plan->flags & VEGA_PLAN_KERN) kern_table(s, b, plan); - if (plan->flags & VEGA_PLAN_ZERO_MARKS) - for (size_t i = 0; i < b->len; ++i) - if (b->info[i].gprops & MARK) { - if (adjust) { - b->pos[i].x_offset -= b->pos[i].x_advance; - b->pos[i].y_offset -= b->pos[i].y_advance; - } - b->pos[i].x_advance = b->pos[i].y_advance = 0; - } + if ((plan->flags & VEGA_PLAN_ZERO_MARKS) && !early) zero_mark_widths(b, adjust); if (b->scratch & HAS_DEFAULT_IGNORABLES) for (size_t i = 0; i < b->len; ++i) if (is_default_ignorable(&b->info[i])) @@ -3318,6 +3473,7 @@ long vega_shape(VegaShaper *b, const VegaShaping *s, const uint32_t *codes, size setup_masks_fraction(b, &plan); if (plan.shaper == VEGA_SHAPER_ARABIC) setup_masks_arabic(b, &plan); if (plan.shaper == VEGA_SHAPER_INDIC) setup_masks_indic(b); + if (plan.shaper == VEGA_SHAPER_KHMER || plan.shaper == VEGA_SHAPER_MYANMAR) setup_masks_category(b); if (plan.shaper == VEGA_SHAPER_HANGUL) for (size_t i = 0; i < b->len; ++i) if (b->info[i].action >= LJMO && b->info[i].action <= TJMO) diff --git a/text/shape.h b/text/shape.h index 7b12009..7f02729 100644 --- a/text/shape.h +++ b/text/shape.h @@ -6,8 +6,8 @@ /* Shaping (docs/design/004-text.md): a run of characters (one script, one * direction) to glyphs and their positions, as HarfBuzz 10 shapes it with - * its default, Arabic, Hebrew, Hangul and Indic shapers, glyph for glyph and - * unit for unit. + * its default, Arabic, Hebrew, Hangul, Indic, Khmer and Myanmar shapers, + * glyph for glyph and unit for unit. * * What a font's shaping needs travels in a symbol file (its shaping * section), written by the build tool: @@ -47,7 +47,8 @@ enum { VEGA_SHAPING_HEADER = 80, VEGA_PLAN_BYTES = 144, VEGA_STEP_BYTES = 8, VEGA_PLAN_MASKS = 16, VEGA_PLAN_STAGES = 5 }; enum { VEGA_SHAPE_LTR = 1, VEGA_SHAPE_RTL = 2 }; -enum { VEGA_SHAPER_DEFAULT, VEGA_SHAPER_ARABIC, VEGA_SHAPER_HEBREW, VEGA_SHAPER_HANGUL, VEGA_SHAPER_INDIC }; +enum { VEGA_SHAPER_DEFAULT, VEGA_SHAPER_ARABIC, VEGA_SHAPER_HEBREW, VEGA_SHAPER_HANGUL, VEGA_SHAPER_INDIC, + VEGA_SHAPER_KHMER, VEGA_SHAPER_MYANMAR }; /* Plan flags: GPOS runs; mark advances are zeroed after it (by GDEF); glyph * classes come from Unicode (the font has none); the font positions marks * (a 'mark' feature); automatic fractions; the kern table kerns; marks are @@ -63,6 +64,8 @@ enum { VEGA_MASK_ISOL = VEGA_MASK_SHAPER, VEGA_MASK_FINA, VEGA_MASK_FIN2, VEGA_M enum { VEGA_MASK_LJMO = VEGA_MASK_SHAPER, VEGA_MASK_VJMO, VEGA_MASK_TJMO }; /* Hangul */ enum { VEGA_MASK_RPHF = VEGA_MASK_SHAPER, VEGA_MASK_PREF, VEGA_MASK_BLWF, VEGA_MASK_ABVF, VEGA_MASK_HALF, VEGA_MASK_PSTF, VEGA_MASK_INDIC_INIT }; /* Indic */ +enum { VEGA_MASK_KHMER_PREF = VEGA_MASK_SHAPER, VEGA_MASK_KHMER_BLWF, VEGA_MASK_KHMER_ABVF, VEGA_MASK_KHMER_PSTF, + VEGA_MASK_KHMER_CFAR }; /* Khmer */ /* The stages a plan names: Indic's, whose features decide a consonant's * position by whether they would substitute it. */ enum { VEGA_STAGE_RPHF, VEGA_STAGE_PREF, VEGA_STAGE_BLWF, VEGA_STAGE_PSTF, VEGA_STAGE_VATU }; |
