Skip to content

M4: SVG import units and text fidelity (M4.18, M4.19) - #65

Merged
bflatastic merged 2 commits into
mainfrom
pr/m4-svg-import
Oct 5, 2026
Merged

bflatastic merged 2 commits into
mainfrom
pr/m4-svg-import

Conversation

@bflatastic

Copy link
Copy Markdown
Member

Part of M4 (Files). SVG import keeps physical units and text fidelity: text stays in its place in the stacking order with its group, clip, mask and paints, and <textPath>, per-character positions, weights, spacing and CSS selectors survive. This ports an earlier session's work onto current main and reconciles it with #6, #8, #11 and the M3 import additions.

  • M4.18: SVG import units.
    • A pixel or unitless user unit is 1 pt; absolute units keep their physical size at 72 pt per inch (12pt is 12 pt, 1in is 72 pt). Unitless 100 stays 100.
    • SVG import: root sizes in mm/cm/in/pt open at their physical size #11's root handling is kept: a root size in mm or inches opens at its physical size (210mm → 595.28 pt), with CSS-px user units.
    • The document's units follow the unit of the root width (Pixels without one).
    • Font sizes (em/ex/%, CSS size keywords), text positions, letter spacing, stroke widths and dashes follow their units.
  • M4.19: SVG text import fidelity.
    • Placement: each <text> is swapped for a placeholder before parsing, so it keeps its z-order, parent group, clip, mask, opacity, <use> instances and <a href> link.
    • Paints: gradient and pattern paints resolve in text space.
    • Type on a path: <textPath> honours startOffset, side and the text anchor (this replaces SVG import: <textPath> comes back as type on a path #6's simpler version). Vertical writing-mode becomes type on a vertical path.
    • Character positions: per-character x/y/dx/dy/rotate become line breaks, kerning, baseline shift and rotation. SVG import: tspan line breaks follow the baseline gap (none on the same baseline, bounded far gaps) #8's cap on line breaks is kept, and far-off values stay finite.
    • Character styles: baseline-shift, numeric weights mapped to style names, letter and word spacing, kerning, and the character-stroke options are kept.
    • CSS selectors: type, #id, several classes, attribute, descendant and child selectors, with !important and specificity.
    • Patterns: <pattern> imports as a pattern swatch.

Known limits (documented in svg/src/lib.rs):

  • Lines after the first start at the first line's x.
  • Text inside a clip path is ignored.
  • Absolute positions inside type on a path are ignored.

Checks

Rebased onto 39ce7b0 (#30). cargo xtask ci passes all 7 steps (fmt, clippy, test, assets, brands, layers, wasm).

  • New tests: svg/tests/import_units.rs and import_text.rs, covering the spec's cases plus links, strokes, siblings and finite positions. The import fuzz test gains the new attributes and a hostile-CSS property test.
  • Nothing dropped:
    • every test name on main still exists (1,913 → 1,942);
    • command ids, UI_COMMANDS and MCP tools are unchanged.
  • Two changed tests in svg/tests/roundtrip.rs:
    • import_clip_mask_and_warnings now checks a pattern swatch is imported instead of a "pattern not supported" warning.
    • import_tspan_on_the_same_baseline_stays_on_the_line checks the red run's exact position, since "world" now sits at its exact x.

🤖 Generated with Claude Code

bflatastic and others added 2 commits October 5, 2026 07:09
…px map 1:1 to pt, document units follow the file

A pixel (and a unitless user unit) is a point and absolute lengths convert at 72 pt per inch
(`font-size="12pt"` is 12 pt, `1in` is 72 pt); a root size in absolute units keeps the physical
size main already gave it (`210mm` opens as a 595.28 pt artboard, user units being CSS pixels of
it). The document's units follow the unit of the root width (pixels when it has none). Text font
sizes honour their units (em/ex/% relative to the parent size, CSS size keywords), as do text
positions, letter spacing, stroke widths and dashes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd mask; textPath, per-character positions, weights, spacing, paints and CSS selectors survive

Each <text> is swapped for a placeholder group before usvg runs, so the text keeps its z-order,
parent group, clip, mask, opacity, link and <use> instances, and usvg resolves its gradient and
pattern paints (in text space). <textPath> becomes type on a path (startOffset, side, anchor),
vertical writing-mode type on a vertical path. Per-character x/y/dx/dy/rotate lists become line
breaks (bounded), kerning (absolute x measured against the layout), baseline shift and rotation;
baseline-shift, numeric and relative weights (style names), letter/word spacing, kerning and the
character stroke's cap, join, miter and dashes are kept. The style cascade supports type, #id,
multiple-class, attribute, descendant and child selectors with !important and specificity.
<pattern> paints become pattern swatches. The import fuzz covers the new attributes and CSS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant