modern-text 2.0.9 → 2.1.1

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.
Files changed (28) hide show
  1. package/README.md +27 -18
  2. package/dist/deformations/index.cjs +1 -1
  3. package/dist/deformations/index.d.cts +2 -2
  4. package/dist/deformations/index.d.mts +2 -2
  5. package/dist/deformations/index.d.ts +2 -2
  6. package/dist/deformations/index.mjs +1 -1
  7. package/dist/index.cjs +3 -4
  8. package/dist/index.d.cts +20 -21
  9. package/dist/index.d.mts +20 -21
  10. package/dist/index.d.ts +20 -21
  11. package/dist/index.js +4 -4
  12. package/dist/index.mjs +3 -3
  13. package/dist/shared/{modern-text.CKFvdNej.cjs → modern-text.BAnUZM0U.cjs} +506 -495
  14. package/dist/shared/{modern-text.rc-b3qK8.mjs → modern-text.BdF52nXP.mjs} +506 -494
  15. package/dist/shared/{modern-text.CwEJ2EZq.d.ts → modern-text.CGZmjNV0.d.ts} +1 -1
  16. package/dist/shared/{modern-text.K8BlyK2j.mjs → modern-text.CdKp2H-o.mjs} +13 -4
  17. package/dist/shared/{modern-text.DPlppHEM.d.mts → modern-text.DG27jpKj.d.cts} +111 -125
  18. package/dist/shared/{modern-text.DPlppHEM.d.ts → modern-text.DG27jpKj.d.mts} +111 -125
  19. package/dist/shared/{modern-text.DPlppHEM.d.cts → modern-text.DG27jpKj.d.ts} +111 -125
  20. package/dist/shared/{modern-text.BzOupiZm.d.mts → modern-text.DNXfZmiT.d.mts} +1 -1
  21. package/dist/shared/{modern-text.Dmjryk8u.d.cts → modern-text.DzBaDwbG.d.cts} +1 -1
  22. package/dist/shared/{modern-text.DQWdaYoZ.cjs → modern-text._10DVHum.cjs} +13 -4
  23. package/dist/web-components/index.cjs +2 -2
  24. package/dist/web-components/index.d.cts +1 -1
  25. package/dist/web-components/index.d.mts +1 -1
  26. package/dist/web-components/index.d.ts +1 -1
  27. package/dist/web-components/index.mjs +2 -2
  28. package/package.json +1 -1
package/README.md CHANGED
@@ -19,17 +19,16 @@
19
19
  </p>
20
20
 
21
21
  `modern-text` measures and renders rich text on Canvas with a layout model that
22
- mirrors the browser's. It has no React/Vue dependency, ships ESM + CJS, and can
23
- run **either in the browser (using the DOM as ground truth) or fully DOM-free in
24
- Node / SSR / Web Workers**.
22
+ mirrors the browser's. It has no React/Vue dependency, ships ESM + CJS, and is
23
+ **fully DOM-free** the same measure render pipeline runs in the browser,
24
+ Node, SSR or Web Workers.
25
25
 
26
26
  ## Features
27
27
 
28
28
  - 📐 **DOM-accurate layout** — paragraphs, line wrapping, baselines, alignment.
29
- - 🧩 **Two interchangeable layout backends**
30
- - `DomMeasurer` measures via a hidden DOM tree + `getBoundingClientRect()`.
31
- - `FontMeasurer` pure-JS, computes layout from font glyph metrics; runs with
32
- no `document`, so it works in **Node / SSR / Workers** and is deterministic.
29
+ - 🧩 **Pure-JS, DOM-free layout** — the built-in `Measurer` computes layout from
30
+ `modern-font` glyph metrics + kerning with no `document`, so it works in **Node
31
+ / SSR / Workers**, is deterministic, and measures the exact font it renders.
33
32
  - ↔️ **Horizontal & vertical** writing modes (`horizontal-tb`, `vertical-rl`).
34
33
  - 🅰️ **Rich inline styling** — per-fragment font size/family/weight/style, color,
35
34
  letter-spacing, line-height, text-indent, text-align, vertical-align,
@@ -81,23 +80,23 @@ renderText({
81
80
  })
82
81
  ```
83
82
 
84
- ## Layout backends
83
+ ## Layout backend
85
84
 
86
- By default `modern-text` uses the pure-JS **`'font'`** backend (`FontMeasurer`),
87
- which resolves fonts from the `fonts` you pass or from `modern-font`'s global
88
- registry. Pass `'dom'` to use the browser as ground truth, or a custom
89
- `TextMeasurer`:
85
+ `modern-text` measures with a single built-in **`Measurer`** pure-JS and
86
+ DOM-free — which resolves fonts from the `fonts` you pass or from `modern-font`'s
87
+ global registry. It's the default; to plug in your own engine, pass any object
88
+ implementing `TextMeasurer`:
90
89
 
91
90
  ```ts
