In builds with overflow checks (`cargo run`, `cargo test` and
`cargo xtask ci`), PDF export aborted the process when a number it
wrote rounded to -2,147,483,648 as an f32. pdf-writer 0.15 writes
-2,147,483,648.0 as an integer, and `Limits::register_int` overflows
when it takes the absolute value. krilla 0.8.2's `Surface` then fails
an assertion in its `Drop` while that panic unwinds.
`svg_hostile_markup_never_panics` in `import_fuzz` hit it with type
shifted 2,147,483,649 pt down. Paths, gradients, dash offsets, images
and artboards that far out hit it too, and so does a group around a
stroke wide enough to reach that far. Release builds have no overflow
checks.
PDF export now leaves out art whose coordinates or transform entries
lie beyond 1,000,000,000 in its own space or in the content stream it
is drawn into, and glyph lines whose spacing lies beyond 1,000,000,000
thousandths of an em. That covers paths and clipping paths, glyph
lines, images, gradients, strokes whose width, caps or miter spikes
reach that far, and the art and marks of a page whose artboard lies
that far, which is written blank. A dash offset beyond the bound is
written as its remainder after whole periods of the pattern.
The type's transform is now pushed inside the form that marks a fill
or stroke of type to overprint. The exporter checks what that form
holds through the type's transform, as it checks the same fill or
stroke without overprint.
Character tightening and optical kerning space each glyph differently, as
Japanese titles often are. The importer evened a line out with one
tracking value (type on a path took none), leaving glyphs up to an eighth
of an em out of place. When the file's font is installed and one value
can't place every glyph, each character now keeps its own tracking,
measured along the baseline (along the path for type on a path).
Glyphs closed up by up to half an em (the proportional widths of CJK
punctuation) also continue their line instead of starting another.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a PDF (or the PDF part of an .ai file), a glyph of a subset font that
no ToUnicode map, CID table or character map of the font program names
was drawn as outlines, so one text object came in split: "RADA" as the
type "ADA" plus the R's outline.
Such a glyph is now looked for in the installed face of the font's name:
the first character (of those single-byte fonts encode: ASCII, Latin-1,
Latin Extended-A, punctuation, the euro and trade mark signs, fi and fl)
whose glyph draws the same outline, point for point within 2/1000 em,
names it. A subset carries the font's own outlines, so an installed copy
of the same font finds every glyph. Other versions of the font, empty
glyphs and glyphs that are no character still keep their outlines, with
the warning as before. Results are cached per glyph.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A font library can hold two versions of one family and style (a new
Montserrat-Bold.ttf beside an old montserrat.ttf). The font database kept
only the first one loaded, so type set in the other couldn't be matched
when a PDF or .ai was opened: it came in as outlines. Now every version
loads (the first stays the one family and style resolve to), PDF import
tries the other versions when the glyphs differ, and type set in a version
the family and style alone wouldn't pick names it in CharStyle::font_version
(left out when None, so documents without it are unchanged). Layout,
shaping, the substituted-glyph highlight, Find Font, Package and Document
Info follow it; changing the font clears it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A text matrix that shears the glyphs (type turned and slanted as a whole)
came in as point type turned by the baseline's angle only: the glyphs stood
square on the baseline, so the type drew somewhere else (60 pt type leaning
by 0.25 came back about 10 pt off its outlines). The placement now keeps the
slant (the em's lean along the baseline per unit of its height, the height
measured across the baseline), lines only join glyphs with the same slant,
and the type object's transform shears by it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PDF or PDF-compatible .ai whose ToUnicode map gives some glyphs U+FFFD (as
some files do for the digits of an embedded CID font) imported those glyphs as
type showing the replacement character, so every digit came in as "�".
U+FFFD now counts as no mapping. Such a glyph takes its character from the
embedded font where it can (Adobe's CID tables for CID-keyed CFF, as before,
then the font program's own cmap), and otherwise keeps its outline from the
embedded font, so it looks as the file draws it. The import warns which font
had characters the file doesn't name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Document symbols placed inline in a run of type (beyond Illustrator; like
InDesign inline anchored objects): TextRun.inline holds the symbol, scale and
shift; the run is one U+FFFC laid out as a glyph sized to the art, centred on
the cap height. text.insertInline and Type > Insert Inline Symbol; editing
keeps the graphic whole; canvas, SVG (<use>) and PDF draw the art; Create
Outlines turns it into a symbol instance.
Type on a path could only be repositioned by Type on a Path Options, whose menu item sent
`{start: 0}`, so OK threw the text to the start of its path; nothing showed or dragged its
brackets. Now, as in Illustrator:
- Type on a path flows between a start and an end bracket (`TextKind::OnPath { end }`, None:
the end of the path or once round a closed one; `TextKind::path_span`). Flipping
(`TextObject::flip_on_path`) reverses the path keeping a closed path's first anchor and swaps
the brackets, so the type keeps its stretch of the path.
- Type on a Path Options gains Align to Path (ascender, descender, center, baseline) and
Spacing (glyphs spaced as if set that far above the path: closed up round the outside of
curves, opened up round the inside), laid out by the text engine.
- `type.pathOptions {start?, end?, flip?, effect?, alignToPath?, spacing?, ids?}` sets them in
one undo step; with none of them it queries. The menu item opens the dialog with the selected
type's Effect, Flip, Align to Path and Spacing (dropdowns for the choices) and leaves the
brackets alone, so OK as opened changes nothing.
- The Selection and Direct Selection tools show selected type on a path's path and its start,
centre and end brackets (`tools::pathtype`, with a bracket cursor): dragging the start or end
bracket sets where the type runs, dragging the centre bracket slides it along the path and,
across the path, flips it (Cmd/Ctrl held: it only slides). Each drag is one undo step.
- Arc-length measuring moved to `vectorcraft_geom::ArcPath` (layout, the tools and
`text.createInPath` share it). SVG export outlines path type whose end bracket, alignment or
spacing `<textPath>` can't express.
- docs/mcp.md documents the command and the drags; ROADMAP updated; dialog strings translated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Japanese PDFs and PDF-compatible .ai files often set text in CID fonts
with no ToUnicode map, in vertical columns, and in fonts the reader
doesn't have. They came in as outlines (or as the wrong characters).
- CID fonts without ToUnicode: the character comes from the font's CFF
charset through the standard CMaps (hayro's `embed-cmaps`); Kangxi
radicals that some generators map to are unified with their ideographs
(⿓ → 龍).
- A glyph becomes live type only when the installed font draws the same
glyph: compared by box with the font of the PDF's PostScript name, or
the closest style of its family (a variable font's named instances),
whose style the type then takes. Otherwise it keeps its outline.
- Vertical columns (WMode 1, glyphs with no horizontal advance) come back
as vertical type, letter spacing as tracking; a single upright glyph is
vertical type too. Horizontal lines get tracking from the glyph
positions the same way.
- Text in a font that isn't installed keeps its outlines (what prints),
with an editable copy in a substitute font, drawn pink, on a
non-printing layer named after the missing fonts (stroke-only glyphs
included).
- Marked content other than a layer's (spans with actual text,
artifacts) no longer ends a line of type: 篳篥・龍笛・笙 came back as
three objects when one character was in a span.
- Tests in tests_live_text.rs and tests_realtext.rs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The font database made one style per font file, so a variable font
such as Figtree[wght].ttf listed only its default instance (Light).
Faces now also yield their fvar named instances, each a style of the
family named by its subfamily name (the default instance is the face
itself; unnamed or duplicate instances are left out; axes and
instances read are capped). The system font scan reads the fvar table
beside the name table, so installed instances are listed before the
file is loaded and found by name and by PostScript name.
An instance face carries its axis settings: skrifa draws its outlines
and reads its metrics and advances at that location, harfrust shapes
with a ShaperInstance, so layout, the canvas, Create Outlines and hit
testing all use the instance. PDF export embeds it through krilla's
Font::new_variable, SVG @font-face through subsetter's
subset_with_variations: both write a static font of that instance.
Tests build a small variable font in code (test_fonts, via
write-fonts; no font file is committed) and check listing, resolving,
glyphs and advances, PDF and SVG export and damaged fvar/gvar tables;
Bahnschrift and Segoe UI Variable are checked where installed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Convert Text to Outlines off (advanced.outlineText: false), type is
written as selectable, searchable text: each glyph in its font, embedded
as a subset with a ToUnicode map, where its outline would be (rotated,
scaled, skewed and on-path type too; gradients keep their place). Fonts
whose licence forbids embedding, or allows only whole fonts, fall back to
outlines with a warning; a subset threshold below 100% is accepted with a
warning. Laid-out glyphs now carry their glyph id and transform.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>