Skip to content
Apexify.jsDocs
Apexify.js version v5.4.5

Node / Text Rendering · guide

Text on a circular arc (textOnCurve)

Current Apexify.js 6.0.0 documentation for Text on a circular arc (textOnCurve).

apexify.jsRuntime: nodeCURRENTSince 6.0.0

When textOnCurve is set (TextCurveConfig), EnhancedTextRenderer routes each newline-separated row through renderCurvedLine.


Activating arc layout

SOURCEtypescript
typescript
Studio
textOnCurve?: TextCurveConfig; interface TextCurveConfig {  sweepAngle: number;       // degrees — arc span along which glyphs distribute  radius?: number;  up?: boolean;             // default true — bend toward −y (“smile”) vs inverted  layoutMode?: CurvedTextLayoutMode;  baselineOffset?: number;  // radial outward shift (pixels)  startAngleDeg?: number;   // rotate entire arc around anchor (degrees)}

Degenerate cases → straight text

If sweepAngle <= 0, sweepAngle >= 360, computeCircularArcPlacements returns null, or there are zero graphemes, the renderer falls back to renderEnhancedTextLine at (x, y) — same styling stack as normal horizontal text.


Geometry model (lib-next/text/text-curved.ts)

  • Graphemes come from Intl.Segmenter when available — combining marks stay attached.
  • Horizontal centres along the straight baseline (measureGraphemeCenters) drive proportional spacing along the arc.
  • anchor (x, y) is the mid-string anchor on the arc (apex when up: true at startAngleDeg: 0).
  • baselineOffset expands/contracts Rdraw = R + baselineOffset without changing angular spacing.

layoutMode (CurvedTextLayoutMode)

Relates measured typographic width W, user sweep θ, and optional radius (**documented on CurvedTextLayoutMode in types/text.ts):

ModeRadius rule
fitR = W / θ — radius ignored for geometry; arc length matches measured width.
clamp (default)R = max(radius ?? R_fit, R_fit) — never tighter than fit.
overrideR = radius ?? R_fit; if R·θ < W, sweep expands so spacing never crowds.

Multi-line arcs

text.split('\n') feeds independent arcs. y advances per line by lineHeight * fontSize where lineHeight defaults 1.4 (resolveTextLayout).


Example — badge arc

SOURCEtypescript
typescript
Studio
await painter.createText(  {    text: 'PREMIUM QUALITY',    x: 400,    y: 340,    font: { size: 28, family: 'Arial' },    placement: { textAlign: 'center', textBaseline: 'alphabetic' },    fill: { color: '#fefce8' },    stroke: { color: '#422006', width: 2 },    textOnCurve: {      sweepAngle: 140,      radius: 220,      up: true,      layoutMode: 'clamp',      startAngleDeg: 0,      baselineOffset: 4,    },  },  buffer);

Interaction with maxWidth

Arc mode does not word-wrap — layout.maxWidth is ignored for branching (textOnCurve wins first). Insert \\n manually if you need stacked arcs.


Next steps