modern-text 0.5.12 → 0.6.0

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.
package/dist/index.cjs CHANGED
@@ -456,6 +456,9 @@ function filterEmpty(val) {
456
456
  }
457
457
  return res;
458
458
  }
459
+ function needsFetch(source) {
460
+ return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("blob://");
461
+ }
459
462
 
460
463
  var __defProp$3 = Object.defineProperty;
461
464
  var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -526,9 +529,6 @@ function definePlugin(options) {
526
529
  }
527
530
 
528
531
  class Measurer {
529
- constructor(_text) {
530
- this._text = _text;
531
- }
532
532
  _styleToDomStyle(style) {
533
533
  const _style = { ...style };
534
534
  for (const key in style) {
@@ -560,8 +560,7 @@ class Measurer {
560
560
  * </ul>
561
561
  * </section>
562
562
  */
563
- createDom() {
564
- const { paragraphs, computedStyle } = this._text;
563
+ createDom(paragraphs, rootStyle) {
565
564
  const documentFragment = document.createDocumentFragment();
566
565
  const dom = document.createElement("section");
567
566
  Object.assign(dom.style, {
@@ -569,7 +568,7 @@ class Measurer {
569
568
  height: "max-content",
570
569
  whiteSpace: "pre-wrap",
571
570
  wordBreak: "break-all",
572
- ...this._styleToDomStyle(computedStyle),
571
+ ...this._styleToDomStyle(rootStyle),
573
572
  position: "fixed",
574
573
  visibility: "hidden"
575
574
  });
@@ -626,9 +625,11 @@ class Measurer {
626
625
  const range = document.createRange();
627
626
  range.selectNodeContents(text);
628
627
  const data = text.data ?? "";
628
+ let offset = 0;
629
629
  Array.from(data).forEach((char, index) => {
630
- const start = data.indexOf(char);
630
+ const start = offset += data.substring(offset).indexOf(char);
631
631
  const end = start + char.length;
632
+ offset += char.length;
632
633
  range.setStart(text, Math.max(start, 0));
633
634
  range.setEnd(text, end);
634
635
  const rects = range.getClientRects?.() ?? [range.getBoundingClientRect()];
@@ -662,8 +663,7 @@ class Measurer {
662
663
  characters
663
664
  };
664
665
  }
665
- measureDom(dom) {
666
- const { paragraphs } = this._text;
666
+ measureDom(paragraphs, dom) {
667
667
  const rect = dom.getBoundingClientRect();
668
668
  const measured = this._measureDom(dom);
669
669
  measured.paragraphs.forEach((p) => {
@@ -715,12 +715,12 @@ class Measurer {
715
715
  boundingBox: new modernPath2d.BoundingBox(0, 0, rect.width, rect.height)
716
716
  };
717
717
  }
718
- measure(dom) {
718
+ measure(paragraphs, rootStyle, dom) {
719
719
  let destory;
720
720
  if (!dom) {
721
- ({ dom, destory } = this.createDom());
721
+ ({ dom, destory } = this.createDom(paragraphs, rootStyle));
722
722
  }
723
- const result = this.measureDom(dom);
723
+ const result = this.measureDom(paragraphs, dom);
724
724
  destory?.();
725
725
  return result;
726
726
  }
@@ -823,33 +823,74 @@ class EventEmitter {
823
823
  function highlight() {
824
824
  const paths = [];
825
825
  const clipRects = [];
826
- const svgStringToSvgPaths = /* @__PURE__ */ new Map();
827
- async function getPaths(svg) {
828
- let result = svgStringToSvgPaths.get(svg);
829
- if (!result) {
830
- if (svg.startsWith("http")) {
831
- svg = await fetch(svg).then((rep) => rep.text());
826
+ const loaded = /* @__PURE__ */ new Map();
827
+ const parsed = /* @__PURE__ */ new Map();
828
+ async function loadSvg(svg) {
829
+ if (!loaded.has(svg)) {
830
+ loaded.set(svg, svg);
831
+ try {
832
+ loaded.set(svg, await fetch(svg).then((rep) => rep.text()));
833
+ } catch (err) {
834
+ console.warn(err);
835
+ loaded.delete(svg);
832
836
  }
833
- const dom = modernPath2d.parseSvgToDom(svg);
837
+ }
838
+ }
839
+ function getPaths(svg) {
840
+ let result = parsed.get(svg);
841
+ if (!result) {
842
+ const dom = modernPath2d.parseSvgToDom(
843
+ needsFetch(svg) ? loaded.get(svg) ?? svg : svg
844
+ );
834
845
  const paths2 = modernPath2d.parseSvg(dom);
835
846
  result = { dom, paths: paths2 };
836
- svgStringToSvgPaths.set(svg, result);
847
+ parsed.set(svg, result);
837
848
  }
838
849
  return result;
839
850
  }
840
851
  return definePlugin({
841
852
  name: "highlight",
842
853
  paths,
843
- update: async (text) => {
854
+ load: async (text) => {
855
+ const promises = [];
856
+ text.forEachCharacter((character) => {
857
+ const { computedStyle: style } = character;
858
+ const { highlightImage, highlightReferImage } = style;
859
+ if (needsFetch(highlightImage)) {
860
+ promises.push(loadSvg(highlightImage));
861
+ }
862
+ if (needsFetch(highlightReferImage)) {
863
+ promises.push(loadSvg(highlightReferImage));
864
+ }
865
+ });
866
+ await Promise.all(promises);
867
+ },
868
+ update: (text) => {
844
869
  clipRects.length = 0;
845
870
  paths.length = 0;
846
871
  let groups = [];
847
872
  let group;
848
873
  let prevStyle;
849
874
  text.forEachCharacter((character) => {
850
- const { isVertical, computedStyle: style, inlineBox } = character;
851
- if (!isNone(style.highlightImage) && character.glyphBox) {
852
- if ((!prevStyle || isEqualValue(prevStyle.highlightImage, style.highlightImage) && isEqualValue(prevStyle.highlightColormap, style.highlightColormap) && isEqualValue(prevStyle.highlightLine, style.highlightLine) && isEqualValue(prevStyle.highlightSize, style.highlightSize) && isEqualValue(prevStyle.highlightThickness, style.highlightThickness)) && group?.length && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top) && group[0].fontSize === style.fontSize) {
875
+ const {
876
+ computedStyle: style
877
+ } = character;
878
+ const {
879
+ highlightImage
880
+ } = style;
881
+ if (!isNone(highlightImage)) {
882
+ const {
883
+ inlineBox,
884
+ isVertical
885
+ } = character;
886
+ const {
887
+ fontSize,
888
+ highlightColormap,
889
+ highlightLine,
890
+ highlightSize,
891
+ highlightThickness
892
+ } = style;
893
+ if ((!prevStyle || isEqualValue(prevStyle.highlightImage, highlightImage) && isEqualValue(prevStyle.highlightColormap, highlightColormap) && isEqualValue(prevStyle.highlightLine, highlightLine) && isEqualValue(prevStyle.highlightSize, highlightSize) && isEqualValue(prevStyle.highlightThickness, highlightThickness)) && group?.length && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top) && group[0].fontSize === fontSize) {
853
894
  group.push(character);
854
895
  } else {
855
896
  group = [];
@@ -868,8 +909,12 @@ function highlight() {
868
909
  for (let i = 0; i < groups.length; i++) {
869
910
  const characters = groups[i];
870
911
  const char = characters[0];
871
- const groupBox = modernPath2d.BoundingBox.from(...characters.map((c) => c.glyphBox));
872
- const { computedStyle: style } = char;
912
+ const groupBox = modernPath2d.BoundingBox.from(
913
+ ...characters.filter((c) => c.glyphBox).map((c) => c.glyphBox)
914
+ );
915
+ const {
916
+ computedStyle: style
917
+ } = char;
873
918
  const {
874
919
  fontSize,
875
920
  writingMode,
@@ -883,7 +928,7 @@ function highlight() {
883
928
  const isVertical = writingMode.includes("vertical");
884
929
  const thickness = parseValueNumber(highlightThickness, { fontSize, total: groupBox.width }) / groupBox.width;
885
930
  const colormap = parseColormap(highlightColormap);
886
- const { paths: svgPaths, dom: svgDom } = await getPaths(highlightImage);
931
+ const { paths: svgPaths, dom: svgDom } = getPaths(highlightImage);
887
932
  const aBox = modernPath2d.getPathsBoundingBox(svgPaths, true);
888
933
  const styleScale = fontSize / aBox.width * 2;
889
934
  const cBox = new modernPath2d.BoundingBox().copy(groupBox);
@@ -899,7 +944,7 @@ function highlight() {
899
944
  cBox.width = userWidth;
900
945
  }
901
946
  if (!isNone(highlightReferImage) && isNone(highlightLine)) {
902
- const bBox = modernPath2d.getPathsBoundingBox((await getPaths(highlightReferImage)).paths, true);
947
+ const bBox = modernPath2d.getPathsBoundingBox(getPaths(highlightReferImage).paths, true);
903
948
  aBox.copy(bBox);
904
949
  } else {
905
950
  let line;
@@ -1050,8 +1095,16 @@ function listStyle() {
1050
1095
  const { paragraphs, isVertical, fontSize } = text;
1051
1096
  const padding = fontSize * 0.45;
1052
1097
  paragraphs.forEach((paragraph) => {
1053
- const { computedStyle: style } = paragraph;
1054
- const { listStyleImage, listStyleColormap, listStyleSize, listStyleType, color } = style;
1098
+ const {
1099
+ computedStyle: style
1100
+ } = paragraph;
1101
+ const {
1102
+ color,
1103
+ listStyleImage,
1104
+ listStyleColormap,
1105
+ listStyleSize,
1106
+ listStyleType
1107
+ } = style;
1055
1108
  const colormap = parseColormap(listStyleColormap);
1056
1109
  let size = listStyleSize;
1057
1110
  let image;
@@ -1080,18 +1133,10 @@ function listStyle() {
1080
1133
  const m = new modernPath2d.Matrix3();
1081
1134
  if (isVertical) {
1082
1135
  const reScale = fontSize / imageBox.height * scale;
1083
- m.translate(-imageBox.left, -imageBox.top);
1084
- m.rotate(Math.PI / 2);
1085
- m.scale(reScale, reScale);
1086
- m.translate(fontSize / 2 - imageBox.height * reScale / 2, 0);
1087
- m.translate(box.left + (box.width - fontSize) / 2, fBox.top - padding);
1136
+ m.translate(-imageBox.left, -imageBox.top).rotate(Math.PI / 2).scale(reScale, reScale).translate(fontSize / 2 - imageBox.height * reScale / 2, 0).translate(box.left + (box.width - fontSize) / 2, fBox.top - padding);
1088
1137
  } else {
1089
1138
  const reScale = fontSize / imageBox.height * scale;
1090
- m.translate(-imageBox.left, -imageBox.top);
1091
- m.translate(-imageBox.width, 0);
1092
- m.scale(reScale, reScale);
1093
- m.translate(0, fontSize / 2 - imageBox.height * reScale / 2);
1094
- m.translate(fBox.left - padding, box.top + (box.height - fontSize) / 2);
1139
+ m.translate(-imageBox.left, -imageBox.top).translate(-imageBox.width, 0).scale(reScale, reScale).translate(0, fontSize / 2 - imageBox.height * reScale / 2).translate(fBox.left - padding, box.top + (box.height - fontSize) / 2);
1095
1140
  }
1096
1141
  paths.push(...imagePaths.map((p) => {
1097
1142
  const path = p.clone();
@@ -1227,11 +1272,24 @@ function textDecoration() {
1227
1272
  let group;
1228
1273
  let prevStyle;
1229
1274
  text.forEachCharacter((character) => {
1230
- const { computedStyle: style, isVertical, inlineBox, underlinePosition, underlineThickness, strikeoutPosition, strikeoutSize } = character;
1231
- if (!isNone(style.textDecoration)) {
1275
+ const {
1276
+ computedStyle: style,
1277
+ isVertical,
1278
+ inlineBox,
1279
+ underlinePosition,
1280
+ underlineThickness,
1281
+ strikeoutPosition,
1282
+ strikeoutSize
1283
+ } = character;
1284
+ const {
1285
+ color,
1286
+ textDecoration: textDecoration2,
1287
+ writingMode
1288
+ } = style;
1289
+ if (!isNone(textDecoration2)) {
1232
1290
  let flag = false;
1233
- if (prevStyle?.textDecoration === style.textDecoration && prevStyle?.writingMode === style.writingMode && prevStyle?.color === style.color && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top)) {
1234
- switch (style.textDecoration) {
1291
+ if (prevStyle?.textDecoration === textDecoration2 && prevStyle?.writingMode === writingMode && prevStyle?.color === color && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top)) {
1292
+ switch (textDecoration2) {
1235
1293
  case "underline":
1236
1294
  if (group[0].underlinePosition === underlinePosition && group[0].underlineThickness === underlineThickness) {
1237
1295
  flag = true;
@@ -1257,8 +1315,18 @@ function textDecoration() {
1257
1315
  }
1258
1316
  });
1259
1317
  groups.forEach((group2) => {
1260
- const { computedStyle: style, isVertical, underlinePosition, underlineThickness, strikeoutPosition, strikeoutSize } = group2[0];
1261
- const { textDecoration: textDecoration2, color } = style;
1318
+ const {
1319
+ computedStyle: style,
1320
+ isVertical,
1321
+ underlinePosition,
1322
+ underlineThickness,
1323
+ strikeoutPosition,
1324
+ strikeoutSize
1325
+ } = group2[0];
1326
+ const {
1327
+ color,
1328
+ textDecoration: textDecoration2
1329
+ } = style;
1262
1330
  const { left, top, width, height } = modernPath2d.BoundingBox.from(...group2.map((c) => c.inlineBox));
1263
1331
  let position = isVertical ? left + width : top;
1264
1332
  const direction = isVertical ? -1 : 1;
@@ -1401,7 +1469,7 @@ class Text extends EventEmitter {
1401
1469
  __publicField(this, "glyphBox", new modernPath2d.BoundingBox());
1402
1470
  __publicField(this, "pathBox", new modernPath2d.BoundingBox());
1403
1471
  __publicField(this, "boundingBox", new modernPath2d.BoundingBox());
1404
- __publicField(this, "measurer", new Measurer(this));
1472
+ __publicField(this, "measurer", new Measurer());
1405
1473
  __publicField(this, "plugins", /* @__PURE__ */ new Map());
1406
1474
  __publicField(this, "fonts");
1407
1475
  this.debug = options.debug ?? false;
@@ -1435,6 +1503,9 @@ class Text extends EventEmitter {
1435
1503
  });
1436
1504
  return this;
1437
1505
  }
1506
+ async load() {
1507
+ await Promise.all(Array.from(this.plugins.values()).map((p) => p.load?.(this)));
1508
+ }
1438
1509
  updateParagraphs() {
1439
1510
  this.computedStyle = { ...defaultTextStyles, ...this.style };
1440
1511
  let { content, computedStyle: style } = this;
@@ -1486,7 +1557,7 @@ class Text extends EventEmitter {
1486
1557
  this.paragraphs = paragraphs;
1487
1558
  return this;
1488
1559
  }
1489
- async measure(dom = this.measureDom) {
1560
+ measure(dom = this.measureDom) {
1490
1561
  const old = {
1491
1562
  paragraphs: this.paragraphs,
1492
1563
  lineBox: this.lineBox,
@@ -1496,18 +1567,16 @@ class Text extends EventEmitter {
1496
1567
  boundingBox: this.boundingBox
1497
1568
  };
1498
1569
  this.updateParagraphs();
1499
- const result = this.measurer.measure(dom);
1570
+ const result = this.measurer.measure(this.paragraphs, this.computedStyle, dom);
1500
1571
  this.paragraphs = result.paragraphs;
1501
1572
  this.lineBox = result.boundingBox;
1502
1573
  this.characters.forEach((c) => {
1503
1574
  c.update(this.fonts);
1504
1575
  });
1505
1576
  this.rawGlyphBox = this.getGlyphBox();
1506
- const plugins = Array.from(this.plugins.values());
1507
- plugins.sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0));
1508
- for (let i = 0; i < plugins.length; i++) {
1509
- await plugins[i].update?.(this);
1510
- }
1577
+ Array.from(this.plugins.values()).sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
1578
+ plugin.update?.(this);
1579
+ });
1511
1580
  this.glyphBox = this.getGlyphBox();
1512
1581
  this.updatePathBox().updateBoundingBox();
1513
1582
  for (const key in old) {
@@ -1563,28 +1632,26 @@ class Text extends EventEmitter {
1563
1632
  this.needsUpdate = true;
1564
1633
  return this;
1565
1634
  }
1566
- async update() {
1567
- const result = await this.measure();
1635
+ update() {
1636
+ const result = this.measure();
1568
1637
  for (const key in result) {
1569
1638
  this[key] = result[key];
1570
1639
  }
1571
1640
  this.emit("update", { text: this });
1641
+ return this;
1572
1642
  }
1573
- async render(options) {
1643
+ render(options) {
1574
1644
  const { view, pixelRatio = 2 } = options;
1575
1645
  const ctx = view.getContext("2d");
1576
1646
  if (!ctx) {
1577
1647
  return;
1578
1648
  }
1579
1649
  if (this.needsUpdate) {
1580
- await this.update();
1650
+ this.update();
1581
1651
  }
1582
1652
  setupView(ctx, pixelRatio, this.boundingBox);
1583
1653
  uploadColors(ctx, this);
1584
- const plugins = Array.from(this.plugins.values());
1585
- plugins.sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0));
1586
- for (let i = 0; i < plugins.length; i++) {
1587
- const plugin = plugins[i];
1654
+ Array.from(this.plugins.values()).sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0)).forEach((plugin) => {
1588
1655
  if (plugin.render) {
1589
1656
  plugin.render?.(ctx, this);
1590
1657
  } else if (plugin.paths) {
@@ -1597,17 +1664,21 @@ class Text extends EventEmitter {
1597
1664
  });
1598
1665
  });
1599
1666
  }
1600
- }
1667
+ });
1601
1668
  this.emit("render", { text: this, view, pixelRatio });
1602
1669
  }
1603
1670
  }
1604
1671
 
1605
- function measureText(options) {
1606
- return new Text(options).measure();
1672
+ async function measureText(options) {
1673
+ const text = new Text(options);
1674
+ await text.load();
1675
+ return text.measure();
1607
1676
  }
1608
1677
 
1609
- function renderText(options) {
1610
- return new Text(options).render(options);
1678
+ async function renderText(options) {
1679
+ const text = new Text(options);
1680
+ await text.load();
1681
+ text.render(options);
1611
1682
  }
1612
1683
 
1613
1684
  exports.Character = Character;
@@ -1627,6 +1698,7 @@ exports.isEqualValue = isEqualValue;
1627
1698
  exports.isNone = isNone;
1628
1699
  exports.listStyle = listStyle;
1629
1700
  exports.measureText = measureText;
1701
+ exports.needsFetch = needsFetch;
1630
1702
  exports.parseColor = parseColor;
1631
1703
  exports.parseColormap = parseColormap;
1632
1704
  exports.parseValueNumber = parseValueNumber;
package/dist/index.d.cts CHANGED
@@ -121,8 +121,6 @@ interface MeasureDomResult {
121
121
  boundingBox: BoundingBox;
122
122
  }
123
123
  declare class Measurer {
124
- protected _text: Text;
125
- constructor(_text: Text);
126
124
  protected _styleToDomStyle(style: Partial<TextStyle>): Record<string, any>;
127
125
  /**
128
126
  * <section style="...">
@@ -134,7 +132,7 @@ declare class Measurer {
134
132
  * </ul>
135
133
  * </section>
136
134
  */
137
- createDom(): {
135
+ createDom(paragraphs: Paragraph[], rootStyle: TextStyle): {
138
136
  dom: HTMLElement;
139
137
  destory: () => void;
140
138
  };
@@ -143,8 +141,8 @@ declare class Measurer {
143
141
  fragments: MeasuredFragment[];
144
142
  characters: MeasuredCharacter[];
145
143
  };
146
- measureDom(dom: HTMLElement): MeasureDomResult;
147
- measure(dom?: HTMLElement): MeasureDomResult;
144
+ measureDom(paragraphs: Paragraph[], dom: HTMLElement): MeasureDomResult;
145
+ measure(paragraphs: Paragraph[], rootStyle: TextStyle, dom?: HTMLElement): MeasureDomResult;
148
146
  }
149
147
 
150
148
  interface TextRenderOptions {
@@ -201,14 +199,15 @@ declare class Text extends EventEmitter<TextEventMap> {
201
199
  fragmentIndex: number;
202
200
  characterIndex: number;
203
201
  }) => void): this;
202
+ load(): Promise<void>;
204
203
  updateParagraphs(): this;
205
- measure(dom?: HTMLElement | undefined): Promise<MeasureResult>;
204
+ measure(dom?: HTMLElement | undefined): MeasureResult;
206
205
  getGlyphBox(): BoundingBox;
207
206
  updatePathBox(): this;
208
207
  updateBoundingBox(): this;
209
208
  requestUpdate(): this;
210
- update(): Promise<void>;
211
- render(options: TextRenderOptions): Promise<void>;
209
+ update(): this;
210
+ render(options: TextRenderOptions): void;
212
211
  }
213
212
 
214
213
  type Sizeable = `${number}%` | `${number}rem` | number;
@@ -286,15 +285,15 @@ interface ParagraphContent extends Partial<TextStyle> {
286
285
  fragments: FragmentContent[];
287
286
  }
288
287
  type TextContent = string | FragmentContent | ParagraphContent | (string | FragmentContent | ParagraphContent | (string | FragmentContent)[])[];
289
- type PromiseLike<T> = T | Promise<T>;
290
288
  interface TextPlugin {
291
289
  name: string;
292
290
  paths?: Path2D[];
293
291
  getBoundingBox?: (text: Text) => BoundingBox | undefined;
292
+ update?: (text: Text) => void;
294
293
  updateOrder?: number;
295
- update?: (text: Text) => PromiseLike<void>;
294
+ render?: (ctx: CanvasRenderingContext2D, text: Text) => void;
296
295
  renderOrder?: number;
297
- render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
296
+ load?: (text: Text) => Promise<void>;
298
297
  }
299
298
  interface TextOptions {
300
299
  debug?: boolean;
@@ -334,6 +333,8 @@ declare function definePlugin(options: TextPlugin): TextPlugin;
334
333
 
335
334
  declare function measureText(options: TextOptions): Promise<MeasureResult>;
336
335
 
336
+ declare function renderText(options: TextOptions & TextRenderOptions): Promise<void>;
337
+
337
338
  declare function highlight(): TextPlugin;
338
339
 
339
340
  declare function listStyle(): TextPlugin;
@@ -343,8 +344,6 @@ declare function getTransform2D(text: Text, style: Partial<TextStyle>): Matrix3;
343
344
 
344
345
  declare function textDecoration(): TextPlugin;
345
346
 
346
- declare function renderText(options: TextOptions & TextRenderOptions): Promise<void>;
347
-
348
347
  interface ValueContext {
349
348
  total: number;
350
349
  fontSize: number;
@@ -356,5 +355,6 @@ declare function isEqualObject(obj1: Record<string, any>, obj2: Record<string, a
356
355
  declare function isEqualValue(val1: any, val2: any): boolean;
357
356
  declare function hexToRgb(hex: string): string | null;
358
357
  declare function filterEmpty(val: Record<string, any> | undefined): Record<string, any> | undefined;
358
+ declare function needsFetch(source: string): boolean;
359
359
 
360
- export { Character, type DrawShapePathsOptions, type FontKerning, type FontStyle, type FontWeight, Fragment, type FragmentContent, type HighlightColormap, type HighlightImage, type HighlightLine, type HighlightReferImage, type HighlightSize, type HighlightThickness, type LinearGradient, type ListStyleColormap, type ListStyleImage, type ListStylePosition, type ListStyleSize, type ListStyleType, type MeasureDomResult, type MeasureResult, type MeasuredCharacter, type MeasuredFragment, type MeasuredParagraph, Measurer, Paragraph, type ParagraphContent, type Sizeable, Text, type TextAlign, type TextContent, type TextDecorationLine, type TextDrawStyle, type TextEventMap, type TextInlineStyle, type TextLineStyle, type TextOptions, type TextOrientation, type TextPlugin, type TextRenderOptions, type TextStyle, type TextTransform, type TextWrap, type VerticalAlign, type WritingMode, defaultTextStyles, definePlugin, drawPath, filterEmpty, getTransform2D, hexToRgb, highlight, isEqualObject, isEqualValue, isNone, listStyle, measureText, parseColor, parseColormap, parseValueNumber, render, renderText, setupView, textDecoration, uploadColor, uploadColors };
360
+ export { Character, type DrawShapePathsOptions, type FontKerning, type FontStyle, type FontWeight, Fragment, type FragmentContent, type HighlightColormap, type HighlightImage, type HighlightLine, type HighlightReferImage, type HighlightSize, type HighlightThickness, type LinearGradient, type ListStyleColormap, type ListStyleImage, type ListStylePosition, type ListStyleSize, type ListStyleType, type MeasureDomResult, type MeasureResult, type MeasuredCharacter, type MeasuredFragment, type MeasuredParagraph, Measurer, Paragraph, type ParagraphContent, type Sizeable, Text, type TextAlign, type TextContent, type TextDecorationLine, type TextDrawStyle, type TextEventMap, type TextInlineStyle, type TextLineStyle, type TextOptions, type TextOrientation, type TextPlugin, type TextRenderOptions, type TextStyle, type TextTransform, type TextWrap, type VerticalAlign, type WritingMode, defaultTextStyles, definePlugin, drawPath, filterEmpty, getTransform2D, hexToRgb, highlight, isEqualObject, isEqualValue, isNone, listStyle, measureText, needsFetch, parseColor, parseColormap, parseValueNumber, render, renderText, setupView, textDecoration, uploadColor, uploadColors };
package/dist/index.d.mts CHANGED
@@ -121,8 +121,6 @@ interface MeasureDomResult {
121
121
  boundingBox: BoundingBox;
122
122
  }
123
123
  declare class Measurer {
124
- protected _text: Text;
125
- constructor(_text: Text);
126
124
  protected _styleToDomStyle(style: Partial<TextStyle>): Record<string, any>;
127
125
  /**
128
126
  * <section style="...">
@@ -134,7 +132,7 @@ declare class Measurer {
134
132
  * </ul>
135
133
  * </section>
136
134
  */
137
- createDom(): {
135
+ createDom(paragraphs: Paragraph[], rootStyle: TextStyle): {
138
136
  dom: HTMLElement;
139
137
  destory: () => void;
140
138
  };
@@ -143,8 +141,8 @@ declare class Measurer {
143
141
  fragments: MeasuredFragment[];
144
142
  characters: MeasuredCharacter[];
145
143
  };
146
- measureDom(dom: HTMLElement): MeasureDomResult;
147
- measure(dom?: HTMLElement): MeasureDomResult;
144
+ measureDom(paragraphs: Paragraph[], dom: HTMLElement): MeasureDomResult;
145
+ measure(paragraphs: Paragraph[], rootStyle: TextStyle, dom?: HTMLElement): MeasureDomResult;
148
146
  }
149
147
 
150
148
  interface TextRenderOptions {
@@ -201,14 +199,15 @@ declare class Text extends EventEmitter<TextEventMap> {
201
199
  fragmentIndex: number;
202
200
  characterIndex: number;
203
201
  }) => void): this;
202
+ load(): Promise<void>;
204
203
  updateParagraphs(): this;
205
- measure(dom?: HTMLElement | undefined): Promise<MeasureResult>;
204
+ measure(dom?: HTMLElement | undefined): MeasureResult;
206
205
  getGlyphBox(): BoundingBox;
207
206
  updatePathBox(): this;
208
207
  updateBoundingBox(): this;
209
208
  requestUpdate(): this;
210
- update(): Promise<void>;
211
- render(options: TextRenderOptions): Promise<void>;
209
+ update(): this;
210
+ render(options: TextRenderOptions): void;
212
211
  }
213
212
 
214
213
  type Sizeable = `${number}%` | `${number}rem` | number;
@@ -286,15 +285,15 @@ interface ParagraphContent extends Partial<TextStyle> {
286
285
  fragments: FragmentContent[];
287
286
  }
288
287
  type TextContent = string | FragmentContent | ParagraphContent | (string | FragmentContent | ParagraphContent | (string | FragmentContent)[])[];
289
- type PromiseLike<T> = T | Promise<T>;
290
288
  interface TextPlugin {
291
289
  name: string;
292
290
  paths?: Path2D[];
293
291
  getBoundingBox?: (text: Text) => BoundingBox | undefined;
292
+ update?: (text: Text) => void;
294
293
  updateOrder?: number;
295
- update?: (text: Text) => PromiseLike<void>;
294
+ render?: (ctx: CanvasRenderingContext2D, text: Text) => void;
296
295
  renderOrder?: number;
297
- render?: (ctx: CanvasRenderingContext2D, text: Text) => PromiseLike<void>;
296
+ load?: (text: Text) => Promise<void>;
298
297
  }
299
298
  interface TextOptions {
300
299
  debug?: boolean;
@@ -334,6 +333,8 @@ declare function definePlugin(options: TextPlugin): TextPlugin;
334
333
 
335
334
  declare function measureText(options: TextOptions): Promise<MeasureResult>;
336
335
 
336
+ declare function renderText(options: TextOptions & TextRenderOptions): Promise<void>;
337
+
337
338
  declare function highlight(): TextPlugin;
338
339
 
339
340
  declare function listStyle(): TextPlugin;
@@ -343,8 +344,6 @@ declare function getTransform2D(text: Text, style: Partial<TextStyle>): Matrix3;
343
344
 
344
345
  declare function textDecoration(): TextPlugin;
345
346
 
346
- declare function renderText(options: TextOptions & TextRenderOptions): Promise<void>;
347
-
348
347
  interface ValueContext {
349
348
  total: number;
350
349
  fontSize: number;
@@ -356,5 +355,6 @@ declare function isEqualObject(obj1: Record<string, any>, obj2: Record<string, a
356
355
  declare function isEqualValue(val1: any, val2: any): boolean;
357
356
  declare function hexToRgb(hex: string): string | null;
358
357
  declare function filterEmpty(val: Record<string, any> | undefined): Record<string, any> | undefined;
358
+ declare function needsFetch(source: string): boolean;
359
359
 
360
- export { Character, type DrawShapePathsOptions, type FontKerning, type FontStyle, type FontWeight, Fragment, type FragmentContent, type HighlightColormap, type HighlightImage, type HighlightLine, type HighlightReferImage, type HighlightSize, type HighlightThickness, type LinearGradient, type ListStyleColormap, type ListStyleImage, type ListStylePosition, type ListStyleSize, type ListStyleType, type MeasureDomResult, type MeasureResult, type MeasuredCharacter, type MeasuredFragment, type MeasuredParagraph, Measurer, Paragraph, type ParagraphContent, type Sizeable, Text, type TextAlign, type TextContent, type TextDecorationLine, type TextDrawStyle, type TextEventMap, type TextInlineStyle, type TextLineStyle, type TextOptions, type TextOrientation, type TextPlugin, type TextRenderOptions, type TextStyle, type TextTransform, type TextWrap, type VerticalAlign, type WritingMode, defaultTextStyles, definePlugin, drawPath, filterEmpty, getTransform2D, hexToRgb, highlight, isEqualObject, isEqualValue, isNone, listStyle, measureText, parseColor, parseColormap, parseValueNumber, render, renderText, setupView, textDecoration, uploadColor, uploadColors };
360
+ export { Character, type DrawShapePathsOptions, type FontKerning, type FontStyle, type FontWeight, Fragment, type FragmentContent, type HighlightColormap, type HighlightImage, type HighlightLine, type HighlightReferImage, type HighlightSize, type HighlightThickness, type LinearGradient, type ListStyleColormap, type ListStyleImage, type ListStylePosition, type ListStyleSize, type ListStyleType, type MeasureDomResult, type MeasureResult, type MeasuredCharacter, type MeasuredFragment, type MeasuredParagraph, Measurer, Paragraph, type ParagraphContent, type Sizeable, Text, type TextAlign, type TextContent, type TextDecorationLine, type TextDrawStyle, type TextEventMap, type TextInlineStyle, type TextLineStyle, type TextOptions, type TextOrientation, type TextPlugin, type TextRenderOptions, type TextStyle, type TextTransform, type TextWrap, type VerticalAlign, type WritingMode, defaultTextStyles, definePlugin, drawPath, filterEmpty, getTransform2D, hexToRgb, highlight, isEqualObject, isEqualValue, isNone, listStyle, measureText, needsFetch, parseColor, parseColormap, parseValueNumber, render, renderText, setupView, textDecoration, uploadColor, uploadColors };