Right-to-left type on a path that overflows searched for the start of its
text that fits with a binary search, which assumed that a longer start
never fits once a shorter one does not. A letter tracked to a negative
advance breaks that: with steps of 6, 6, -3 and 6 points in a 10 point
span, the first three letters fit while the first two do not, and the
search showed only the first letter.
- Without hanging punctuation, and with no spacing or on a path of one
straight segment, every start is measured in O(n log n): each glyph
keeps its place in the whole text's visual order, except the trailing
whitespace and controls that UAX #9 rule L1 sets to the paragraph's
level.
- With hanging punctuation, or spacing on a curve, when a letter has a
negative advance, a start is a candidate only when its glyphs, in its
own visual order and each at the least step it can take, reach no
farther than the path's length. This lower bound holds with negative
advances.
- At most 32 candidates are arranged from the longest. When none of
them fits, a binary search over the rest shows a start that fits,
which is not always the longest.
- With hanging punctuation, or spacing on a curve, text with no negative
advance keeps the binary search.
Right-aligned and centered type on a curve with Spacing was placed using
the width its glyphs have at the start bracket, while the steps were
measured where the line was placed, so a line that fits could end past
the end bracket and lose glyphs. It is now placed using the width of the
steps it has where it is placed.
A right-to-left line draws the text before a tab after it, but its width
was measured in the order of its text. A right-aligned line such as
"אב גד<tab>הו זח" with a stop at 150 pt in a 300 pt frame ended 5.59 pt
past the frame's right edge, and right, center and decimal stops aligned
the text before the tab instead of the text drawn after it.
- The line's width is measured in drawing order, each tab up to its stop
from where the glyphs drawn before it end. Right, Auto and centered
lines are aligned by that width. Stops are still measured from the
left edge.
- Right, center and decimal stops align the text drawn after the tab.
- Area type breaks a line with a tab where it fits as drawn, and wraps a
line that fits only in the order of its text. Once the line holds a
tab, it reads on while the least it can be drawn at fits, then ends at
the latest break that fits as drawn: up to 256 breaks are measured
from the latest back, and earlier breaks without a hyphen are
bisected. Hyphenation points are measured as drawn the same way, so
the line hyphenates at a point that fits.
- Only lines of horizontal paragraphs with right-to-left text keep a
list of their break candidates.
- A tab that ends a wrapped right-to-left line is drawn before the line
at its own width instead of pushing the line to the right.
Docs: docs/target-app-parity.md.
A threaded story measured each type-on-a-path frame, and each area frame
whose lines align to the center, the bottom or justify vertically, with
the whole rest of the story. A story of 1,500 one-line paragraphs through
40 such frames was laid out over about 2.1 million bytes while the frames
were measured.
- Area type without Shrink Text to Fit now takes the paragraph probe for
every vertical alignment. A reflow pass that places less text is
undone, so when the final layout stops before the last paragraph break
measured, every pass placed the same lines as with the whole rest.
- Type on a path takes it when its line starts at the start bracket: no
Dynamic Text, Optical Margin Alignment or Roman Hanging Punctuation,
the first paragraph aligned Left, Justify Left or Auto, and vertical
type or no right-to-left text in the rest of the story. The story is
scanned for right-to-left text once per distribution.
- Shrink Text to Fit, the other path settings and a share that ends in
the story's last paragraph are still measured with the whole rest.
The same 40 frames are now measured over 21,035 bytes (aligned to the
center) and 20,583 bytes (paths aligned Left). Each frame's share is
unchanged: the tests compare every slice with the whole-rest result for
both distribution entry points, across alignments, frame shapes, wraps,
path settings, right-to-left text, styles, lists and seeded stories.
A paragraph whose characters differ in line metrics (type size, font,
leading, vertical scale, or an inline graphic taller than the text) was
broken with the Single-line Composer in area type of a shape other than
a rectangle, or with text wrap. The every-line composer now composes it
there too.
- The composer takes each line's width from a measure that follows
where the lines above it went (`composer::Measure`,
`compose_with`). The fixed widths by line number are one such measure.
- In a frame whose lines are not all as wide, each line the composer
tries is placed from the pen the lines above it left, at its own
tallest metrics, as the line loop places it (`layout/shaped.rs`).
Starts with the same placement are shared, and each start keeps the
placements it was tried with.
- A line too long for its room does not end the search from its start
while a glyph after it could make the line taller and set it lower,
where the frame may be wider.
- The line loop places a composed line in such a frame at the line's
tallest metrics, so it gets the room the composer measured for it.
- The search for one paragraph stops after 1,000,000 lines tried or
2,000,000 baseline steps. The searches of all the paragraphs of one
frame share a budget of 2,000,000 lines and 4,000,000 steps, kept
apart from the line loop's own steps. A paragraph whose search stops
is broken line by line. A paragraph the frame has no room for is
broken line by line too, and overflows as before.
Tests: crates/text/src/tests/combos.rs (a stepped frame, a frame that
widens, text wrap with two spans on a baseline, overflow, a long
paragraph) and crates/text/src/layout/wrapping_tests.rs (the composer
with a measure, and the shared search budget).
A selection inside a warichu block was highlighted across the whole
line of text, so selecting a character of the upper block line
highlighted the lower one too, and a selection from one block line to
the next spanned the wrong characters. Each block line now gets its own
band, out to halfway to the block lines next to it, with one highlight
over each run of selected characters drawn side by side on it. A
selection that runs past the line still covers the line break.
Glyphs now carry their block line, so the characters of one block line
stay together for the highlight and for a click whatever their own
Character Alignment.
- Insert Kashidas skips the characters of a warichu block.
- Blocks whose characters the bidi order draws in turn keep where each
block starts.
- A click on a block line goes to that line's characters.
- Digits marked Warichu in vertical type on a path stay across the column.
- A block of several fonts gives its line the tallest ascent and the
deepest descent among them.
- Warichu Settings on characters not marked Warichu do not split shaping.
- Warichu Settings leaves a value outside its range as it is unless it is
edited.
The Character panel menu has Warichu and Warichu Settings… with Show East
Asian Options. Warichu characters are set at a percentage of their size
and stacked on 1 to 6 lines inside their line: one above the other in
horizontal type and side by side in vertical type. Warichu Settings sets
Lines, Line Gap, Scale and Alignment.
- text.setFormat and text.setRangeStyle take warichu, warichuLines,
warichuGap, warichuScale and warichuAlign; ui.warichuSettings opens
the dialog.
- A stretch of warichu characters is one block that stays whole on one
line. The canvas and every export draw the stacked lines; SVG, PDF and
the .ai writers write such type as outlines, as they do ruby. Type on a
path sets the characters at their own size.
- The attributes are saved natively, carried by character styles and
translated in every complete catalog.
Docs: docs/mcp.md, docs/type-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.
Paragraph panel menu > Bunri-Kinshi (with the East Asian options) and
text.setFormat {bunriKinshi} turn it on per paragraph. A line then
never breaks inside two em dashes, two horizontal ellipses, two two-dot
leaders or the two halves of a vertical kana repeat mark, the pairs W3C
JLREQ lists for class cl-08 (https://www.w3.org/TR/jlreq/#cl-08), nor
inside a chain of em dashes. Both composers, vertical type, Kinsoku
Shori Type, hyphenation and No Break follow it, and a pair wider than
the line stays whole on it. Justification and letter spacing add no
space inside a pair.
The setting works only with a Kinsoku Set: with Kinsoku Set None the
paragraph keeps the value without effect and the menu item is
unavailable. It is off by default, saved with the paragraph only when
on, carried by paragraph styles (Paragraph Style Options > East Asian
Composition Settings), and turned off by Reset Panel. A value that is
not true or false is refused. The .ai editing data has no field for it,
so saving reports a paragraph with it on and a pair in it as left out.
Docs: docs/mcp.md, docs/target-app-parity.md.
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.
Korean text broke after every Hangul syllable. A paragraph now has
Korean Line Break Anywhere, set from the Paragraph panel menu (with the
East Asian options), from the East Asian Composition Settings section of
Paragraph Style Options, and with `text.setFormat {koreanLineBreakAnywhere}`.
- Off, a word that contains Hangul (the characters between spaces) breaks
only at its spaces and after East Asian punctuation, or where the word
is wider than the line. Both composers follow it, and the word takes no
hyphen. On, a line may break between any two syllables, as before.
- New type has it off. Paragraphs and text objects saved before the
option read as on, so their lines do not move; the value is saved only
when it is off.
- Paragraph styles carry it, Reset Panel turns it off, and the .ai writer
reports it as lost for Korean paragraphs.
The help page: https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/set-line-breaks-korean-text.html
The command is described in docs/mcp.md, and docs/target-app-parity.md
lists the option.
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.
Type › Dynamic Text (`type.dynamicText`, also on the Contextual Task Bar
for type on a path) scales the text by one factor so it fills the span
between its start and end brackets, and the layout fits it again after
each edit to the text, the path or a bracket. The first character's
size moves in 0.1 pt steps within 0.1 to 1,296 pt. Turning it off, or
threading the type, keeps the sizes it was drawn at.
- The model saves `dynamicText` only when on.
- SVG, the legacy .ai writer and the .ai editing data write the drawn
sizes through `vectorcraft_text::drawn`; the editing data warns that
the setting is not written.
- Shrink Text to Fit and Dynamic Text step the size the Character panel
shows when a size measures the cap height, x-height or ICF box.
Docs: docs/mcp.md, docs/type-parity.md, docs/target-app-parity.md.
Part of #4134.
A centred or right-aligned line of tracked type was measured with the
tracking after its last character, so it sat half that tracking
(centred) or the whole tracking (right-aligned) left of where the page
of users' .ai files draws it. That page aligns a line as wide as its
characters without that tracking.
Shaping now records the tracking each glyph's advance holds
(SGlyph::tracking), and a line that isn't justified leaves its last
character's out of the width it is aligned by. The last character is
the last one the line draws before a paragraph's end, a forced line
break or the spaces a wrap leaves; spaces or a tab typed before a
paragraph's end or a forced line break keep the letter's tracking, as
the page does. Forced line breaks and wraps aren't in the files: they
follow the rule so that turning a wrap into a paragraph or a forced line
break keeps the line in place. Hyphenated lines, lines whose last
character hangs, vertical and right-to-left lines and type on a path
keep it. The tracking stays in the glyph's advance, so the caret,
selections, line breaking and Fit Headline are unchanged.
LineInfo::trailing gives the amount. Right-aligned area type sets the
text after its last tab the same way.
What aligns by line widths follows. Convert To Area Type makes its
frame end where the lines are aligned, with room for the tracking, so
tracked type converted either way stays in place. Affinity import lines
up differently aligned paragraphs of artistic text by the aligned
widths. SVG import moves a centred or right-aligned letter-spaced line
back by its share of the last letter spacing, so it stays where a reader
draws it and SVG export, which anchors lines at the end of their
advances, reads back unchanged.
Centred and right-aligned tracked type lays out differently than before
in every document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a vertical path, an upright glyph is now placed in its column as
vertical point type places it, then the column is centered on the path.
- Its cell starts at its place on the path; it started 0.12 em earlier
with fonts whose cell top is less than a full cell above the baseline.
- A baseline shift moves it across the column instead of along the path.
- A horizontal scale widens it evenly about the column's center.
- A combining mark stays on its upright letter.
- The caret and the selection highlight span the column, half its em on
each side of the path, instead of the font's ascent and descent.
With Skew, 3D Ribbon and Stair Step on a path that runs to the left,
letters are a quarter turn counterclockwise from upright glyphs: Skew and
3D Ribbon mirror both kinds there, and Stair Step keeps letters level. The
test, the progress doc and the params text of Tate-chu-yoko Settings now
state these conditions and the block's directions on a path.
Follows #5557. Letters and digits lying on their side in vertical type on
a path kept their top toward the path's end, opposite the upright glyphs.
They are now set as horizontal type on the path sets them: baseline along
the path, top to the path's left, a quarter turn clockwise from upright
glyphs (CSS Writing Modes 3, Unicode UAX #50).
- The column is centered on the path: upright glyphs, letters on their
side and tate-chu-yoko blocks have their em box centers on the path
line instead of a column one em wide to the path's right.
- Upright glyphs are turned in the path effect's glyph space, so with
Skew on a curve their top runs back along the tangent and their x axis
stays up the page, as letters keep theirs.
- Tate-chu-yoko blocks on a path stand upright, stay whole on a curve and
move by Tate-chu-yoko Settings toward the path's start and left.
- A tate-chu-yoko block of mixed sizes sets every glyph in its first
character's em, on a path and off it.
Existing vertical type on a path redraws with these placements.
Proportional Metrics changes an upright glyph's cell on a vertical path, and its width runs along that changed cell. Its font ligature carets were still divided by the unchanged cell, so the caret, a click and the selection missed the drawn stop. They now divide the same length the width uses.
In vertical type on a path, every glyph was turned a quarter turn with
its top toward the path's end. An upright glyph's characters then read
backwards along the path, so a ligature's font caret other than its
middle did not line up with the glyph as drawn.
An upright glyph whose top faces the path's end now turns half a turn
about the centre of its cell, so its top faces the path's start. It
keeps the same stretch of the path. Glyphs lying on their side, and
upright glyphs that the Skew, 3D Ribbon and Stair Step effects already
turn this way on a path running to the left, are unchanged. The ligature
stops follow the top of the cell, so the caret, a click and the
selection meet the drawn caret line.
An envelope mapped type from its laid-out box: ascent to descent for
point type, the frame for area type. The distorted glyphs then filled
less of the envelope than other art does, with room above the capitals
and below the baseline.
The envelope now maps type from the bounds of its glyph outlines,
underlines and strikethroughs when the type is on its own, in groups
and layers, or used as a clipping path. Inline graphics count by their
boxes. Rotated type and type in perspective map from their outlines as
they are drawn. Make with Mesh,
Make with Top Object and Reset with Mesh without Maintain Shape use
those bounds. Type that draws nothing keeps its laid-out box, and so
does type inside any other object, such as a blend, a repeat or a
symbol instance. Shrink Text to Fit type is measured as it is laid out
on the page.
vectorcraft-text's envelope_ink measures the type, and
register_envelope_ink registers it with the document crate. The engine
session, Renderer::new, live evaluation in vectorcraft-effects and the
.ai reader register it. The document crate keeps the measured boxes per
thread, by object and by a hash of the content, and measures again when
the type, the fonts, the hyphenation exceptions or the transform's
scale or rotation change.
This is VectorCraft's own rule. Envelopes of type made in VectorCraft
and in saved files map from the glyph outline bounds instead of the
type's laid-out box, so they redraw differently.
Layout kept no record of a run's face, so each paragraph looked up the face
of its first character three times for its line metrics and once more to
shape it. The layout now keeps each run's face the first time it is looked
up and uses it for shaping, for paragraph metrics, for on-path type's empty
line and for the column offset of vertical point type. Glyphs, positions,
bounds and baselines do not change.
A test-only helper lays text out looking every face up at each use, and a
new test requires point, vertical point, area and path type laid out both
ways to match exactly.
The layouts of 5,000 short point type objects take a median of 215 million
CPU cycles of the laying-out thread instead of 236 million in a release
build (11 alternating runs of each build on Windows 11, measured while
other builds were running).
Paint-only harakat runs stay inside one shaping cluster so letters keep
their joining forms and marks their attachment. When a character-level
shaping pass matches the monotone glyphs, a glyph may take a later
in-cluster character's run only if GDEF classifies it as a mark (class 3)
and that character is a harakat; otherwise ownership stays at the cluster
start. Conservative: a mark may miss its colour; a base letter must never
take a mark's. Fonts without GDEF and AAT faces fall back the same way.
Insert Kashidas tatweels take the base letter's paint, not a trailing
mark's. Private harakat module; type-parity, target-app-parity and roadmap
M7 evidence updated (scores unchanged). Tests cover paint_only / ranges /
own_chars (wide mark, equal advance, multi-component, single-glyph
ligature, review counterexample), colour, hide, repeated glyph ids,
kashida, forced mark-last emit_kashida, and installed fonts.
- Auto leading stays auto: each line takes the paragraph's share (key 7)
of the largest type on it. The leading was the first two lines'
distance for every line, so 10 pt lines under a 15 pt heading came in
18 pt apart.
- The paragraph's hyphenation options (keys 10 to 15) are read.
- Area type of one frame whose last lines the file sets, but which don't
fit here (the fallback font, or a descent past the frame's bottom),
gets a frame taller from its bottom (vectorcraft_text::fit_source_lines),
with a warning.
A user's .ai whose type differed from its page by 6.6%, so that it opened
from its PDF part with each line its own text object, now opens from its
layers.
Baseline shift on an upright glyph in vertical type moved it up the column
instead of across it, at any Character Rotation, because the shift was
applied before the glyph was turned upright. The shift is now taken off
before the turn and put back after it, so an upright glyph moves across the
column by the shift as a letter lying on its side does, and still turns
about its shifted pen position.
On a path, vertical type turns an upright glyph about its pen, so its cell
runs from 0.12 em before the pen to 0.88 em after it without the font's
vertical metrics. The ligature caret, a click and the selection measured
the cell from the pen instead. They now take the cell where the glyph's
transform draws it, with its placement, baseline shift, Horizontal Scale
and Skew.
A manual kerning value on an inline graphic's character was stored and
shown but did not move the text after it. The graphic's advance now
includes the value, as a letter's does, and in right-to-left horizontal
type its art moves right by the value, so the space goes between it and
the next character in reading order. Auto and Optical kerning still apply
no font pairs to an inline graphic.
Right-to-left manual kerning was added to the glyph's offset, which
Character Rotation turns, so a rotated character with kerning moved
across the line (up at 90 degrees) instead of away from the next
character in reading order. The shift is now applied to the pen
position along the baseline, outside the rotation, and the rotation
turns the glyph about that moved pen position.
The Paragraph panel menu (with the East Asian options) gains Kinsoku Shori
Type, and text.setFormat takes kinsokuType. Where the Kinsoku Set keeps a
character off the start of the next line or the end of this one, Push In
First sets it on the line and closes the line's gaps next to CJK
characters to fit, each by at most a tenth of an em of the character
before it; a line that cannot close up that much, or that holds a tab,
pushes out. With Optical Margin Alignment or Roman Hanging Punctuation
on, the gaps that hanging punctuation keeps open are not counted. Push
Out First pushes in only when pushing out would leave the line more than
an em short. Push Out Only, the default, is the line breaking area type
had before, so existing documents lay out unchanged. The setting does
nothing with Kinsoku Set None, is saved only when it is not Push Out
Only, and paragraph styles carry it. Reset Panel sets it back to Push
Out Only.
Docs: docs/mcp.md, docs/type-parity.md, docs/target-app-parity.md.