render-tag 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,32 @@
2
2
  * Pure layout: walks styled segments and lays each grapheme along a path.
3
3
  * No canvas rendering happens here — that's the caller's job. Reusable
4
4
  * for hit-testing, debugging, alternate renderers (SVG, GPU).
5
+ *
6
+ * Joining-script support: graphemes from Arabic, Hebrew, Indic, Thai, Khmer,
7
+ * Myanmar and related scripts within a single segment are grouped into one
8
+ * "shaped run" placement. The browser's fillText/measureText then shapes the
9
+ * run as a unit (cursive joining, reordering, conjuncts) — something
10
+ * per-grapheme drawing cannot do.
5
11
  */
6
- import { applyFont } from '../layout.js';
12
+ import { applyFont, getFontMetrics } from '../layout.js';
7
13
  import { stringToArray } from './grapheme.js';
14
+ /**
15
+ * Returns the local-y of the alphabetic baseline given a textBaseline
16
+ * choice and a glyph's ascent/descent. All baseline-relative computations
17
+ * (decoration positions, background polygons, bounds cells) ADD this offset
18
+ * to their local-y so the path line through (0,0) corresponds to the
19
+ * requested baseline anchor.
20
+ */
21
+ export function baselineLocalY(tb, ascent, descent) {
22
+ switch (tb) {
23
+ case 'alphabetic': return 0;
24
+ case 'middle': return (ascent - descent) / 2;
25
+ case 'top': return ascent;
26
+ case 'bottom': return -descent;
27
+ case 'hanging': return ascent * 0.8;
28
+ case 'ideographic': return -descent * 0.5;
29
+ }
30
+ }
8
31
  /**
9
32
  * Flatten a styled tree into a flat sequence of styled text segments.
10
33
  * Walks in document order; concatenates text under nested inline elements.
@@ -24,27 +47,131 @@ export function flattenSegments(root) {
24
47
  walk(root, false);
25
48
  return out;
26
49
  }
50
+ // Unicode ranges where graphemes need shape-aware rendering. The browser's
51
+ // fillText handles these correctly only when given the whole run at once,
52
+ // not one grapheme at a time:
53
+ // - Hebrew (no joining, but RTL+BiDi)
54
+ // - Arabic + presentation forms (cursive joining)
55
+ // - N'Ko, Mandaic, Syriac, Thaana (joining/RTL)
56
+ // - Devanagari, Bengali, Gurmukhi, Gujarati, Oriya, Tamil, Telugu, Kannada,
57
+ // Malayalam, Sinhala (Indic reordering + conjuncts)
58
+ // - Thai, Lao (combining marks + word break)
59
+ // - Tibetan (stacking)
60
+ // - Myanmar (reordering + stacking)
61
+ // - Khmer (reordering + subscript consonants)
62
+ // IMPORTANT: written with `\u` escapes only. Mixing literal RTL characters
63
+ // confuses the regex parser at parse time — e.g. the precomposed `יִ` is
64
+ // actually two code points (U+05D9 + U+05B4) and a literal range starting
65
+ // at the second code point engulfs CJK / Hangul / Hiragana / Katakana,
66
+ // causing `needsShaping('中')` to return true.
67
+ const SHAPING_RE = new RegExp('[' +
68
+ '\\u0590-\\u05FF' + // Hebrew
69
+ '\\u0600-\\u06FF' + // Arabic
70
+ '\\u0700-\\u074F' + // Syriac
71
+ '\\u0750-\\u077F' + // Arabic Supplement
72
+ '\\u0780-\\u07BF' + // Thaana
73
+ '\\u07C0-\\u07FF' + // NKo
74
+ '\\u0800-\\u083F' + // Samaritan
75
+ '\\u0840-\\u085F' + // Mandaic
76
+ '\\u0860-\\u086F' + // Syriac Supplement
77
+ '\\u08A0-\\u08FF' + // Arabic Extended-A
78
+ '\\u0900-\\u097F' + // Devanagari
79
+ '\\u0980-\\u09FF' + // Bengali
80
+ '\\u0A00-\\u0A7F' + // Gurmukhi
81
+ '\\u0A80-\\u0AFF' + // Gujarati
82
+ '\\u0B00-\\u0B7F' + // Oriya
83
+ '\\u0B80-\\u0BFF' + // Tamil
84
+ '\\u0C00-\\u0C7F' + // Telugu
85
+ '\\u0C80-\\u0CFF' + // Kannada
86
+ '\\u0D00-\\u0D7F' + // Malayalam
87
+ '\\u0D80-\\u0DFF' + // Sinhala
88
+ '\\u0E00-\\u0E7F' + // Thai
89
+ '\\u0E80-\\u0EFF' + // Lao
90
+ '\\u0F00-\\u0FFF' + // Tibetan
91
+ '\\u1000-\\u109F' + // Myanmar
92
+ '\\u1780-\\u17FF' + // Khmer
93
+ '\\u1800-\\u18AF' + // Mongolian
94
+ '\\uFB1D-\\uFB4F' + // Hebrew Presentation Forms
95
+ '\\uFB50-\\uFDFF' + // Arabic Presentation Forms-A
96
+ '\\uFE70-\\uFEFF' + // Arabic Presentation Forms-B
97
+ ']');
98
+ function needsShaping(s) {
99
+ return SHAPING_RE.test(s);
100
+ }
101
+ /**
102
+ * Split a single styled segment into PreGlyphs.
103
+ *
104
+ * Non-joining graphemes (Latin, CJK, …) emit one PreGlyph per grapheme so the
105
+ * curve can drive per-glyph rotation. Joining-script graphemes are grouped
106
+ * into runs (split at whitespace + style boundaries) so the browser can shape
107
+ * them correctly when we later call fillText on the run as a whole.
108
+ *
109
+ * For RTL segments, the RUN ORDER is reversed (not the graphemes inside a
110
+ * shaped run) — that way Arabic words still shape correctly while flowing in
111
+ * visual right-to-left order along an LTR path walk.
112
+ */
113
+ function preGlyphsForSegment(ctx, seg) {
114
+ const graphemes = stringToArray(seg.text);
115
+ if (graphemes.length === 0)
116
+ return [];
117
+ applyFont(ctx, seg.style);
118
+ // Always assign — when the current segment's letterSpacing is 0/unset,
119
+ // we still need to reset the previous segment's value.
120
+ ctx.letterSpacing = `${seg.style.letterSpacing || 0}px`;
121
+ const { ascent, descent } = getFontMetrics(ctx, seg.style);
122
+ // Group graphemes into (shaped run | single non-shaped grapheme).
123
+ // Boundaries: shape-status change, ASCII whitespace.
124
+ const runs = [];
125
+ let currentShapedRun = '';
126
+ for (const g of graphemes) {
127
+ const isSpace = g === ' ';
128
+ if (needsShaping(g) && !isSpace) {
129
+ currentShapedRun += g;
130
+ }
131
+ else {
132
+ if (currentShapedRun) {
133
+ runs.push({ text: currentShapedRun, shaped: true, isSpace: false });
134
+ currentShapedRun = '';
135
+ }
136
+ runs.push({ text: g, shaped: false, isSpace });
137
+ }
138
+ }
139
+ if (currentShapedRun) {
140
+ runs.push({ text: currentShapedRun, shaped: true, isSpace: false });
141
+ }
142
+ // RTL: reverse run order. Don't reverse graphemes inside a shaped run —
143
+ // the browser will lay them out right-to-left during fillText.
144
+ if (seg.rtl)
145
+ runs.reverse();
146
+ // Measure each run with the current ctx font/letterSpacing.
147
+ const out = [];
148
+ for (const r of runs) {
149
+ const width = ctx.measureText(r.text).width;
150
+ out.push({
151
+ text: r.text,
152
+ width,
153
+ style: seg.style,
154
+ isSpace: r.isSpace,
155
+ ascent,
156
+ descent,
157
+ shaped: r.shaped,
158
+ });
159
+ }
160
+ return out;
161
+ }
27
162
  /**
28
163
  * Lay out graphemes along a path. Pure: does not call ctx.fillText.
29
164
  *
30
165
  * Algorithm:
31
- * 1. Per segment, split into graphemes and measure each via ctx.measureText.
32
- * RTL segments have their grapheme order reversed before walking the path
33
- * (so a left-to-right path walk produces correct visual order).
166
+ * 1. Per segment, split into graphemes and shaped runs, measure each.
34
167
  * 2. Compute total natural width.
35
168
  * 3. Pick a starting offset along the path based on `align`.
36
- * 4. For each grapheme:
37
- * p0 = path.getPointAtLength(offset)
38
- * p1 = path.getPointAtLength(offset + width)
39
- * rotation = atan2(p1.y - p0.y, p1.x - p0.x)
40
- * A trailing kerning slack (sum-of-glyph-widths > whole-string width)
41
- * can push the last glyph 1–2px past pathLength; clamp the end point
42
- * to pathLength in that narrow case to avoid dropping the last glyph.
43
- * 5. If a glyph would extend past the path entirely, stop emitting.
169
+ * 4. For each placement: get p0 / p1 from the path, rotation = atan2(p1-p0).
170
+ * If a placement would overshoot, only allow it within kerning slack.
44
171
  */