92
- new Text({ fonts, measurer: 'font' }) // pure-JS, DOM-free (default)
93
- new Text({ fonts, measurer: 'dom' }) // browser ground truth
91
+ new Text({ fonts }) // uses the built-in Measurer (default)
94
92
  new Text({ measurer: myCustomMeasurer }) // any object implementing TextMeasurer
95
93
  ```
96
94
 
97
95
  ### Node / SSR / Workers
98
96
 
99
- `FontMeasurer` needs no `document`, so the whole measure → render pipeline runs
100
- outside the browser. Register fonts from a buffer with `modern-font`:
97
+ The built-in `Measurer` needs no `document`, so the whole measure → render
98
+ pipeline runs outside the browser. Register fonts from a buffer with
99
+ `modern-font`:
101
100
 
102
101
  ```ts
103
102
  import { readFileSync } from 'node:fs'
@@ -232,11 +231,21 @@ renderText({
232
231
  view,
233
232
  fonts,
234
233
  content: 'Deformation',
235
- style: { fontSize: 100 },
236
- deformation: { type: 'arch-curve' },
234
+ style: { fontSize: 64 },
235
+ deformation: { type: 'arch-curve', intensities: [50] },
237
236
  })
238
237
  ```
239
238
 
239
+ `intensities` is the strength of the effect, `0`–`100` per axis. Most presets
240
+ read a single value (`intensities[0]`); a few are **two-axis** and also read
241
+ `intensities[1]` — e.g. `skew` and `trapezoid` slant/taper independently on the
242
+ x and y axes. A missing axis is treated as `0`.
243
+
244
+ Deformation is **scale-invariant**: the geometry is derived from the text's own
245
+ font size, so the same `intensities` produce the same shape at any `fontSize` —
246
+ you don't pass a reference size. (The legacy `deformation.maxFontSize` field is
247
+ no longer needed and is only used as a fallback when no glyphs are measurable.)
248
+
240
249
  <details>
241
250
  <summary>Available presets (34)</summary>
242
251
 
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const modernPath2d = require('modern-path2d');
4
- const deformation = require('../shared/modern-text.DQWdaYoZ.cjs');
4
+ const deformation = require('../shared/modern-text._10DVHum.cjs');
5
5
  const curves = require('../shared/modern-text.MC5bIC9E.cjs');
6
6
  require('modern-idoc');
7
7
 
@@ -1,6 +1,6 @@
1
- import { c as DeformationPreset } from '../shared/modern-text.Dmjryk8u.cjs';
1
+ import { c as DeformationPreset } from '../shared/modern-text.DzBaDwbG.cjs';
2
2
  import 'modern-path2d';
3
- import '../shared/modern-text.DPlppHEM.cjs';
3
+ import '../shared/modern-text.DG27jpKj.cjs';
4
4
  import 'modern-idoc';
5
5
  import 'modern-font';
6
6
 
@@ -1,6 +1,6 @@
1
- import { c as DeformationPreset } from '../shared/modern-text.BzOupiZm.mjs';
1
+ import { c as DeformationPreset } from '../shared/modern-text.DNXfZmiT.mjs';
2
2
  import 'modern-path2d';
3
- import '../shared/modern-text.DPlppHEM.mjs';
3
+ import '../shared/modern-text.DG27jpKj.mjs';
4
4
  import 'modern-idoc';
5
5
  import 'modern-font';
6
6
 
@@ -1,6 +1,6 @@
1
- import { c as DeformationPreset } from '../shared/modern-text.CwEJ2EZq.js';
1
+ import { c as DeformationPreset } from '../shared/modern-text.CGZmjNV0.js';
2
2
  import 'modern-path2d';
3
- import '../shared/modern-text.DPlppHEM.js';
3
+ import '../shared/modern-text.DG27jpKj.js';
4
4
  import 'modern-idoc';
5
5
  import 'modern-font';
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { Vector2 } from 'modern-path2d';
2
- import { d as defineDeformation } from '../shared/modern-text.K8BlyK2j.mjs';
2
+ import { d as defineDeformation } from '../shared/modern-text.CdKp2H-o.mjs';
3
3
  import { H as HeartCurve, R as RectangularCurve, E as EllipseCurve, C as CircleCurve, P as PolygonCurve } from '../shared/modern-text.fT17R5HY.mjs';
4
4
  import 'modern-idoc';
5
5
 
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const Text = require('./shared/modern-text.CKFvdNej.cjs');
4
- const deformation = require('./shared/modern-text.DQWdaYoZ.cjs');
3
+ const Text = require('./shared/modern-text.BAnUZM0U.cjs');
4
+ const deformation = require('./shared/modern-text._10DVHum.cjs');
5
5
  const curves = require('./shared/modern-text.MC5bIC9E.cjs');
6
6
  require('modern-font');
7
7
  require('modern-idoc');
@@ -29,9 +29,8 @@ function renderText(options, load) {
29
29
 
30
30
  exports.Canvas2DRenderer = Text.Canvas2DRenderer;
31
31
  exports.Character = Text.Character;
32
- exports.DomMeasurer = Text.DomMeasurer;
33
- exports.FontMeasurer = Text.FontMeasurer;
34
32
  exports.Fragment = Text.Fragment;
33
+ exports.Measurer = Text.Measurer;
35
34
  exports.Paragraph = Text.Paragraph;
36
35
  exports.Text = Text.Text;
37
36
  exports.backgroundPlugin = Text.backgroundPlugin;
package/dist/index.d.cts CHANGED
@@ -1,21 +1,20 @@
1
- import { i as Plugin, k as TextMeasurer, P as Paragraph, M as MeasureDomResult, a as Character, O as Options, c as MeasureResult, R as RenderOptions, T as Text } from './shared/modern-text.DPlppHEM.cjs';
2
- export { C as Canvas2DRenderer, D as DomMeasurer, b as DrawShapePathsOptions, F as Fragment, d as MeasuredCharacter, e as MeasuredCharacterRect, f as MeasuredFragment, g as MeasuredParagraph, h as MeasurerKind, j as TextEvents, t as textDefaultStyle } from './shared/modern-text.DPlppHEM.cjs';
1
+ import { c as Plugin, e as TextMeasurer, P as Paragraph, b as MeasurerResult, a as Character, O as Options, M as MeasureResult, R as RenderOptions, T as Text } from './shared/modern-text.DG27jpKj.cjs';
2
+ export { C as Canvas2DRenderer, D as DrawShapePathsOptions, F as Fragment, d as TextEvents, t as textDefaultStyle } from './shared/modern-text.DG27jpKj.cjs';
3
3
  import { Fonts } from 'modern-font';
4
4
  import { FullStyle, NormalizedStyle, NormalizedHighlight, NormalizedEffect } from 'modern-idoc';
5
5
  import { BoundingBox, Vector2, Path2DSet, Transform2D } from 'modern-path2d';
6
- import { c as DeformationPreset } from './shared/modern-text.Dmjryk8u.cjs';
7
- export { B as BendContext, a as BendPreset, C as CurvePreset, D as DeformationCharInfo, b as DeformationCurve, F as FfdContext, d as FfdPreset, O as OffsetPreset, V as VerbatimContext } from './shared/modern-text.Dmjryk8u.cjs';
6
+ import { c as DeformationPreset } from './shared/modern-text.DzBaDwbG.cjs';
7
+ export { B as BendContext, a as BendPreset, C as CurvePreset, D as DeformationCharInfo, b as DeformationCurve, F as FfdContext, d as FfdPreset, O as OffsetPreset, V as VerbatimContext } from './shared/modern-text.DzBaDwbG.cjs';
8
8
 
9
9
  declare function definePlugin(options: Plugin): Plugin;
10
10
 
11
11
  /**
12
- * Pure-JS, DOM-free text measurer a drop-in alternative to {@link DomMeasurer}.
12
+ * The pure-JS, DOM-free text measurer (the only built-in backend).
13
13
  *
14
- * Instead of mounting a `<section>/<ul>/<li>/<span>` tree and reading
15
- * `getBoundingClientRect()`, it computes the same four-level boxes
16
- * (`character.inlineBox`/`lineBox`, `fragment.inlineBox`, `paragraph.lineBox`)
17
- * from `modern-font` glyph advances, so the downstream
18
- * measure → glyph → plugin pipeline is unchanged and it runs in Node/SSR/Worker.
14
+ * It computes the four-level boxes (`character.inlineBox`/`lineBox`, `fragment.inlineBox`,
15
+ * `paragraph.lineBox`) directly from `modern-font` glyph advances + kerning — no DOM, so it
16
+ * runs in Node/SSR/Worker and measures the *exact* font that is rendered (advances come from
17
+ * the same SFNT the glyph paths do, so layout and rendering are pixel-consistent by construction).
19
18
  *
20
19
  * ## Scope
21
20
  * - `horizontal-tb` (LTR) and `vertical-rl` (columns right-to-left, glyphs top↓)
@@ -23,37 +22,36 @@ declare function definePlugin(options: Plugin): Plugin;
23
22
  * - per-line `text-align` (start/left/center/end/right), `text-indent` (first line)
24
23
  * - box model: root + paragraph `padding`/`margin` (horizontal), `letter-spacing`,
25
24
  * `line-height`; block `vertical-align` (top/middle/bottom) at fixed height
25
+ * - pair kerning (GPOS `kern` feature / legacy `kern` table) folded into break + positioning
26
26
  *
27
27
  * ## Not yet implemented (TODO)
28
28
  * - UAX#14 `word-break: normal` + 避头尾/kinsoku (`line-break`)
29
29
  * - BiDi, per-fragment inline `vertical-align`, borders
30
30
  * - vertical: per-paragraph margin/padding and block alignment
31
- * - kerning/ligatures (GSUB/GPOS) — fine for CJK, diverges for proportional Latin
31
+ * - ligatures (GSUB) — fine for CJK and most UI Latin
32
32
  *
33
- * Coordinates are relative to the root border-box top-left (matching the DOM
34
- * measurer, whose rects are taken relative to `section.getBoundingClientRect()`).
33
+ * Coordinates are relative to the root border-box top-left.
35
34
  */
36
- declare class FontMeasurer implements TextMeasurer {
37
- measure(paragraphs: Paragraph[], rootStyle: FullStyle, _dom?: HTMLElement, fonts?: Fonts): MeasureDomResult;
35
+ declare class Measurer implements TextMeasurer {
36
+ measure(paragraphs: Paragraph[], rootStyle: FullStyle, _dom?: HTMLElement, fonts?: Fonts): MeasurerResult;
38
37
  protected _rootPadding(rootStyle: FullStyle): {
39
38
  top: number;
40
39
  right: number;
41
40
  bottom: number;
42
41
  left: number;
43
42
  };
44
- protected _measureHorizontal(paragraphs: Paragraph[], rootStyle: FullStyle): MeasureDomResult;
43
+ protected _measureHorizontal(paragraphs: Paragraph[], rootStyle: FullStyle): MeasurerResult;
45
44
  /**
46
45
  * Vertical writing-mode (`vertical-rl`): columns stack right-to-left, glyphs
47
46
  * flow top→bottom. It is the horizontal layout with the inline and block axes
48
47
  * swapped — the inline (down-column) advance is `advanceWidth` (CJK upright = em;
49
48
  * Latin is rotated, so its advance ≈ its width), the cross-axis content box is
50
- * `advanceHeight`, and the column thickness is `fontHeight`. The lineBox is
51
- * derived exactly as DomMeasurer.measureParagraphDom's vertical branch, so it
52
- * stays accurate even though inlineBox carries the content-box rounding residual.
49
+ * `advanceHeight`, and the column thickness is `fontHeight`. The lineBox stays
50
+ * accurate even though inlineBox carries the content-box rounding residual.
53
51
  *
54
52
  * v1: `vertical-rl` only; no per-paragraph margin/padding or block alignment.
55
53
  */
56
- protected _measureVertical(paragraphs: Paragraph[], rootStyle: FullStyle): MeasureDomResult;
54
+ protected _measureVertical(paragraphs: Paragraph[], rootStyle: FullStyle): MeasurerResult;
57
55
  /** Advance step including CSS letter-spacing (px). */
58
56
  protected _advance(character: Character): number;
59
57
  /**
@@ -62,6 +60,7 @@ declare class FontMeasurer implements TextMeasurer {
62
60
  * plus explicit `\n`/`\r` hard breaks. The newline itself occupies no line box.
63
61
  */
64
62
  protected _breakLines(paragraph: Paragraph, avail: number): Character[][];
63
+ protected _applyKerning(paragraphs: Paragraph[]): void;
65
64
  protected _unionInto(target: BoundingBox, boxes: BoundingBox[]): void;
66
65
  protected _shiftAll(paragraphs: Paragraph[], dy: number): void;
67
66
  dispose(): void;
@@ -240,5 +239,5 @@ declare function parseColormap(colormap: 'none' | Record<string, string>): Recor
240
239
  declare function isEqualObject(obj1: Record<string, any>, obj2: Record<string, any>): boolean;
241
240
  declare function isEqualValue(val1: any, val2: any): boolean;
242
241
 
243
- export { Character, CircleCurve, DeformationPreset, EllipseCurve, FontMeasurer, HeartCurve, MeasureDomResult, MeasureResult, Options, Paragraph, Plugin, PolygonCurve, RectangularCurve, RenderOptions, Text, TextMeasurer, backgroundPlugin, createSvgLoader, createSvgParser, defineDeformation, definePlugin, deformationPlugin, getDeformationNames, getEffectTransform2D, getHighlightStyle, highlightPlugin, isEqualObject, isEqualValue, listStylePlugin, measureText, outlinePlugin, parseColormap, parseTransformOrigin, parseValueNumber, removeDeformation, renderPlugin, renderText, textDecorationPlugin };
242
+ export { Character, CircleCurve, DeformationPreset, EllipseCurve, HeartCurve, MeasureResult, Measurer, MeasurerResult, Options, Paragraph, Plugin, PolygonCurve, RectangularCurve, RenderOptions, Text, TextMeasurer, backgroundPlugin, createSvgLoader, createSvgParser, defineDeformation, definePlugin, deformationPlugin, getDeformationNames, getEffectTransform2D, getHighlightStyle, highlightPlugin, isEqualObject, isEqualValue, listStylePlugin, measureText, outlinePlugin, parseColormap, parseTransformOrigin, parseValueNumber, removeDeformation, renderPlugin, renderText, textDecorationPlugin };
244
243
  export type { RenderTextOptions, SvgLoader, SvgParser };
package/dist/index.d.mts CHANGED
@@ -1,21 +1,20 @@
1
- import { i as Plugin, k as TextMeasurer, P as Paragraph, M as MeasureDomResult, a as Character, O as Options, c as MeasureResult, R as RenderOptions, T as Text } from './shared/modern-text.DPlppHEM.mjs';
2
- export { C as Canvas2DRenderer, D as DomMeasurer, b as DrawShapePathsOptions, F as Fragment, d as MeasuredCharacter, e as MeasuredCharacterRect, f as MeasuredFragment, g as MeasuredParagraph, h as MeasurerKind, j as TextEvents, t as textDefaultStyle } from './shared/modern-text.DPlppHEM.mjs';
1
+ import { c as Plugin, e as TextMeasurer, P as Paragraph, b as MeasurerResult, a as Character, O as Options, M as MeasureResult, R as RenderOptions, T as Text } from './shared/modern-text.DG27jpKj.mjs';
2
+ export { C as Canvas2DRenderer, D as DrawShapePathsOptions, F as Fragment, d as TextEvents, t as textDefaultStyle } from './shared/modern-text.DG27jpKj.mjs';
3
3
  import { Fonts } from 'modern-font';
4
4
  import { FullStyle, NormalizedStyle, NormalizedHighlight, NormalizedEffect } from 'modern-idoc';
5
5
  import { BoundingBox, Vector2, Path2DSet, Transform2D } from 'modern-path2d';
6
- import { c as DeformationPreset } from './shared/modern-text.BzOupiZm.mjs';
7
- export { B as BendContext, a as BendPreset, C as CurvePreset, D as DeformationCharInfo, b as DeformationCurve, F as FfdContext, d as FfdPreset, O as OffsetPreset, V as VerbatimContext } from './shared/modern-text.BzOupiZm.mjs';
6
+ import { c as DeformationPreset } from './shared/modern-text.DNXfZmiT.mjs';
7
+ export { B as BendContext, a as BendPreset, C as CurvePreset, D as DeformationCharInfo, b as DeformationCurve, F as FfdContext, d as FfdPreset, O as OffsetPreset, V as VerbatimContext } from './shared/modern-text.DNXfZmiT.mjs';
8
8
 
9
9
  declare function definePlugin(options: Plugin): Plugin;
10
10
 
11
11
  /**
12
- * Pure-JS, DOM-free text measurer a drop-in alternative to {@link DomMeasurer}.
12
+ * The pure-JS, DOM-free text measurer (the only built-in backend).
13
13
  *
14
- * Instead of mounting a `<section>/<ul>/<li>/<span>` tree and reading
15
- * `getBoundingClientRect()`, it computes the same four-level boxes
16
- * (`character.inlineBox`/`lineBox`, `fragment.inlineBox`, `paragraph.lineBox`)
17
- * from `modern-font` glyph advances, so the downstream
18
- * measure → glyph → plugin pipeline is unchanged and it runs in Node/SSR/Worker.
14
+ * It computes the four-level boxes (`character.inlineBox`/`lineBox`, `fragment.inlineBox`,
15
+ * `paragraph.lineBox`) directly from `modern-font` glyph advances + kerning — no DOM, so it
16
+ * runs in Node/SSR/Worker and measures the *exact* font that is rendered (advances come from
17
+ * the same SFNT the glyph paths do, so layout and rendering are pixel-consistent by construction).
19
18
  *
20
19
  * ## Scope
21
20
  * - `horizontal-tb` (LTR) and `vertical-rl` (columns right-to-left, glyphs top↓)
@@ -23,37 +22,36 @@ declare function definePlugin(options: Plugin): Plugin;
23
22
  * - per-line `text-align` (start/left/center/end/right), `text-indent` (first line)
24
23
  * - box model: root + paragraph `padding`/`margin` (horizontal), `letter-spacing`,
25
24
  * `line-height`; block `vertical-align` (top/middle/bottom) at fixed height
25
+ * - pair kerning (GPOS `kern` feature / legacy `kern` table) folded into break + positioning
26
26
  *
27
27
  * ## Not yet implemented (TODO)
28
28
  * - UAX#14 `word-break: normal` + 避头尾/kinsoku (`line-break`)
29
29
  * - BiDi, per-fragment inline `vertical-align`, borders
30
30
  * - vertical: per-paragraph margin/padding and block alignment
31
- * - kerning/ligatures (GSUB/GPOS) — fine for CJK, diverges for proportional Latin
31
+ * - ligatures (GSUB) — fine for CJK and most UI Latin
32
32
  *
33
- * Coordinates are relative to the root border-box top-left (matching the DOM
34
- * measurer, whose rects are taken relative to `section.getBoundingClientRect()`).
33
+ * Coordinates are relative to the root border-box top-left.
35
34
  */
36
- declare class FontMeasurer implements TextMeasurer {
37
- measure(paragraphs: Paragraph[], rootStyle: FullStyle, _dom?: HTMLElement, fonts?: Fonts): MeasureDomResult;
35
+ declare class Measurer implements TextMeasurer {
36
+ measure(paragraphs: Paragraph[], rootStyle: FullStyle, _dom?: HTMLElement, fonts?: Fonts): MeasurerResult;
38
37
  protected _rootPadding(rootStyle: FullStyle): {
39
38
  top: number;
40
39
  right: number;
41
40
  bottom: number;
42
41
  left: number;
43
42
  };
44
- protected _measureHorizontal(paragraphs: Paragraph[], rootStyle: FullStyle): MeasureDomResult;
43
+ protected _measureHorizontal(paragraphs: Paragraph[], rootStyle: FullStyle): MeasurerResult;
45
44
  /**
46
45
  * Vertical writing-mode (`vertical-rl`): columns stack right-to-left, glyphs
47
46
  * flow top→bottom. It is the horizontal layout with the inline and block axes
48
47
  * swapped — the inline (down-column) advance is `advanceWidth` (CJK upright = em;
49
48
  * Latin is rotated, so its advance ≈ its width), the cross-axis content box is
50
- * `advanceHeight`, and the column thickness is `fontHeight`. The lineBox is
51
- * derived exactly as DomMeasurer.measureParagraphDom's vertical branch, so it
52
- * stays accurate even though inlineBox carries the content-box rounding residual.
49
+ * `advanceHeight`, and the column thickness is `fontHeight`. The lineBox stays
50
+ * accurate even though inlineBox carries the content-box rounding residual.
53
51
  *
54
52
  * v1: `vertical-rl` only; no per-paragraph margin/padding or block alignment.
55
53
  */
56
- protected _measureVertical(paragraphs: Paragraph[], rootStyle: FullStyle): MeasureDomResult;
54
+ protected _measureVertical(paragraphs: Paragraph[], rootStyle: FullStyle): MeasurerResult;
57
55
  /** Advance step including CSS letter-spacing (px). */
58
56
  protected _advance(character: Character): number;
59
57
  /**
@@ -62,6 +60,7 @@ declare class FontMeasurer implements TextMeasurer {
62
60
  * plus explicit `\n`/`\r` hard breaks. The newline itself occupies no line box.
63
61
  */
64
62
  protected _breakLines(paragraph: Paragraph, avail: number): Character[][];
63
+ protected _applyKerning(paragraphs: Paragraph[]): void;
65
64
  protected _unionInto(target: BoundingBox, boxes: BoundingBox[]): void;
66
65
  protected _shiftAll(paragraphs: Paragraph[], dy: number): void;
67
66
  dispose(): void;
@@ -240,5 +239,5 @@ declare function parseColormap(colormap: 'none' | Record<string, string>): Recor
240
239
  declare function isEqualObject(obj1: Record<string, any>, obj2: Record<string, any>): boolean;
241
240
  declare function isEqualValue(val1: any, val2: any): boolean;
242
241
 
243
- export { Character, CircleCurve, DeformationPreset, EllipseCurve, FontMeasurer, HeartCurve, MeasureDomResult, MeasureResult, Options, Paragraph, Plugin, PolygonCurve, RectangularCurve, RenderOptions, Text, TextMeasurer, backgroundPlugin, createSvgLoader, createSvgParser, defineDeformation, definePlugin, deformationPlugin, getDeformationNames, getEffectTransform2D, getHighlightStyle, highlightPlugin, isEqualObject, isEqualValue, listStylePlugin, measureText, outlinePlugin, parseColormap, parseTransformOrigin, parseValueNumber, removeDeformation, renderPlugin, renderText, textDecorationPlugin };
242
+ export { Character, CircleCurve, DeformationPreset, EllipseCurve, HeartCurve, MeasureResult, Measurer, MeasurerResult, Options, Paragraph, Plugin, PolygonCurve, RectangularCurve, RenderOptions, Text, TextMeasurer, backgroundPlugin, createSvgLoader, createSvgParser, defineDeformation, definePlugin, deformationPlugin, getDeformationNames, getEffectTransform2D, getHighlightStyle, highlightPlugin, isEqualObject, isEqualValue, listStylePlugin, measureText, outlinePlugin, parseColormap, parseTransformOrigin, parseValueNumber, removeDeformation, renderPlugin, renderText, textDecorationPlugin };
244
243
  export type { RenderTextOptions, SvgLoader, SvgParser };
package/dist/index.d.ts CHANGED
@@ -1,21 +1,20 @@
1
- import { i as Plugin, k as TextMeasurer, P as Paragraph, M as MeasureDomResult, a as Character, O as Options, c as MeasureResult, R as RenderOptions, T as Text } from './shared/modern-text.DPlppHEM.js';
2
- export { C as Canvas2DRenderer, D as DomMeasurer, b as DrawShapePathsOptions, F as Fragment, d as MeasuredCharacter, e as MeasuredCharacterRect, f as MeasuredFragment, g as MeasuredParagraph, h as MeasurerKind, j as TextEvents, t as textDefaultStyle } from './shared/modern-text.DPlppHEM.js';
1
+ import { c as Plugin, e as TextMeasurer, P as Paragraph, b as MeasurerResult, a as Character, O as Options, M as MeasureResult, R as RenderOptions, T as Text } from './shared/modern-text.DG27jpKj.js';
2
+ export { C as Canvas2DRenderer, D as DrawShapePathsOptions, F as Fragment, d as TextEvents, t as textDefaultStyle } from './shared/modern-text.DG27jpKj.js';
3
3
  import { Fonts } from 'modern-font';
4
4
  import { FullStyle, NormalizedStyle, NormalizedHighlight, NormalizedEffect } from 'modern-idoc';
5
5
  import { BoundingBox, Vector2, Path2DSet, Transform2D } from 'modern-path2d';
6
- import { c as DeformationPreset } from './shared/modern-text.CwEJ2EZq.js';
7
- export { B as BendContext, a as BendPreset, C as CurvePreset, D as DeformationCharInfo, b as DeformationCurve, F as FfdContext, d as FfdPreset, O as OffsetPreset, V as VerbatimContext } from './shared/modern-text.CwEJ2EZq.js';
6
+ import { c as DeformationPreset } from './shared/modern-text.CGZmjNV0.js';
7
+ export { B as BendContext, a as BendPreset, C as CurvePreset, D as DeformationCharInfo, b as DeformationCurve, F as FfdContext, d as FfdPreset, O as OffsetPreset, V as VerbatimContext } from './shared/modern-text.CGZmjNV0.js';
8
8
 
9
9
  declare function definePlugin(options: Plugin): Plugin;
10
10
 
11
11
  /**
12
- * Pure-JS, DOM-free text measurer a drop-in alternative to {@link DomMeasurer}.
12
+ * The pure-JS, DOM-free text measurer (the only built-in backend).
13
13
  *
14
- * Instead of mounting a `<section>/<ul>/<li>/<span>` tree and reading
15
- * `getBoundingClientRect()`, it computes the same four-level boxes
16
- * (`character.inlineBox`/`lineBox`, `fragment.inlineBox`, `paragraph.lineBox`)
17
- * from `modern-font` glyph advances, so the downstream
18
- * measure → glyph → plugin pipeline is unchanged and it runs in Node/SSR/Worker.
14
+ * It computes the four-level boxes (`character.inlineBox`/`lineBox`, `fragment.inlineBox`,
15
+ * `paragraph.lineBox`) directly from `modern-font` glyph advances + kerning — no DOM, so it
16
+ * runs in Node/SSR/Worker and measures the *exact* font that is rendered (advances come from
17
+ * the same SFNT the glyph paths do, so layout and rendering are pixel-consistent by construction).
19
18
  *
20
19
  * ## Scope
21
20
  * - `horizontal-tb` (LTR) and `vertical-rl` (columns right-to-left, glyphs top↓)
@@ -23,37 +22,36 @@ declare function definePlugin(options: Plugin): Plugin;
23
22
  * - per-line `text-align` (start/left/center/end/right), `text-indent` (first line)
24
23
  * - box model: root + paragraph `padding`/`margin` (horizontal), `letter-spacing`,
25
24
  * `line-height`; block `vertical-align` (top/middle/bottom) at fixed height
25
+ * - pair kerning (GPOS `kern` feature / legacy `kern` table) folded into break + positioning
26
26
  *
27
27
  * ## Not yet implemented (TODO)
28
28
  * - UAX#14 `word-break: normal` + 避头尾/kinsoku (`line-break`)
29
29
  * - BiDi, per-fragment inline `vertical-align`, borders
30
30
  * - vertical: per-paragraph margin/padding and block alignment
31
- * - kerning/ligatures (GSUB/GPOS) — fine for CJK, diverges for proportional Latin
31
+ * - ligatures (GSUB) — fine for CJK and most UI Latin
32
32
  *
33
- * Coordinates are relative to the root border-box top-left (matching the DOM
34
- * measurer, whose rects are taken relative to `section.getBoundingClientRect()`).
33
+ * Coordinates are relative to the root border-box top-left.
35
34
  */
36
- declare class FontMeasurer implements TextMeasurer {
37
- measure(paragraphs: Paragraph[], rootStyle: FullStyle, _dom?: HTMLElement, fonts?: Fonts): MeasureDomResult;
35
+ declare class Measurer implements TextMeasurer {
36
+ measure(paragraphs: Paragraph[], rootStyle: FullStyle, _dom?: HTMLElement, fonts?: Fonts): MeasurerResult;
38
37
  protected _rootPadding(rootStyle: FullStyle): {
39
38
  top: number;
40
39
  right: number;
41
40
  bottom: number;
42
41
  left: number;
43
42
  };
44
- protected _measureHorizontal(paragraphs: Paragraph[], rootStyle: FullStyle): MeasureDomResult;
43
+ protected _measureHorizontal(paragraphs: Paragraph[], rootStyle: FullStyle): MeasurerResult;
45
44
  /**
46
45
  * Vertical writing-mode (`vertical-rl`): columns stack right-to-left, glyphs
47
46
  * flow top→bottom. It is the horizontal layout with the inline and block axes
48
47
  * swapped — the inline (down-column) advance is `advanceWidth` (CJK upright = em;
49
48
  * Latin is rotated, so its advance ≈ its width), the cross-axis content box is
50
- * `advanceHeight`, and the column thickness is `fontHeight`. The lineBox is
51
- * derived exactly as DomMeasurer.measureParagraphDom's vertical branch, so it
52
- * stays accurate even though inlineBox carries the content-box rounding residual.
49
+ * `advanceHeight`, and the column thickness is `fontHeight`. The lineBox stays
50
+ * accurate even though inlineBox carries the content-box rounding residual.
53
51
  *
54
52
  * v1: `vertical-rl` only; no per-paragraph margin/padding or block alignment.
55
53
  */
56
- protected _measureVertical(paragraphs: Paragraph[], rootStyle: FullStyle): MeasureDomResult;
54
+ protected _measureVertical(paragraphs: Paragraph[], rootStyle: FullStyle): MeasurerResult;
57
55
  /** Advance step including CSS letter-spacing (px). */
58
56
  protected _advance(character: Character): number;
59
57
  /**
@@ -62,6 +60,7 @@ declare class FontMeasurer implements TextMeasurer {
62
60
  * plus explicit `\n`/`\r` hard breaks. The newline itself occupies no line box.
63
61
  */
64
62
  protected _breakLines(paragraph: Paragraph, avail: number): Character[][];
63
+ protected _applyKerning(paragraphs: Paragraph[]): void;
65
64
  protected _unionInto(target: BoundingBox, boxes: BoundingBox[]): void;
66
65
  protected _shiftAll(paragraphs: Paragraph[], dy: number): void;
67
66
  dispose(): void;
@@ -240,5 +239,5 @@ declare function parseColormap(colormap: 'none' | Record<string, string>): Recor
240
239
  declare function isEqualObject(obj1: Record<string, any>, obj2: Record<string, any>): boolean;
241
240
  declare function isEqualValue(val1: any, val2: any): boolean;
242
241
 
243
- export { Character, CircleCurve, DeformationPreset, EllipseCurve, FontMeasurer, HeartCurve, MeasureDomResult, MeasureResult, Options, Paragraph, Plugin, PolygonCurve, RectangularCurve, RenderOptions, Text, TextMeasurer, backgroundPlugin, createSvgLoader, createSvgParser, defineDeformation, definePlugin, deformationPlugin, getDeformationNames, getEffectTransform2D, getHighlightStyle, highlightPlugin, isEqualObject, isEqualValue, listStylePlugin, measureText, outlinePlugin, parseColormap, parseTransformOrigin, parseValueNumber, removeDeformation, renderPlugin, renderText, textDecorationPlugin };
242
+ export { Character, CircleCurve, DeformationPreset, EllipseCurve, HeartCurve, MeasureResult, Measurer, MeasurerResult, Options, Paragraph, Plugin, PolygonCurve, RectangularCurve, RenderOptions, Text, TextMeasurer, backgroundPlugin, createSvgLoader, createSvgParser, defineDeformation, definePlugin, deformationPlugin, getDeformationNames, getEffectTransform2D, getHighlightStyle, highlightPlugin, isEqualObject, isEqualValue, listStylePlugin, measureText, outlinePlugin, parseColormap, parseTransformOrigin, parseValueNumber, removeDeformation, renderPlugin, renderText, textDecorationPlugin };
244
243
  export type { RenderTextOptions, SvgLoader, SvgParser };