Skip to content

Preserve cursive shaping under letter spacing - #713

Closed
waywardmonkeys wants to merge 2 commits into
linebender:mainfrom
waywardmonkeys:parley/cursive-letter-spacing
Closed

Preserve cursive shaping under letter spacing#713
waywardmonkeys wants to merge 2 commits into
linebender:mainfrom
waywardmonkeys:parley/cursive-letter-spacing

Conversation

@waywardmonkeys

Copy link
Copy Markdown
Contributor

CSS Text requires user agents that cannot perform cursive elongation to avoid inserting gaps between a cursive script’s typographic letter units. Such text is effectively treated as one tracking unit per word, while spacing can still apply to non-cursive content such as spaces. Parley instead added letter_spacing to every shaped cluster, which broke Arabic and other cursive connections.

Use the resolved-script fact retained by ShapedRun to suppress letter spacing for non-space clusters in scripts with joining characters. Spaces still receive both letter_spacing and word_spacing, and non-cursive runs remain tracked normally. Because the decision uses the resolved script rather than the presence of a Join_Causing character, a Latin run containing U+200D ZERO WIDTH JOINER remains trackable.

CSS Text and CSS Fonts also specify that non-default letter spacing disables optional ligatures, while low-level font-feature-settings take precedence. Add default liga=0 and clig=0 shaping features only when tracking is actually applied and the author did not supply those tags. Required and cursive shaping features remain untouched.

References:

Add coverage for default and explicitly authored ligatures, cursive words and spaces, and Latin text containing ZWJ. Extend parley_bench with nonzero-letter-spacing cases so this path remains measurable.

Final Tango comparisons against the parent commit were flat within run-to-run noise for tracked Arabic (+0.03% and -0.64%) and Latin (+0.44% and -0.97%). Tracked Japanese measured +2.79% and +4.19% because shaping now processes the two required optional-ligature overrides; the zero-spacing suite showed no stable regression across repeated runs.

Layout policy needs to distinguish scripts whose letters participate in cursive joining without treating every `Join_Causing` character as evidence of a cursive script. In particular, U+200D ZERO WIDTH JOINER has `Joining_Type=Join_Causing` and can occur in Latin or emoji text.

Pack `Joining_Type` into `parley_data::Properties`, and generate a sorted lookup of ISO 15924 tags for scripts containing `Left_Joining`, `Right_Joining`, or `Dual_Joining` characters. Excluding `Join_Causing` and `Transparent` keeps `Zyyy` from being classified solely because it owns ZWJ and inherited marks.

Expose `script_has_joining_characters` in `parley_engine` for its `parlance::Script` vocabulary, and retain the resolved `script` in each `ShapedRun`. `Item` and `ShapedRun` derive their convenience accessor from that script instead of caching duplicate state, so public record construction remains straightforward and there is no script/classification invariant to maintain.

The lookup deliberately reports a Unicode fact. It does not inspect a particular string or decide letter-spacing policy.
CSS Text requires user agents that cannot perform cursive elongation to avoid inserting gaps between a cursive script’s typographic letter units. Such text is effectively treated as one tracking unit per word, while spacing can still apply to non-cursive content such as spaces. Parley instead added `letter_spacing` to every shaped cluster, which broke Arabic and other cursive connections.

Use the resolved-script fact retained by `ShapedRun` to suppress letter spacing for non-space clusters in scripts with joining characters. Spaces still receive both `letter_spacing` and `word_spacing`, and non-cursive runs remain tracked normally. Because the decision uses the resolved script rather than the presence of a `Join_Causing` character, a Latin run containing U+200D ZERO WIDTH JOINER remains trackable.

CSS Text and CSS Fonts also specify that non-default letter spacing disables optional ligatures, while low-level `font-feature-settings` take precedence. Add default `liga=0` and `clig=0` shaping features only when tracking is actually applied and the author did not supply those tags. Required and cursive shaping features remain untouched.

References:

- CSS Text Level 3, Tracking and Cursive Scripts: https://www.w3.org/TR/css-text-3/#cursive-spacing

- CSS Fonts Level 4, Feature and variation precedence: https://www.w3.org/TR/css-fonts-4/#feature-variation-precedence

Add coverage for default and explicitly authored ligatures, cursive words and spaces, and Latin text containing ZWJ. Extend `parley_bench` with nonzero-letter-spacing cases so this path remains measurable.

Final Tango comparisons against the parent commit were flat within run-to-run noise for tracked Arabic (+0.03% and -0.64%) and Latin (+0.44% and -0.97%). Tracked Japanese measured +2.79% and +4.19% because shaping now processes the two required optional-ligature overrides; the zero-spacing suite showed no stable regression across repeated runs.
@waywardmonkeys

Copy link
Copy Markdown
Contributor Author

This builds on top of #712. It was done with GPT 5.6, Sol, xhigh.

It exists because I figured there should be a use case in tree for what I needed from #712. My code does not rely on this at all as it isn't using parley itself.

@waywardmonkeys

Copy link
Copy Markdown
Contributor Author

This causes problems for Tom, so I will not pursue this.

@waywardmonkeys
waywardmonkeys deleted the parley/cursive-letter-spacing branch July 26, 2026 03:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant