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
@@ -1,6 +1,6 @@
1
1
  import { NormalizedStyle, NormalizedFill, NormalizedOutline, FullStyle, TextObject, ReactivableEvents, Reactivable, NormalizedText, NormalizedEffect, LinearGradientWithType, RadialGradientWithType, NormalizedShadow } from 'modern-idoc';
2
2
  import { BoundingBox, Path2D, Vector2, Vector2Like, Path2DSet, Path2DStyle } from 'modern-path2d';
3
- import { Fonts, SFNT } from 'modern-font';
3
+ import { SFNT, Fonts } from 'modern-font';
4
4
 
5
5
  declare class Fragment {
6
6
  readonly content: string;
@@ -20,22 +20,21 @@ declare class Fragment {
20
20
  initCharacters(): this;
21
21
  }
22
22
 
23
- declare class Character {
24
- content: string;
25
- index: number;
26
- parent: Fragment;
27
- path: Path2D<Character>;
28
- lineBox: BoundingBox;
29
- inlineBox: BoundingBox;
23
+ interface GlyphTemplate {
24
+ path: Path2D;
30
25
  glyphBox?: BoundingBox;
31
- advanceWidth: number;
26
+ }
27
+ interface FontMetrics {
28
+ sfnt: SFNT;
29
+ unitsPerEm: number;
32
30
  advanceHeight: number;
31
+ baseline: number;
32
+ ascender: number;
33
+ descender: number;
33
34
  underlinePosition: number;
34
35
  underlineThickness: number;
35
36
  strikeoutPosition: number;
36
37
  strikeoutSize: number;
37
- ascender: number;
38
- descender: number;
39
38
  typoAscender: number;
40
39
  typoDescender: number;
41
40
  typoLineGap: number;
@@ -43,9 +42,47 @@ declare class Character {
43
42
  winDescent: number;
44
43
  xHeight: number;
45
44
  capHeight: number;
46
- baseline: number;
47
45
  centerDiviation: number;
48
46
  fontStyle?: 'bold' | 'italic';
47
+ }
48
+ declare class Character {
49
+ content: string;
50
+ index: number;
51
+ parent: Fragment;
52
+ protected _path?: Path2D<Character>;
53
+ protected _lazyPath?: {
54
+ tmpl: GlyphTemplate;
55
+ x: number;
56
+ y: number;
57
+ style: any;
58
+ };
59
+ get path(): Path2D<Character>;
60
+ set path(value: Path2D<Character>);
61
+ inlineBox: BoundingBox;
62
+ get lineBox(): BoundingBox;
63
+ glyphBox?: BoundingBox;
64
+ advanceWidth: number;
65
+ kerningBefore: number;
66
+ protected _glyphIndex?: number;
67
+ protected _metrics?: FontMetrics;
68
+ get advanceHeight(): number;
69
+ get baseline(): number;
70
+ get ascender(): number;
71
+ get descender(): number;
72
+ get underlinePosition(): number;
73
+ get underlineThickness(): number;
74
+ get strikeoutPosition(): number;
75
+ get strikeoutSize(): number;
76
+ get typoAscender(): number;
77
+ get typoDescender(): number;
78
+ get typoLineGap(): number;
79
+ get winAscent(): number;
80
+ get winDescent(): number;
81
+ get xHeight(): number;
82
+ get capHeight(): number;
83
+ get centerDiviation(): number;
84
+ get fontStyle(): 'bold' | 'italic' | undefined;
85
+ translateY(dy: number): void;
49
86
  get compatibleGlyphBox(): BoundingBox;
50
87
  get center(): Vector2;
51
88
  get computedFill(): NormalizedFill | undefined;
@@ -57,17 +94,18 @@ declare class Character {
57
94
  constructor(content: string, index: number, parent: Fragment);
58
95
  protected _getFontSFNT(fonts?: Fonts): SFNT | undefined;
59
96
  updateGlyph(sfnt?: SFNT | undefined): this;
97
+ computeKerningBefore(prev?: Character): number;
60
98
  /**
61
99
  * Populate glyph metrics only (advance width/height, ascender/descender,
62
100
  * baseline, …) without building the glyph `path` or touching boxes.
63
101
  *
64
- * The DOM {@link DomMeasurer} never needs this it reads positions back from the
65
- * browser. A pure-JS measurer (e.g. `FontMeasurer`) must know advances *before*
66
- * it can place characters, so it calls this ahead of layout. `update()` later
67
- * recomputes the same metrics while building the path, so this is idempotent.
102
+ * The pure-JS `Measurer` must know advances *before* it can place characters,
103
+ * so it calls this ahead of layout. `update()` later recomputes the same metrics
104
+ * while building the path, so this is idempotent.
68
105
  */
69
106
  measureGlyph(fonts?: Fonts): this;
70
107
  update(fonts?: Fonts): this;
108
+ protected _updateFromCache(sfnt: SFNT, style: FullStyle): void;
71
109
  protected _italic(path: Path2D, startPoint?: Vector2Like): void;
72
110
  getGlyphMinMax(min?: Vector2, max?: Vector2, withStyle?: boolean): {
73
111
  min: Vector2;
@@ -76,97 +114,24 @@ declare class Character {
76
114
  getGlyphBoundingBox(withStyle?: boolean): BoundingBox | undefined;
77
115
  }
78
116
 
79
- interface MeasuredParagraph {
80
- paragraphIndex: number;
81
- left: number;
82
- top: number;
83
- width: number;
84
- height: number;
85
- }
86
- interface MeasuredFragment {
87
- paragraphIndex: number;
88
- fragmentIndex: number;
89
- left: number;
90
- top: number;
91
- width: number;
92
- height: number;
93
- }
94
- interface MeasuredCharacter {
95
- paragraphIndex: number;
96
- fragmentIndex: number;
97
- characterIndex: number;
98
- newParagraphIndex: number;
99
- content: string;
100
- left: number;
101
- top: number;
102
- width: number;
103
- height: number;
104
- textHeight: number;
105
- textWidth: number;
106
- }
107
- interface MeasuredCharacterRect {
108
- content: string;
109
- top: number;
110
- left: number;
111
- width: number;
112
- height: number;
113
- }
114
- interface MeasureDomResult {
117
+ /**
118
+ * A measurer's output: the paragraphs with their four-level boxes filled in place,
119
+ * plus the overall bounding box. (Formerly `MeasureDomResult`; layout is now
120
+ * DOM-free via {@link import('./Measurer').Measurer}.)
121
+ */
122
+ interface MeasurerResult {
115
123
  paragraphs: Paragraph[];
116
124
  boundingBox: BoundingBox;
117
125
  }
118
- interface RootDomStyles {
119
- section: Record<string, any>;
120
- ul: Record<string, any>;
121
- }
122
- declare class DomMeasurer {
123
- static notZeroStyles: Set<string>;
124
- static pxStyles: Set<string>;
125
- protected _styleCache: WeakMap<object, Record<string, any>>;
126
- protected _mountedDom?: HTMLElement;
127
- protected _mountedSignature?: string;
128
- protected _toDomStyle(style: Record<string, any>): Record<string, any>;
129
- protected _resolveRootStyles(rootStyle: FullStyle): RootDomStyles;
130
- protected _applyRootStyle(section: HTMLElement, ul: HTMLElement, rootStyle: FullStyle): void;
131
- protected _applyLiStyle(li: HTMLElement, paragraph: Paragraph): void;
132
- protected _applySpanStyle(span: HTMLElement, fragment: Fragment): void;
133
- protected _applyFragmentContent(span: HTMLElement, fragment: Fragment): void;
134
- protected _hide(element: HTMLElement): void;
135
- protected _signature(paragraphs: Paragraph[]): string;
136
- /**
137
- * <section style="...">
138
- * <ul>
139
- * <li style="...">
140
- * <span style="...">...</span>
141
- * <span>...</span>
142
- * </li>
143
- * </ul>
144
- * </section>
145
- */
146
- createDom(paragraphs: Paragraph[], rootStyle: FullStyle): HTMLElement;
147
- protected _patchDom(dom: HTMLElement, paragraphs: Paragraph[], rootStyle: FullStyle, hidden: boolean): void;
148
- measureDomText(text: Text): MeasuredCharacterRect[];
149
- measureDom(dom: HTMLElement): {
150
- paragraphs: MeasuredParagraph[];
151
- fragments: MeasuredFragment[];
152
- characters: MeasuredCharacter[];
153
- };
154
- measureParagraphDom(paragraphs: Paragraph[], dom: HTMLElement): MeasureDomResult;
155
- measure(paragraphs: Paragraph[], rootStyle: FullStyle, dom?: HTMLElement): MeasureDomResult;
156
- protected _mount(paragraphs: Paragraph[], rootStyle: FullStyle): HTMLElement;
157
- protected _unmount(): void;
158
- dispose(): void;
159
- }
160
-
161
126
  interface Plugin {
162
127
  name: string;
163
128
  pathSet?: Path2DSet;
164
- getBoundingBox?: (text: Text$1) => BoundingBox | undefined;
165
- update?: (text: Text$1) => void;
129
+ getBoundingBox?: (text: Text) => BoundingBox | undefined;
130
+ update?: (text: Text) => void;
166
131
  updateOrder?: number;
167
132
  render?: (renderer: Canvas2DRenderer) => void;
168
133
  renderOrder?: number;
169
- load?: (text: Text$1) => Promise<void>;
134
+ load?: (text: Text) => Promise<void>;
170
135
  context?: Record<string, any>;
171
136
  }
172
137
  /**
@@ -174,34 +139,32 @@ interface Plugin {
174
139
  * (`character.inlineBox`/`lineBox`, `fragment.inlineBox`, `paragraph.lineBox`)
175
140
  * in place and return the overall bounding box.
176
141
  *
177
- * - `DomMeasurer` DOM-based, uses the browser as ground truth (default).
178
- * - `FontMeasurer` pure-JS, DOM-free; needs `fonts` to resolve glyph advances.
179
- *
180
- * `fonts` is passed positionally by `Text.measure()`; DOM-based measurers ignore
181
- * it (a method may safely declare fewer parameters than the interface).
142
+ * The built-in backend is the pure-JS, DOM-free {@link import('./Measurer').Measurer},
143
+ * which resolves glyph advances/kerning from `modern-font` it runs in Node/SSR/Worker
144
+ * and measures the exact font that is rendered. `fonts` is passed positionally by
145
+ * `Text.measure()`.
182
146
  */
183
147
  interface TextMeasurer {
184
- measure: (paragraphs: Paragraph[], rootStyle: FullStyle, dom?: HTMLElement, fonts?: Fonts) => MeasureDomResult;
185
- createDom?: (paragraphs: Paragraph[], rootStyle: FullStyle) => HTMLElement;
148
+ measure: (paragraphs: Paragraph[], rootStyle: FullStyle, dom?: HTMLElement, fonts?: Fonts) => MeasurerResult;
186
149
  dispose?: () => void;
187
150
  }
188
- /** Built-in layout backends. `'font'` → `FontMeasurer`, `'dom'` → `DomMeasurer`. */
189
- type MeasurerKind = 'dom' | 'font';
190
151
  interface Options extends TextObject {
191
152
  debug?: boolean;
192
153
  measureDom?: HTMLElement;
193
154
  fonts?: Fonts;
194
155
  plugins?: Plugin[];
195
- /**
196
- * Layout backend: `'font'` (pure-JS) or `'dom'` (browser), or a custom
197
- * `TextMeasurer`. Defaults to `'font'`.
198
- */
199
- measurer?: MeasurerKind | TextMeasurer;
200
156
  }
201
157
 
202
158
  interface RenderOptions {
203
159
  view: HTMLCanvasElement;
204
160
  pixelRatio?: number;
161
+ /** 只渲染 boundingBox 内的这一子块(相对偏移 + 尺寸),用于超大文字按 GPU 上限分块栅格。 */
162
+ region?: {
163
+ x: number;
164
+ y: number;
165
+ width: number;
166
+ height: number;
167
+ };
205
168
  onContext?: (context: CanvasRenderingContext2D) => void;
206
169
  }
207
170
  interface MeasureResult {
@@ -215,25 +178,25 @@ interface MeasureResult {
215
178
  declare const textDefaultStyle: FullStyle;
216
179
  interface TextEvents extends ReactivableEvents {
217
180
  update: [ctx: {
218
- text: Text$1;
181
+ text: Text;
219
182
  }];
220
183
  measure: [ctx: {
221
- text: Text$1;
184
+ text: Text;
222
185
  result: MeasureResult;
223
186
  }];
224
187
  render: [ctx: {
225
- text: Text$1;
188
+ text: Text;
226
189
  view: HTMLCanvasElement;
227
190
  pixelRatio: number;
228
191
  }];
229
192
  }
230
- interface Text$1 {
193
+ interface Text {
231
194
  on: <K extends keyof TextEvents & string>(event: K, listener: (...args: TextEvents[K]) => void) => this;
232
195
  once: <K extends keyof TextEvents & string>(event: K, listener: (...args: TextEvents[K]) => void) => this;
233
196
  off: <K extends keyof TextEvents & string>(event: K, listener: (...args: TextEvents[K]) => void) => this;
234
197
  emit: <K extends keyof TextEvents & string>(event: K, ...args: TextEvents[K]) => this;
235
198
  }
236
- declare class Text$1 extends Reactivable {
199
+ declare class Text extends Reactivable {
237
200
  debug: boolean;
238
201
  content: NormalizedText['content'];
239
202
  style?: NormalizedText['style'];
@@ -259,6 +222,14 @@ declare class Text$1 extends Reactivable {
259
222
  pathSets: Path2DSet[];
260
223
  protected _paragraphs: Paragraph[];
261
224
  protected _cachedCharacters?: Character[];
225
+ incrementalLayout: boolean;
226
+ protected _prevParagraphs: Paragraph[];
227
+ protected _prevContentKeys: string[];
228
+ protected _prevStyleKey: string;
229
+ protected _prevFonts: unknown;
230
+ protected _prevFontsSet: boolean;
231
+ protected _pendingContentKeys: string[];
232
+ protected _pendingStyleKey: string;
262
233
  protected _pluginsByUpdateOrder: Plugin[];
263
234
  protected _pluginsByRenderOrder: Plugin[];
264
235
  protected _renderer?: Canvas2DRenderer;
@@ -287,7 +258,7 @@ declare class Text$1 extends Reactivable {
287
258
  *
288
259
  * No-op for already-decoded fonts and for formats without async decoding.
289
260
  */
290
- protected _decodeFonts(): Promise<void>;
261
+ protected _decodeFonts(): Promise<number>;
291
262
  /**
292
263
  * Coerce numeric style fields to finite numbers.
293
264
  *
@@ -301,10 +272,13 @@ declare class Text$1 extends Reactivable {
301
272
  * numerics like `parseFloat`, matching the normalized path).
302
273
  */
303
274
  protected _normalizeComputedStyle(style: FullStyle): FullStyle;
275
+ protected _buildParagraph(p: NormalizedText['content'][number], pIndex: number): Paragraph;
276
+ protected _canReuseLayout(styleKey: string): boolean;
304
277
  protected _update(): this;
305
- createDom(): HTMLElement;
306
278
  measure(dom?: HTMLElement | undefined): MeasureResult;
307
279
  getGlyphBox(): BoundingBox;
280
+ protected _paragraphGlyphBox(paragraph: Paragraph): BoundingBox | undefined;
281
+ protected _computeGlyphBox(): BoundingBox;
308
282
  protected _updateInlineBox(): this;
309
283
  protected _updatePathBox(): this;
310
284
  protected _updateBoundingBox(): this;
@@ -319,15 +293,21 @@ declare class Text$1 extends Reactivable {
319
293
  declare class Paragraph {
320
294
  readonly style: NormalizedStyle;
321
295
  readonly index: number;
322
- readonly parent: Text$1;
296
+ readonly parent: Text;
323
297
  lineBox: BoundingBox;
324
298
  fragments: Fragment[];
325
299
  fill?: NormalizedFill;
326
300
  outline?: NormalizedOutline;
301
+ _layoutDirty: boolean;
302
+ _layoutTop: number;
303
+ _layoutHeight: number;
304
+ _layoutRight: number;
305
+ _glyphBox?: BoundingBox;
306
+ _layoutValid: boolean;
327
307
  computedStyle: FullStyle;
328
308
  computedFill: NormalizedFill | undefined;
329
309
  computedOutline: NormalizedOutline | undefined;
330
- constructor(style: NormalizedStyle, index: number, parent: Text$1);
310
+ constructor(style: NormalizedStyle, index: number, parent: Text);
331
311
  update(): this;
332
312
  }
333
313
 
@@ -335,10 +315,16 @@ interface DrawShapePathsOptions extends Partial<Path2DStyle> {
335
315
  clipRect?: BoundingBox;
336
316
  }
337
317
  declare class Canvas2DRenderer {
338
- text: Text$1;
318
+ text: Text;
339
319
  context: CanvasRenderingContext2D;
340
320
  pixelRatio: number;
341
- constructor(text: Text$1, context: CanvasRenderingContext2D);
321
+ region?: {
322
+ x: number;
323
+ y: number;
324
+ width: number;
325
+ height: number;
326
+ };
327
+ constructor(text: Text, context: CanvasRenderingContext2D);
342
328
  protected _setupView: () => void;
343
329
  protected _setupColors: () => void;
344
330
  setup: () => this;
@@ -359,5 +345,5 @@ declare class Canvas2DRenderer {
359
345
  drawCharacter: (character: Character, effect?: NormalizedEffect) => void;
360
346
  }
361
347
 
362
- export { Canvas2DRenderer as C, DomMeasurer as D, Fragment as F, Paragraph as P, Text$1 as T, Character as a, textDefaultStyle as t };
363
- export type { MeasureDomResult as M, Options as O, RenderOptions as R, DrawShapePathsOptions as b, MeasureResult as c, MeasuredCharacter as d, MeasuredCharacterRect as e, MeasuredFragment as f, MeasuredParagraph as g, MeasurerKind as h, Plugin as i, TextEvents as j, TextMeasurer as k };
348
+ export { Canvas2DRenderer as C, Fragment as F, Paragraph as P, Text as T, Character as a, textDefaultStyle as t };
349
+ export type { DrawShapePathsOptions as D, MeasureResult as M, Options as O, RenderOptions as R, MeasurerResult as b, Plugin as c, TextEvents as d, TextMeasurer as e };