In a frame that is not a rectangle, or with text wrap, layout moves a
line down to the first span wide enough for its first word. The word
was measured at full width before Mojikumi set an opening bracket at
the line's start flush, and with its last character at full width, so
a word that fit as set moved down to a wider line. It also ended at any
break opportunity, so an opening bracket counted without the character
kinsoku keeps with it and could be placed alone on a line too narrow
for that character.
- word_width ends the word only where break_line may end a line: at a
cluster end that kinsoku allows, not inside a No Break run, whose
spaces count. It leaves out the spaces at the word's end and narrows
its last glyph by the Mojikumi line-end credit.
- place_flow_line measures the word after the opening-bracket trim, and
the Every-line Composer's first-line placement takes off the same
start credit, so the composed widths match the placed lines.
With Line-end Punctuation Half Width, a closing bracket, comma or full
stop ending a line is set half width, but both composers measured it at
full width. A closing bracket that fit only at half width went to the
next line with the character before it, and so did a comma or full stop
with Burasagari None.
The single-line and every-line composers now measure each candidate
line with its last character set as narrow as Mojikumi sets it, using
the same rule the line is drawn with. A mark that already lost its half
to the punctuation after it and a line ending in a hyphen take nothing
more off, and the quarter em between Japanese and Latin text counts only
where the line ends. Kinsoku Shori Type measures the pushed-in and
pushed-out lines the same way. Right-to-left lines and Mojikumi None
break as before.
A test font sets ideographs and Japanese punctuation one em wide, so
the layout tests run without system fonts.
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>
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>
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.
Shaping split a cluster wherever two characters' styles differed, so
setting Standard Vertical Roman Alignment or a tate-chu-yoko offset on the
f of "fi" in horizontal type broke the ligature. Horizontal type does not
read those settings, so they no longer split a cluster there. Vertical
type still shapes such characters apart.
Kinsoku push-in room does not count a gap after inline art or inside a list marker, but the squeeze also closed those gaps. The squeeze now applies only to the gaps the room counts.
- A file read for opening is laid out in the composite fonts registered
then. Opening a document and reverting the front one now measure again
the type set in a font named like a composite font that an open document
defines, the font database holds or the session registered earlier. Type
in other fonts, missing ones included, keeps the bounds it was read with.
- Switching to a document or closing the front one measures again the type
set in a composite font whose definition changed, and any type without
bounds.
- A document reverted while in the back measures its type in composite
fonts when it comes to the front, not in the front document's.
- Text holding an inline graphic whose symbol's type was measured again
takes the graphic's new size.
- A failed or panicked command puts the font database's composite fonts
back with the documents.
- Setting a composite font to the definition an export lends keeps it after
the export ends.
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.
Three tests failed on a loaded machine because they asserted elapsed
time. Each now counts the work that the slow behavior it guards
against would repeat, with a counter compiled only into tests:
- The CSS cascade test counts selector tests, at most two per element
(testing every rule against every element tests N x N). It styles
5,000 elements, so the slow behavior fails on the count rather than
on the hang guard, which now waits 300 s.
- The stacked-glow test counts the silhouettes drawn, 30 for 30 glows
with and without the shadow cache.
- The Zstandard frames test counts the bytes looked at to find the
zero padding.
The hang guards for invalid dash patterns, text layout and script
checks of a pipe wait 300 s instead of 20 s.
Reuse resolved features and borrowed GDEF classes for mark ownership,
skip the character-level pass without eligible mark glyphs, and reuse
paint-only boundary classification. Compare non-paint style attributes
without cloning while preserving the previous PartialEq behavior.
Add deterministic font variants and a field-by-field style oracle
to preserve glyph geometry, conservative ownership and NaN handling.
Opening a document laid out every type object for its bounds and
baselines and threw the layouts away, and the first frame laid the same
objects out again. The open now keeps them, up to 128 MiB, and a frame
takes each one once.
A frame takes a layout only for a text object equal to the one laid out,
and only while the fonts, the system fallback setting and the hyphenation
exceptions are unchanged. When the last frame being drawn ends, the rest
are dropped if a frame took one, and all of them are dropped after eight
frames that take none. A layout with bitmap or color glyphs and a text
object with a gradient are not kept.
Noto Sans Arabic draws a letter's dots as zero-advance GDEF marks, so the tests expected a beh's dot to take its kasra's colour. Compare against the glyphs of the bare letters instead. Strict colour, geometry and kashida ownership assertions stay as they were.
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.
A paragraph that ends in a soft return ends with the empty line after it,
so the line the soft return ends is justified like any other; it was taken
for the paragraph's last line and left aligned.
In Justify Left, Center or Right, only the paragraph's last line keeps its
alignment; a line that a soft return (U+2028) ends is spread to the frame's
edges like any wrapped line. Illustrator's own layout in a user's .ai sets
such lines justified, and VectorCraft left them ragged.
Every shaped segment built its own harfrust shaping plan, although a
plan depends only on the face, direction, script, language and features.
Opening a document lays out each type object once, so a document of many
short objects built thousands of identical plans.
A font face now keeps up to 32 plans, oldest dropped first, and shapes
with the one that matches. Feature ranges still come from the shape
call, so text with the same features over other ranges shares a plan.
The glyphs, bounds and baselines do not change.
Docs: docs/target-app-parity.md.
Past a gutter of 1e300 a step no longer moves the baseline, so every cell of
a 100 x 100 frame was searched for all MAX_STEPS steps: over 40 s per layout.
AreaOptions::gutter has no limit, so a saved file can do this.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
To find the characters that justification or Mojikumi moves, SVG export
without Fewer <tspan> Elements laid out every live type object a second
time, as point type with
Mojikumi None and default Justification options, whenever its paragraph
had Mojikumi on. New type gets Line-end Punctuation Half Width, so every
new type object was laid out twice. Mojikumi only spaces Japanese
punctuation and Japanese letters, so type without them lays out the same
either way.
- TextLayout::mojikumi_applies says whether the text or a list marker
holds a character Mojikumi spaces.
- reader_advances keeps such a paragraph's Mojikumi, and reads the glyphs
from the layout it was given when the object it would lay out is the
same; otherwise it lays the object out again as before.
The SVG is unchanged. Exporting 5,000 point type objects takes a median
of 109.4 ms instead of 171.5 ms on Windows 11.
Optical moved every pair all the way to a fixed 0.08 em ink gap, tighter
than most text faces' own stem spacing, so ordinary text was squeezed
(at 100 pt, Source Sans 3 `nn` closed from 54.7 to 46.9 pt).
Each pair now moves 0.6 of the way toward the face's reference gap (the
median ink gap of `nn`, else `HH` or `oo`; 0.08 em when it has none),
keeps at least 0.3 of it at its narrowest, and changes by at most
0.12 em tighter or 0.06 em looser. Ideographs, kana, hangul, fullwidth
and CJK punctuation forms and dashes keep the font's spacing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follows #5442.
- text.setGlyphAlternate keeps the default glyph as a choice when a
feature of the character's style (salt, a stylistic set) draws another
glyph for it, so the default glyph is drawn. A character that is not
laid out (overflowing text) is shaped alone in its style to decide.
gid: null reports the glyph the style draws.
- The Glyphs panel's alternates grid shows the default glyph first when
the font does not list it, so a chosen alternate can be put back.
- A test for a chosen character that starts a longer grapheme (f with a
combining acute), which is not split from its mark.
- The alternates tests in the engine hold the shared font lock while
they add fonts to the global font database.
With System Layout on or Fractional Widths off, each advance is rounded
to whole points, but a right-to-left character with manual kerning
still moved by the unrounded kerning value. The difference between the
two changed the space after the character before it in reading order.
The character now moves by the change the kerning makes to its rounded
advance, so only the space before the next character changes.
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.
The menu bar builds Type > Font in every frame, the first included, and
its family list waited for the scan of every installed font. When that
scan took longer than the 20 s first-frame watchdog (thousands of fonts
on a slow disk, or each file checked by antivirus), the app took the
graphics adapter for hung and started again without it, until none was
left and no window ever showed.
The menu now lists the fonts already loaded until the scan ends, then
all of them (FontDb::menu_family_list_now, installed_fonts_ready).
optical_pair_after_rtl_separate_mark_uses_base_outline sets Noto Sans
Arabic, which is not in assets/fonts. Without CRAFT_FONTS_DIR the font
database returns a fallback face, so the test checked that face instead.
The test now skips when Noto Sans Arabic is missing, as the Shippori
Mincho tests in the same file do.
With SCALED_COMPONENT_OFFSET, a component's offset now scales each axis
by the length of that axis's basis vector, as the outline loader does,
instead of going through the whole matrix. A rotated or skewed
component's caret no longer lands away from its drawn point.
UNSCALED_COMPONENT_OFFSET now turns the scaling off when both flags are
set.
A composite glyph whose components expand into glyphs with no points
could make the contour-point caret lookup read billions of glyphs. The
whole lookup now shares a budget of 65,536 glyph reads and gives no
caret when it runs out, so the caret falls back to dividing the advance.
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.