Pretext Lab
21SVG target
Arc 7 · Rendering Targets

SVG target

Pretext's output — line strings with positions — doesn't care what surface draws it. Feed the same walker into an <svg> and you get a typographic specimen: a vector document whose text stays razor-crisp at any zoom. No pixelation, no re-rasterization, no DOM measurement on the wrap change.

Use the zoom buttons to push in on the type. At 1× the width slider still controls the wrap, just like the DOM and canvas targets. At 3× you can count the serifs.

zoom · 1.0×
480

Mechanism

Same walker pattern as the DOM and canvas targets. We call prepareWithSegments(text, font) once, then layoutWithLines(prepared, width, lineHeightPx) whenever the wrap width changes. For each returned line we emit one SVG <text> element with an explicit x and y, where y is the baseline: y = lineHeight * ascentRatio + i * lineHeight. SVG text, like canvas text, anchors to the baseline by default.

Zoom is a single transform="scale(s)" on the stage group. Because SVG text is vector — the glyph outlines are in the document, not rasterized pixels — it stays sharp at every scale. The browser re-rasterizes at paint time using the glyph data it already has. The font descriptor we pass to Pretext matches the font-family/size/weight we declare in CSS on the <text> elements, which keeps the measured line breaks aligned with the rendered glyphs.

Nothing about this path reads from the DOM on reflow. The <text> nodes are written with pre-computed positions; the browser never needs to flow them itself.

Application

SVG wins anywhere resolution has to survive a transform:

The SVG target loses native text selection in most browsers (text inside <text> tags behaves differently than ordinary DOM). It keeps accessibility — screen readers read SVG text — and it keeps crispness. Choose it when zoom is the point.

"A word is dead
When it is said,
Some say.
I say it just
Begins to live
That day."

Emily Dickinson, published in Poems, Third Series (1896)

Direct Claude

"zoomable typographic specimen" SVG target; <text x y> nodes per lines[i] "crisp at any zoom" SVG vector <text>; scale via transform, not re-rasterization "print-ready pull quote" emit SVG; export-as-is pipeline; browser never reflows "baseline in SVG" y = lineHeight * 0.78 + i * lineHeight; default anchor is baseline
Arc 7 closes with three targets sharing one walker. Next: virtual scrolling — the first production pattern that makes all three earn their keep.
walker on SVG: <text x y> per line, scale via transform
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext';

const FONT_PX = 24;
const FONT = `500 italic ${FONT_PX}px 'EB Garamond', serif`;
const LINE_HEIGHT_PX = FONT_PX * 1.4;
const ASCENT_RATIO = 0.78;  // SVG text anchors at the baseline by default

const prepared = prepareWithSegments(text, FONT);
const SVG_NS = 'http://www.w3.org/2000/svg';

function renderAt(width) {
  const { lines } = layoutWithLines(prepared, width, LINE_HEIGHT_PX);
  stage.textContent = '';  // clear previous
  for (let i = 0; i < lines.length; i++) {
    const y = LINE_HEIGHT_PX * ASCENT_RATIO + i * LINE_HEIGHT_PX;
    const t = document.createElementNS(SVG_NS, 'text');
    t.setAttribute('class', 'pt-line');
    t.setAttribute('x', String(paddingX));
    t.setAttribute('y', String(paddingY + y));
    t.textContent = lines[i].text;
    stage.appendChild(t);
  }
}

// Zoom is a single transform. Text stays crisp — it's vector.
function setZoom(s) { stage.setAttribute('transform', `scale(${s})`); }