A <tspan>, <textPath> or <a> inside a <text> whose unicode-bidi is embed
now gives its characters its direction as their Character Direction. An
element with normal stays in its parent's embedding, unicode-bidi is not
inherited (inherit takes the parent's value), and the <text> keeps its
own paragraph direction. A run that SVG export writes with direction and
unicode-bidi="embed" now opens with its Character Direction again.
Character Direction holds one level of embedding and no override or
isolation, so bidi-override, isolate and isolate-override are imported
as embeddings in their direction, plaintext as an embedding in the
direction of its first strong character, and an embedding inside
another as an embedding of its own, each with an import warning.
unicode-bidi: plaintext on the <text> runs and anchors each paragraph
in the direction of its first strong character.
https://www.w3.org/TR/SVG11/text.html#RelationshipWithBi-directionalityhttps://www.w3.org/TR/css-writing-modes-3/#unicode-bidi
Character panel > Tsume, shown with Show East Asian Options, takes away a
percentage of the blank space on each side of a character's ink, inside
its own width, in horizontal type. The value is from 0 to 100, 0 by
default. 50 halves that space and 100 sets the ink against the
characters before and after it; the glyph keeps its shape. A character
without ink, such as a space, keeps its width, and a ligature or a
letter with its marks takes the value of its first character. The
font's pair kerning, tracking, manual kerning and Insert Aki are added
after it, and justification keeps it. Vertical type keeps its spacing.
- text.setFormat and text.setRangeStyle take `tsume`; a value that is
not a number or is outside 0 to 100 is refused, naming the key.
Character styles carry it, and a file holding a value outside the
range opens with the nearer end.
- The Character panel's Tsume field has 0, 25, 50, 75 and 100 in its
menu, and Reset Panel sets it to 0. Character Style Options shows it
in Advanced Character Formats.
- Live SVG text places each character Tsume moves, and type on a path
with Tsume is written as outlines.
Docs: docs/mcp.md (Tsume, Character Style Options fields),
docs/target-app-parity.md.
A glyph that justification widens to fill the line keeps its Insert Aki
before it at its width, and its own width, which ligature carets stand
in, now starts where the glyph is drawn. Optical kerning measures a pair
from the letter of a cluster rather than from a combining mark whose
advance holds the cluster's Aki.
The SVG test also checks centered and right-aligned lines with Insert Aki.
Characters take two new attributes, `akiLeft` and `akiRight`: space
before and after each character in reading order, in thousandths of an
em from 0 to 1000, measured as tracking is. `text.setFormat`,
`text.setRangeStyle` and character styles set them; values that are not
numbers or are outside the range are refused, and a file holding one
outside the range opens with the nearer end. Both are saved only when
not 0.
- Layout: Left is the space above a character in vertical type and on
its right in right-to-left type. Wrapping counts the space, a line's
end keeps it when aligned, and justification and Optical kerning
leave it at its set width. A ligature, a letter with its marks and a
tate-chu-yoko block take the values of their first character.
- With Fractional Widths off or System Layout on, the space before and
the space after a character are each set in whole points and added
to its rounded width, so justification keeps them at that width. A
tate-chu-yoko block rounds the space above and below it the same way.
- Character panel: with Show East Asian Options on, Insert Aki Left and
Insert Aki Right fields with 0, 250, 500 and 1000 in their menus;
Reset Panel sets both to 0.
- Character Style Options › Advanced Character Formats shows Insert
Aki Left and Insert Aki Right, with the Character panel's 0, 250, 500
and 1000 in their menus; a typed value below 0 or past 1000 sets 0 or
1000, and Reset Panel unsets both.
- SVG export: live text places each character the space moves, and
type on a path with Insert Aki is written as outlines.
Docs: docs/mcp.md, docs/target-app-parity.md.
- drawn_alike ignores kerning_before, so a space before the "f" of "fi"
no longer splits the ligature.
- SVG export takes a line's starting space from kerningBefore and the em
shaping sets the character in (vectorcraft_text::line_start_space),
not from where the glyph is drawn, which can include a shaping offset;
the reader layout for justified lines leaves the space out, so those
lines start there too.
- Type on a path writes the space as the first character's dx; a space
after a line break, which <textPath> can't write, is outlined.
At the Type tool's caret, kerning goes on the character before the caret.
At the start of the text there is none, so Alt+arrows, the Kerning field
and Reset Tracking and Kerning did nothing; at a later paragraph's start
the kerning landed on the invisible line break.
CharStyle::kerning_before (kerningBefore) is the space before a character
that starts a line by itself: the first character of the text, of a
paragraph, or after a forced line break. Shaping gives its cluster the
space and draws it that much later along the line (right to left the
space is at its right; in vertical type it moves down the column). The
caret at such a line's start sets it (edit::caret_kerning_target), in
type.step, type.resetTracking and the Character panel; text.setRangeStyle
takes it. SVG export starts the line where the character is drawn. The
Eyedropper and character styles leave it alone.
In horizontal type, tracking and manual kerning are now thousandths of
the em as the Horizontal Scale draws it: at 50%, a tracking of 100 adds
0.05 em after each character. They were added at the full em while the
glyph advances took the scale, so narrow tracked type drifted from the
page of users' .ai files letter by letter. That page writes such type's
character spacing as the tracking itself in a text space as wide as the
scale, which PDF scales by it (ISO 32000-1 9.4.4). Vertical type keeps
the full em: its advances down the column don't all take the scale.
vectorcraft_text::spacing_scale gives the factor. Shaping, inline
graphics and the Optical pairs use it, and so do the conversions from
points back to thousandths of an em: the automatic kerning the Character
panel reads and Touch Type's limits (glyph_em), Combine Point Text, and
PDF import, whose tracking is now the file's Tc. Scale Text Attributes
now keeps tracked type where a horizontal stretch drew it.
SVG export writes letter-spacing in thousandths of the font size it
writes (the vertical scale), so uniformly scaled type keeps its spacing
in a viewer.
Type with both a Horizontal Scale other than 100% and tracking or manual
kerning lays out differently than before, in every document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Features take a value (tag=N) in style commands, SVG and native files. Runs that
differ only in their feature lists are shaped in one buffer with ranged features, so
an alternate on one letter of an Arabic word keeps the word joined. FontFace reads
GSUB once (alternate sets, singles, the feature reaching a glyph), names unencoded
glyphs, and alternates_in_context finds a character's alternates in its word.
text.setRangeStyle takes glyphAlternate: null.
* CI: line tables only for workspace crates, no debug info for dependencies
* CI: one integration-test binary per crate
Each crate's tests/*.rs files become modules of tests/it/main.rs. Files that change
process-wide state (Color Settings, the font database, installed plug-ins, the working
directory), the fuzz sweeps and the test targets the Affinity corpus workflow names stay
their own binaries.
* Docs: name the integration tests' new paths
* CI: xtask test with nextest and doctests, one wasm check, clippy --keep-going
cargo xtask ci runs the tests with cargo nextest (cargo test when it is not installed),
builds them with --lib --bins --tests, runs the doctests as their own step, and checks
the wasm crates in one cargo check --keep-going call.