Skip to content

Commit

Permalink
shaping: make mark-to-mark positioning more selective
Browse files Browse the repository at this point in the history
Reported in #107

The selection of glyph pairs for mark-to-mark positioning was too eager.
For the text in question the ligature component position was not being
considered which was resulting in mark-to-mark positioning being applied
to marks 2 and 3 when it should not have. Considering the ligature
component position corrects this.

However, requiring the ligature component position to always match broke
some marks in Vietnamese. The solution was the introduction of a
ligature flag on RawGlyph so that a mark glyph can be tracked as a
ligature. This is used during GPOS to allow mark-to-mark positioning
between a pair of marks if one of them is a ligature despite having
differing ligature component positions.

This behaviour matches Harfbuzz:

https://github.com/harfbuzz/harfbuzz/blob/09a17a086c54e5c1b1aa674bba5b5449286e1480/src/OT/Layout/GPOS/MarkMarkPosFormat1.hh#L134-L138
  • Loading branch information
wezm committed May 29, 2024
1 parent f343287 commit 1bf2db8
Show file tree
Hide file tree
Showing 9 changed files with 25 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ impl<T: FontTableProvider> Font<T> {
small_caps: false,
multi_subst_dup: false,
is_vert_alt: false,
ligature: false,
fake_bold: false,
fake_italic: false,
extra_data: (),
Expand Down
9 changes: 8 additions & 1 deletion src/gpos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -616,12 +616,19 @@ fn forall_mark_mark_glyph_pairs(
let mut i = start;
while i + 1 < infos.len() {
if infos[i].is_mark {
// infos[i] is the base mark. Scan forward looking for attaching marks
for j in i + 1..infos.len() {
f(i, j, infos)?;
if !infos[j].is_mark {
start = i + 1;
continue 'outer;
}

// infos[j] is a candidate attaching mark
if infos[i].glyph.liga_component_pos == infos[j].glyph.liga_component_pos {
f(i, j, infos)?;
} else if infos[i].glyph.ligature || infos[j].glyph.ligature {
f(i, j, infos)?;
}
}
}
i += 1;
Expand Down
4 changes: 4 additions & 0 deletions src/gsub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ impl Ligature {
glyphs[i].unicodes.append(&mut matched_glyph.unicodes);
glyphs[i].extra_data =
GlyphData::merge(glyphs[i].extra_data.clone(), matched_glyph.extra_data);
glyphs[i].ligature = true;
} else {
glyphs[index].liga_component_pos = matched as u16;
skip += 1;
Expand Down Expand Up @@ -127,6 +128,7 @@ pub struct RawGlyph<T> {
pub small_caps: bool,
pub multi_subst_dup: bool,
pub is_vert_alt: bool,
pub ligature: bool,
pub fake_bold: bool,
pub fake_italic: bool,
pub variation: Option<VariationSelector>,
Expand Down Expand Up @@ -440,6 +442,7 @@ fn multiplesubst<T: GlyphData>(
small_caps: glyphs[i].small_caps,
multi_subst_dup: true,
is_vert_alt: glyphs[i].is_vert_alt,
ligature: false,
fake_bold: glyphs[i].fake_bold,
fake_italic: glyphs[i].fake_italic,
extra_data: glyphs[i].extra_data.clone(),
Expand Down Expand Up @@ -981,6 +984,7 @@ fn find_alternate(features_list: &[FeatureInfo], feature_tag: u32) -> Option<usi
/// small_caps: false,
/// multi_subst_dup: false,
/// is_vert_alt: false,
/// ligature: false,
/// fake_bold: false,
/// fake_italic: false,
/// extra_data: (),
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/arabic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ impl From<&RawGlyph<()>> for ArabicGlyph {
small_caps: raw_glyph.small_caps,
multi_subst_dup: raw_glyph.multi_subst_dup,
is_vert_alt: raw_glyph.is_vert_alt,
ligature: raw_glyph.ligature,
fake_bold: raw_glyph.fake_bold,
fake_italic: raw_glyph.fake_italic,
variation: raw_glyph.variation,
Expand All @@ -96,6 +97,7 @@ impl From<&ArabicGlyph> for RawGlyph<()> {
small_caps: arabic_glyph.small_caps,
multi_subst_dup: arabic_glyph.multi_subst_dup,
is_vert_alt: arabic_glyph.is_vert_alt,
ligature: arabic_glyph.ligature,
fake_bold: arabic_glyph.fake_bold,
variation: arabic_glyph.variation,
fake_italic: arabic_glyph.fake_italic,
Expand Down
3 changes: 3 additions & 0 deletions src/scripts/indic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,7 @@ fn insert_dotted_circle(
small_caps: false,
multi_subst_dup: false,
is_vert_alt: false,
ligature: false,
fake_bold: false,
fake_italic: false,
variation: None,
Expand Down Expand Up @@ -2264,6 +2265,7 @@ fn to_raw_glyph_indic(glyph: &RawGlyph<()>) -> RawGlyphIndic {
small_caps: glyph.small_caps,
multi_subst_dup: glyph.multi_subst_dup,
is_vert_alt: glyph.is_vert_alt,
ligature: glyph.ligature,
fake_bold: glyph.fake_bold,
fake_italic: glyph.fake_italic,
variation: glyph.variation,
Expand All @@ -2283,6 +2285,7 @@ fn from_raw_glyph_indic(glyph: RawGlyphIndic) -> RawGlyph<()> {
small_caps: glyph.small_caps,
multi_subst_dup: glyph.multi_subst_dup,
is_vert_alt: glyph.is_vert_alt,
ligature: glyph.ligature,
fake_bold: glyph.fake_bold,
fake_italic: glyph.fake_italic,
variation: glyph.variation,
Expand Down
3 changes: 3 additions & 0 deletions src/scripts/khmer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ fn insert_dotted_circle(dotted_circle_index: u16, glyphs: &mut Vec<RawGlyphKhmer
small_caps: false,
multi_subst_dup: false,
is_vert_alt: false,
ligature: false,
fake_bold: false,
fake_italic: false,
variation: None,
Expand Down Expand Up @@ -606,6 +607,7 @@ fn to_raw_glyph_khmer(g: &RawGlyph<()>) -> RawGlyphKhmer {
small_caps: g.small_caps,
multi_subst_dup: g.multi_subst_dup,
is_vert_alt: g.is_vert_alt,
ligature: g.ligature,
fake_bold: g.fake_bold,
fake_italic: g.fake_italic,
variation: g.variation,
Expand All @@ -624,6 +626,7 @@ fn from_raw_glyph_khmer(g: RawGlyphKhmer) -> RawGlyph<()> {
small_caps: g.small_caps,
multi_subst_dup: g.multi_subst_dup,
is_vert_alt: g.is_vert_alt,
ligature: g.ligature,
fake_bold: g.fake_bold,
fake_italic: g.fake_italic,
variation: g.variation,
Expand Down
2 changes: 2 additions & 0 deletions src/scripts/syriac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl From<&RawGlyph<()>> for SyriacGlyph {
small_caps: raw_glyph.small_caps,
multi_subst_dup: raw_glyph.multi_subst_dup,
is_vert_alt: raw_glyph.is_vert_alt,
ligature: raw_glyph.ligature,
fake_bold: raw_glyph.fake_bold,
fake_italic: raw_glyph.fake_italic,
variation: raw_glyph.variation,
Expand All @@ -113,6 +114,7 @@ impl From<&SyriacGlyph> for RawGlyph<()> {
small_caps: syriac_glyph.small_caps,
multi_subst_dup: syriac_glyph.multi_subst_dup,
is_vert_alt: syriac_glyph.is_vert_alt,
ligature: syriac_glyph.ligature,
fake_bold: syriac_glyph.fake_bold,
variation: syriac_glyph.variation,
fake_italic: syriac_glyph.fake_italic,
Expand Down
1 change: 1 addition & 0 deletions tests/aots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ fn make_direct_glyph(glyph_index: u16) -> RawGlyph<()> {
small_caps: false,
multi_subst_dup: false,
is_vert_alt: false,
ligature: false,
fake_bold: false,
fake_italic: false,
extra_data: (),
Expand Down
1 change: 1 addition & 0 deletions tests/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub fn make_glyph(ch: char, glyph_index: u16) -> RawGlyph<()> {
small_caps: false,
multi_subst_dup: false,
is_vert_alt: false,
ligature: false,
fake_bold: false,
fake_italic: false,
extra_data: (),
Expand Down

0 comments on commit 1bf2db8

Please sign in to comment.