Repository navigation
Support direction and unicode-bidi on text - #1184
Open
matthargett wants to merge 3 commits into
Open
matthargett wants to merge 3 commits into
matthargett wants to merge 3 commits into
Conversation
matthargett
force-pushed
the
rtl-direction
branch
from
October 21, 2026 20:36
0ba954c to
639cef6
Compare
`direction` was never read: every chunk was laid out with a left-to-right base level, and `text-anchor` `start`/`end` always meant left/right. - `direction` on the `text` element is the base level of the bidi algorithm for its chunks. - `start` and `end` anchors follow the `direction` of the element a chunk starts in, also on a text path, where right-to-left text ends at `startOffset` for `start`. - The text position a following chunk continues from does not depend on the anchor or direction of the chunk before it, but on the direction of the paragraph. - `unicode-bidi` `embed`, `bidi-override`, `isolate`, `isolate-override` and `plaintext` become the Unicode formatting characters CSS defines for them, added only for the analysis. They nest like the elements and are opened again in a chunk that starts inside one. - The bidi analysis runs once for a chunk instead of once per span font. - A vertical writing mode ignores both properties, as before. - The writer keeps them with `preserve_text`, including the direction of a chunk that starts inside a `unicode-bidi` element of another direction. Fixes linebender#475.
The references of `direction/rtl`, `unicode-bidi/bidi-override` and `color-font/mixed-text-rtl` were rendered with the property ignored: the text ran off the right edge from `x`. New tests cover anchors, inheritance, `direction` on a `tspan` and on the root `svg` (the example from linebender#475), a `y`-only chunk after a `tspan` with `direction`, mixed scripts, end-of-sentence punctuation, numbers and date ranges (Western and Arabic-Indic digits), nested parentheses and brackets, scientific text (units, signs, Greek letters), decorations, word spacing, a drop shadow, `clip-path`, `mask`, and `embed`, `bidi-override`, `isolate`, `isolate-override` and `plaintext`. Each was compared with Chromium 131 and Firefox before its reference was made.
matthargett
force-pushed
the
rtl-direction
branch
2 times, most recently
from
October 21, 2026 22:22
0ba954c to
f2aa1cd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #475.
directionwas never read: each text chunk was laid out with a left-to-right base level, andtext-anchorstart/endalways meant left/right.unicode-bidiwas ignored too. Both were listed indocs/unsupported.md.What it does
directionon thetextelement (or an ancestor, including the rootsvg) is the base level of the bidi algorithm for the chunks of that text.text-anchorstartandendfollow thedirectionof the element a chunk starts in, also on a text path: right-to-left text ends at astartanchor, and atstartOffseton a path. Base level and anchor come from different elements, as in the browsers (direction="ltr"on atspaninside an rtltextflips the anchor, not the reordering).unicode-bidiembed,bidi-override,isolate,isolate-overrideandplaintextbecome the Unicode formatting characters CSS defines for them. They are added only for the analysis and removed from the runs, so character indexes (dx,dy,rotate) do not move. Scopes nest like the elements; a chunk that starts inside one opens it again.rtl-with-vertical-writing-modeis unchanged).y-only chunk continues from is where the previous chunk would end if it were anchored at its natural start and ran in the direction of the paragraph (as in Chromium, Firefox and WebKit), not where its own anchor and direction put it. Left-to-right chunks in left-to-right text keep the existing behaviour.preserve_text, including the direction of a chunk that starts inside aunicode-bidielement of another direction. New public API:usvg::Direction,Text::direction,TextChunk::direction.Evidence
Every new reference was compared with Chromium 131 and Firefox before it was made (same fonts in all engines through
@font-face, ink positions within 1 px). For 15 of the 21 new reftests Chromium and Firefox both match resvg's ink exactly (1 px). The other six differ in drawing or shaping, not layout:rtl-with-filter(blur kernels),rtl-with-text-decoration(Chromium skips ink under underlines; Firefox matches),rtl-with-clip-path(Firefox draws aclipPathwith text slightly differently; Chromium matches)on-svg-element(the Hebrew comes from a different font in each engine), andrtl-with-numbersandrtl-with-scientific-text, where Chromium spaces the Arabicوslightly differently (Firefox and WebKit match resvg to within 2 px). WebKit is shown where it agrees; it lays out right-to-left text on a path differently from the other two.The example from #475 (visual order is now
כתובת MAC: 00-24-AF-2A-55-FC, right to left; the browsers use a different serif for the Latin letters):WPT
text-bidi-controls-anchors-2(direction="rtl"on ag), with one font in every engine: ink mismatch against Chromium / Firefox / WebKit went from 3,600 / 3,601 / 3,577 px to 0 / 0 / 0.Anchors, inheritance and
tspan:Decoration, word spacing, drop shadow,
clip-path(on the text, and aclipPathwith a text child) andmask:Punctuation at the end of sentences, number and date ranges (Western and Arabic-Indic digits), nested parentheses and brackets, and scientific text (units, signs, Greek letters,
p < 0.05,Na+ / Cl−):Text on a path (rendered with #1166 applied; see Tests):
unicode-bidi:Tests
tests/text/directionandtests/text/unicode-bidi. Existing references are untouched except the three below.direction/rtlandcolor-font/mixed-text-rtl(the line started atxand ran off the right edge) andunicode-bidi/bidi-override(the override was ignored). No other test usesdirectionorunicode-bidi; the complete suite passes (1,778 reftests,cargo test --all --release).usvgtests: inheritance, anchors, the text position a following chunk continues from, anchors on a text path (rtlstartequals ltrend, and so on, which does not depend on the arc-length accuracy), vertical writing mode, override order, and ato_stringround trip.main's 0.5 arc-length accuracy they would have to change again when Improve text-path positioning accuracy #1166 lands. I'll add them after it merges.directionandtext-anchorones round-trip throughto_stringwithpreserve_textto identical glyph positions.tspans with randomunicode-bidi,direction,x, Arabic and Hebrew text and text paths: no panic, no non-finite position, and the same glyphs as without the attributes (except on a path, where moved glyphs leave it).Not changed
letter-spacingin an rtl paragraph still drops the spacing after the visual-last glyph (the browsers keep it after the logical last), and Chromium spaces Arabic letters where Firefox and resvg do not. No test combines them with rtl.dxis not part of the chunk extent used formiddle/end/rtlstartanchoring. Chromium counts it. It differs for ltr text onmaintoo.textelement.