45
172
  export function layoutGlyphsOnPath(input) {
46
- const { segments, path, ctx, align } = input;
47
- // 1. Pre-measure all graphemes, segment-by-segment.
173
+ const { segments, path, ctx, align, textBaseline } = input;
174
+ // 1. Pre-measure all placements (one per grapheme or shaped run).
48
175
  // Caller's ctx state is mutated here (font, fontKerning, letterSpacing).
49
176
  // The outer drawTextOnPath/drawTextOnPathLayout calls ctx.save before this
50
177
  // and ctx.restore after, so the leak doesn't reach the caller.
@@ -54,37 +181,17 @@ export function layoutGlyphsOnPath(input) {
54
181
  for (const seg of segments) {
55
182
  if (!seg.text)
56
183
  continue;
57
- const graphemes = stringToArray(seg.text);
58
- if (graphemes.length === 0)
59
- continue;
60
- if (seg.rtl)
61
- graphemes.reverse();
62
- applyFont(ctx, seg.style);
63
- // Always assign — when the current segment's letterSpacing is 0/unset,
64
- // we still need to reset the previous segment's value.
65
- ctx.letterSpacing = `${seg.style.letterSpacing || 0}px`;
66
- // Track ribbon thickness: CSS line-height in px when set, else font size.
67
184
  const lh = seg.style.lineHeight > 0 ? seg.style.lineHeight : seg.style.fontSize;
68
185
  if (lh > maxLineHeight)
69
186
  maxLineHeight = lh;
70
- // Per-glyph width via measureText (browsers honor letterSpacing here).
71
- for (const g of graphemes) {
72
- const m = ctx.measureText(g);
73
- preGlyphs.push({
74
- char: g,
75
- width: m.width,
76
- style: seg.style,
77
- // Only ASCII space (U+0020) is justify-eligible. \n/\t/  and
78
- // other Unicode whitespace must NOT expand — that would break
79
- // no-break-space contract and treat <br>-synthesized newlines as
80
- // expansible spaces.
81
- isSpace: g === ' ',
82
- });
83
- }
187
+ const segGlyphs = preGlyphsForSegment(ctx, seg);
188
+ if (segGlyphs.length === 0)
189
+ continue;
190
+ preGlyphs.push(...segGlyphs);
84
191
  // Whole-segment width — kerning makes this < sum of per-glyph widths.
85
192
  measuredWholeWidth += ctx.measureText(seg.text).width;
86
193
  }
87
- // 2. Sum natural width (sum of glyph widths + letterSpacing per glyph).
194
+ // 2. Sum natural width (sum of placement widths + letterSpacing per item).
88
195
  let textWidth = 0;
89
196
  for (const g of preGlyphs) {
90
197
  textWidth += g.width + (g.style.letterSpacing || 0);
@@ -114,8 +221,16 @@ export function layoutGlyphsOnPath(input) {
114
221
  }
115
222
  }
116
223
  // 4. Walk the path.
224
+ // We track two cumulative offsets:
225
+ // - `offset` is the path arc-length (used for path.getPointAtLength), and
226
+ // advances by `effectiveWidth` (incl. justify extraPerSpace).
227
+ // - `naturalOffset` is the glyph's position in NATURAL text-space
228
+ // [0, textWidth]. Used as `pathOffset` for gradient slicing — must NOT
229
+ // include extraPerSpace, otherwise late justified glyphs map past the
230
+ // gradient's last stop.
117
231
  const glyphs = [];
118
232
  let offset = startOffset;
233
+ let naturalOffset = 0;
119
234
  for (let i = 0; i < preGlyphs.length; i++) {
120
235
  const g = preGlyphs[i];
121
236
  const effectiveWidth = g.width + (g.isSpace ? extraPerSpace : 0);
@@ -126,7 +241,7 @@ export function layoutGlyphsOnPath(input) {
126
241
  let p1;
127
242
  if (endLen > pathLength) {
128
243
  // Clamp to pathLength only if the overshoot is within the kerning slack —
129
- // matches konva's behavior to keep the last glyph from getting dropped.
244
+ // keeps the last glyph from getting dropped to a sub-px rounding miss.
130
245
  if (endLen - pathLength <= kerningSlack + 0.5) {
131
246
  p1 = path.getPointAtLength(pathLength);
132
247
  }
@@ -141,15 +256,82 @@ export function layoutGlyphsOnPath(input) {
141
256
  break;
142
257
  const rotation = Math.atan2(p1.y - p0.y, p1.x - p0.x);
143
258
  glyphs.push({
144
- char: g.char,
259
+ char: g.text,
145
260
  x: p0.x,
146
261
  y: p0.y,
147
262
  rotation,
148
263
  width: g.width,
149
264
  style: g.style,
265
+ ascent: g.ascent,
266
+ descent: g.descent,
267
+ pathOffset: naturalOffset,
268
+ shaped: g.shaped,
150
269
  });
151
270
  offset = endLen + (g.style.letterSpacing || 0);
271
+ naturalOffset += g.width + (g.style.letterSpacing || 0);
272
+ }
273
+ const bounds = computeBounds(glyphs, textBaseline);
274
+ return {
275
+ glyphs,
276
+ textWidth,
277
+ pathLength,
278
+ lineHeight: maxLineHeight,
279
+ bounds,
280
+ textBaseline,
281
+ };
282
+ }
283
+ /**
284
+ * Bounding box of the rendered text: union of each glyph's
285
+ * `width × per-glyph line-height` cell, rotated by the glyph's tangent.
286
+ *
287
+ * The cell height is the per-glyph line-height (CSS line-height when set,
288
+ * else font-size — Polotno's chosen metric). It is distributed
289
+ * ASYMMETRICALLY above/below the baseline using the font's natural
290
+ * ascent/descent ratio, so cap-height + ascender area is covered and the
291
+ * cell doesn't waste pixels below an empty descender. For a typical font
292
+ * with ascent ~25 / descent ~7 / lineHeight 32, the cell extends ~25px
293
+ * above and ~7px below the baseline, matching the painted glyph extent.
294
+ *
295
+ * Empty layouts return `{ x: 0, y: 0, width: 0, height: 0 }`.
296
+ */
297
+ function computeBounds(glyphs, textBaseline) {
298
+ if (glyphs.length === 0)
299
+ return { x: 0, y: 0, width: 0, height: 0 };
300
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
301
+ for (const g of glyphs) {
302
+ const lh = g.style.lineHeight > 0 ? g.style.lineHeight : g.style.fontSize;
303
+ // Distribute the lineHeight above/below the baseline by the font's
304
+ // natural ascent/descent ratio (CSS line-box half-leading semantics).
305
+ const fontHeight = g.ascent + g.descent;
306
+ const ascentShare = fontHeight > 0 ? g.ascent / fontHeight : 0.75;
307
+ const topFromBaseline = lh * ascentShare; // above baseline
308
+ const bottomFromBaseline = lh * (1 - ascentShare); // below baseline
309
+ // Shift by the local-y of the baseline so the cell stays correct under
310
+ // any textBaseline (e.g. 'middle' places the cell vertically centred
311
+ // on the path; 'top' moves the entire cell down).
312
+ const baseY = baselineLocalY(textBaseline, g.ascent, g.descent);
313
+ const top = baseY - topFromBaseline;
314
+ const bottom = baseY + bottomFromBaseline;
315
+ const c = Math.cos(g.rotation);
316
+ const s = Math.sin(g.rotation);
317
+ // Local cell: x in [0, width], y in [top, bottom]. Rotate + translate.
318
+ const corners = [
319
+ { x: g.x + (-s) * top, y: g.y + c * top },
320
+ { x: g.x + c * g.width + (-s) * top, y: g.y + s * g.width + c * top },
321
+ { x: g.x + c * g.width + (-s) * bottom, y: g.y + s * g.width + c * bottom },
322
+ { x: g.x + (-s) * bottom, y: g.y + c * bottom },
323
+ ];
324
+ for (const p of corners) {
325
+ if (p.x < minX)
326
+ minX = p.x;
327
+ if (p.y < minY)
328
+ minY = p.y;
329
+ if (p.x > maxX)
330
+ maxX = p.x;
331
+ if (p.y > maxY)
332
+ maxY = p.y;
333
+ }
152
334
  }
153
- return { glyphs, textWidth, pathLength, lineHeight: maxLineHeight };
335
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
154
336
  }
155
337
  //# sourceMappingURL=glyph-layout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"glyph-layout.js","sourceRoot":"","sources":["../../src/path/glyph-layout.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AA8C9C;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,MAAM,GAAG,GAAc,EAAE,CAAC;IAC1B,SAAS,IAAI,CAAC,IAAgB,EAAE,YAAqB;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,KAAK,IAAI,YAAY,CAAC;QAC3D,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACjD,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAClB,OAAO,GAAG,CAAC;AACb,CAAC;AAUD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAkB;IACnD,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAE7C,oDAAoD;IACpD,yEAAyE;IACzE,2EAA2E;IAC3E,+DAA+D;IAC/D,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAC3B,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,IAAI;YAAE,SAAS;QACxB,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACrC,IAAI,GAAG,CAAC,GAAG;YAAE,SAAS,CAAC,OAAO,EAAE,CAAC;QACjC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC1B,uEAAuE;QACvE,uDAAuD;QACvD,GAAG,CAAC,aAAa,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,IAAW,CAAC;QAC/D,0EAA0E;QAC1E,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;QAChF,IAAI,EAAE,GAAG,aAAa;YAAE,aAAa,GAAG,EAAE,CAAC;QAC3C,uEAAuE;QACvE,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAC7B,SAAS,CAAC,IAAI,CAAC;gBACb,IAAI,EAAE,CAAC;gBACP,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,kEAAkE;gBAClE,8DAA8D;gBAC9D,iEAAiE;gBACjE,qBAAqB;gBACrB,OAAO,EAAE,CAAC,KAAK,GAAG;aACnB,CAAC,CAAC;QACL,CAAC;QACD,sEAAsE;QACtE,kBAAkB,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;IACxD,CAAC;IAED,wEAAwE;IACxE,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,SAAS,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,oDAAoD;IACpD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,SAAS,IAAI,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;IACxE,CAAC;IAED,oEAAoE;IACpE,uEAAuE;IACvE,uEAAuE;IACvE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,kBAAkB,CAAC,CAAC;IAEjE,kDAAkD;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;IAC/B,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,CAAC;SAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QAC7B,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC;IACpD,CAAC;SAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;QAC3D,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,SAAS,EAAE,CAAC;YAC7C,aAAa,GAAG,CAAC,UAAU,GAAG,SAAS,CAAC,GAAG,UAAU,CAAC;QACxD,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,IAAI,MAAM,GAAG,WAAW,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,MAAM,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,EAAE;YAAE,MAAM;QAEf,IAAI,MAAM,GAAG,MAAM,GAAG,cAAc,CAAC;QACrC,IAAI,EAAgB,CAAC;QACrB,IAAI,MAAM,GAAG,UAAU,EAAE,CAAC;YACxB,0EAA0E;YAC1E,wEAAwE;YACxE,IAAI,MAAM,GAAG,UAAU,IAAI,YAAY,GAAG,GAAG,EAAE,CAAC;gBAC9C,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;YACzC,CAAC;iBAAM,CAAC;gBACN,MAAM;YACR,CAAC;QACH,CAAC;aAAM,CAAC;YACN,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,EAAE;YAAE,MAAM;QAEf,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,CAAC,EAAE,EAAE,CAAC,CAAC;YACP,CAAC,EAAE,EAAE,CAAC,CAAC;YACP,QAAQ;YACR,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,KAAK,EAAE,CAAC,CAAC,KAAK;SACf,CAAC,CAAC;QAEH,MAAM,GAAG,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"glyph-layout.js","sourceRoot":"","sources":["../../src/path/glyph-layout.ts"],"names":[],"mappings":"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"}
@@ -13,20 +13,25 @@
13
13
  * align: 'center',
14
14
  * });
15
15
  *
16
- * For finer control, mirror the main API's split:
16
+ * For finer control:
17
17
  * const result = layoutTextOnPath({ html, path, align });
18
18
  * drawTextOnPathLayout({ layout: result, ctx });
19
- * Use the split when you want to inspect / hit-test glyphs before drawing,
20
- * or render the same layout onto multiple targets.
21
19
  *
22
- * Out of scope for the first cut: text-shadow, gradient/background-clip text,
23
- * text-decoration (underline/line-through), per-glyph kerning callbacks.
24
- * Mixed-script BiDi shaping is not supported (matches the konva limit) — pure
25
- * RTL strings render correctly via CSS `direction: rtl` or the `dir` attribute.
20
+ * Supported styles (matches the main renderer):
21
+ * font-family / size / weight / style / kerning
22
+ * color, -webkit-text-fill-color, -webkit-text-stroke (width + color), paint-order
23
+ * background-color (span backgrounds drawn as curve-following polygons)
24
+ * text-shadow (rotates with glyphs; multi-shadow supported)
25
+ * text-decoration: underline / line-through / overline (solid/dotted/dashed/double/wavy)
26
+ * text-decoration-color, text-decoration-style
27
+ * background-clip:text + background-image:linear-gradient (gradient flows along the path)
28
+ * letter-spacing, direction: rtl, dir="rtl"
29
+ * Arabic / Hebrew / Indic / Thai / Khmer / Myanmar — shaped runs are
30
+ * rendered as a unit so cursive joining and reordering work correctly.
26
31
  */
27
32
  import { type PathLike } from './svg-path.js';
28
- import { type AlignMode, type GlyphPlacement } from './glyph-layout.js';
29
- export type { PathLike, GlyphPlacement, AlignMode };
33
+ import { type AlignMode, type GlyphPlacement, type TextBaseline } from './glyph-layout.js';
34
+ export type { PathLike, GlyphPlacement, AlignMode, TextBaseline };
30
35
  export interface LayoutTextOnPathConfig {
31
36
  /** Rich-text HTML (same dialect as render-tag's main API). */
32
37
  html: string;
@@ -34,6 +39,13 @@ export interface LayoutTextOnPathConfig {
34
39
  path: string | PathLike;
35
40
  /** Alignment of text along the path (default 'left'). */
36
41
  align?: AlignMode;
42
+ /**
43
+ * Where the path runs relative to the rendered text. Default
44
+ * `'alphabetic'` (path = baseline, descenders drop below). Use `'middle'`
45
+ * for design-tool style "text centered on path" rendering. The choice
46
+ * also affects `bounds`.
47
+ */
48
+ textBaseline?: TextBaseline;
37
49
  /**
38
50
  * Optional measurement context. If omitted, an offscreen canvas is created.
39
51
  * Pass one to share font measurement caches with other render-tag calls.
@@ -41,23 +53,39 @@ export interface LayoutTextOnPathConfig {
41
53
  ctx?: CanvasRenderingContext2D;
42
54
  }
43
55
  export interface TextOnPathLayout {
44
- /** Per-glyph placement records (origin point, rotation, style). */
56
+ /** Per-glyph (or per-shaped-run) placement records. */
45
57
  glyphs: GlyphPlacement[];
46
58
  /** Sum of per-glyph advances (the natural width of the rendered text). */
47
59
  textWidth: number;
48
60
  /** Total arc length of the path. */
49
61
  pathLength: number;
50
62
  /**
51
- * Max line height across all segments (CSS line-height in px when set,
52
- * else the segment's font size). Use as the thickness for a background
53
- * ribbon polygon around curved text.
63
+ * Max line height across all segments. Useful as the thickness for a
64
+ * background ribbon polygon around curved text.
54
65
  */
55
66
  lineHeight: number;
67
+ /**
68
+ * Visible bounding box of the rendered text in the layout's coordinate
69
+ * system (same shape as `DOMRect`). Computed as the union of each glyph's
70
+ * `width × per-glyph line-height` cell, rotated by the glyph's tangent.
71
+ *
72
+ * Consumer-facing: render-tag does not consume `bounds` itself. Use it to
73
+ * keep a parent element's width/height in sync with the rendered curved
74
+ * text without re-walking the glyphs.
75
+ */
76
+ bounds: {
77
+ x: number;
78
+ y: number;
79
+ width: number;
80
+ height: number;
81
+ };
82
+ /** The textBaseline mode the layout was computed with. */
83
+ textBaseline: TextBaseline;
56
84
  }
57
85
  export interface DrawTextOnPathLayoutConfig {
58
86
  /** Result from layoutTextOnPath(). */
59
87
  layout: TextOnPathLayout;
60
- /** Destination 2D context. The transform is saved/restored per glyph. */
88
+ /** Destination 2D context. Saved+restored around the whole batch. */
61
89
  ctx: CanvasRenderingContext2D;
62
90
  }
63
91
  export interface DrawTextOnPathConfig {
@@ -69,6 +97,8 @@ export interface DrawTextOnPathConfig {
69
97
  ctx: CanvasRenderingContext2D;
70
98
  /** Alignment of text along the path (default 'left'). */
71
99
  align?: AlignMode;
100
+ /** Where the path runs relative to the text (default 'alphabetic'). */
101
+ textBaseline?: TextBaseline;
72
102
  }
73
103
  export type DrawTextOnPathResult = TextOnPathLayout;
74
104
  /**
@@ -84,13 +114,18 @@ export type DrawTextOnPathResult = TextOnPathLayout;
84
114
  export declare function layoutTextOnPath(config: LayoutTextOnPathConfig): TextOnPathLayout;
85
115
  /**
86
116
  * Draw a pre-computed layout onto a canvas context.
87
- * Each glyph is rendered with its own style; ctx state is saved/restored
88
- * around the whole batch so the caller's state is preserved.
117
+ *
118
+ * Rendering passes (matching CSS painting order):
119
+ * 1. Span backgrounds (background-color, curve-following polygons)
120
+ * 2. Text shadows (per glyph, multi-shadow supported, rotates with glyph)
121
+ * 3. Glyph fill + stroke (paint-order aware; gradient text via slicing)
122
+ * 4. Text decoration (underline / line-through / overline)
123
+ *
124
+ * ctx state is saved+restored around the entire batch — nothing leaks.
89
125
  */
90
126
  export declare function drawTextOnPathLayout(config: DrawTextOnPathLayoutConfig): void;
91
127
  /**
92
128
  * Convenience: compute layout and draw in a single call.
93
- * Equivalent to `drawTextOnPathLayout({ layout: layoutTextOnPath(...), ctx })`.
94
129
  */
95
130
  export declare function drawTextOnPath(config: DrawTextOnPathConfig): DrawTextOnPathResult;
96
131
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/path/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAMH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,cAAc,EACpB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;AAEpD,MAAM,WAAW,sBAAsB;IACrC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,GAAG,CAAC,EAAE,wBAAwB,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,0BAA0B;IACzC,sCAAsC;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,yEAAyE;IACzE,GAAG,EAAE,wBAAwB,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,8BAA8B;IAC9B,GAAG,EAAE,wBAAwB,CAAC;IAC9B,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;CACnB;AAED,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAEpD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CAmBjF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,0BAA0B,GAAG,IAAI,CAQ7E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAWjF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/path/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAWH,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAElE,MAAM,WAAW,sBAAsB;IACrC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,GAAG,CAAC,EAAE,wBAAwB,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,uDAAuD;IACvD,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAChE,0DAA0D;IAC1D,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,0BAA0B;IACzC,sCAAsC;IACtC,MAAM,EAAE,gBAAgB,CAAC;IACzB,qEAAqE;IACrE,GAAG,EAAE,wBAAwB,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,8BAA8B;IAC9B,GAAG,EAAE,wBAAwB,CAAC;IAC9B,yDAAyD;IACzD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,CAAC;AAEpD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CAkBjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,0BAA0B,GAAG,IAAI,CAa7E;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAUjF"}