aboutsummaryrefslogtreecommitdiffstats
path: root/text
diff options
context:
space:
mode:
Diffstat (limited to 'text')
-rw-r--r--text/shape-build.c64
-rw-r--r--text/shape.c194
-rw-r--r--text/shape.h9
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 };