modern-text 0.5.13 → 0.6.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.
- package/dist/index.cjs +198 -104
- package/dist/index.d.cts +29 -22
- package/dist/index.d.mts +29 -22
- package/dist/index.d.ts +29 -22
- package/dist/index.js +4 -4
- package/dist/index.mjs +198 -105
- package/package.json +1 -1
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
|
-
|
|
564
|
-
const { paragraphs, computedStyle } = this._text;
|
|
563
|
+
createParagraphDom(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(
|
|
571
|
+
...this._styleToDomStyle(rootStyle),
|
|
573
572
|
position: "fixed",
|
|
574
573
|
visibility: "hidden"
|
|
575
574
|
});
|
|
@@ -598,12 +597,41 @@ class Measurer {
|
|
|
598
597
|
destory: () => dom.parentNode?.removeChild(dom)
|
|
599
598
|
};
|
|
600
599
|
}
|
|
601
|
-
|
|
600
|
+
measureDomText(text) {
|
|
601
|
+
const range = document.createRange();
|
|
602
|
+
range.selectNodeContents(text);
|
|
603
|
+
const data = text.data ?? "";
|
|
604
|
+
let offset = 0;
|
|
605
|
+
return Array.from(data).map((char) => {
|
|
606
|
+
const start = offset += data.substring(offset).indexOf(char);
|
|
607
|
+
const end = start + char.length;
|
|
608
|
+
offset += char.length;
|
|
609
|
+
range.setStart(text, Math.max(start, 0));
|
|
610
|
+
range.setEnd(text, end);
|
|
611
|
+
const rects = range.getClientRects?.() ?? [range.getBoundingClientRect()];
|
|
612
|
+
let rect = rects[rects.length - 1];
|
|
613
|
+
if (rects.length > 1 && rect.width < 2) {
|
|
614
|
+
rect = rects[rects.length - 2];
|
|
615
|
+
}
|
|
616
|
+
const content = range.toString();
|
|
617
|
+
if (content !== "" && rect && rect.width + rect.height !== 0) {
|
|
618
|
+
return {
|
|
619
|
+
content,
|
|
620
|
+
top: rect.top,
|
|
621
|
+
left: rect.left,
|
|
622
|
+
height: rect.height,
|
|
623
|
+
width: rect.width
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
return void 0;
|
|
627
|
+
}).filter(Boolean);
|
|
628
|
+
}
|
|
629
|
+
measureDom(dom) {
|
|
602
630
|
const paragraphs = [];
|
|
603
631
|
const fragments = [];
|
|
604
632
|
const characters = [];
|
|
605
|
-
dom.querySelectorAll("li").forEach((
|
|
606
|
-
const pBox =
|
|
633
|
+
dom.querySelectorAll("li").forEach((pDom, paragraphIndex) => {
|
|
634
|
+
const pBox = pDom.getBoundingClientRect();
|
|
607
635
|
paragraphs.push({
|
|
608
636
|
paragraphIndex,
|
|
609
637
|
left: pBox.left,
|
|
@@ -611,8 +639,8 @@ class Measurer {
|
|
|
611
639
|
width: pBox.width,
|
|
612
640
|
height: pBox.height
|
|
613
641
|
});
|
|
614
|
-
|
|
615
|
-
const fBox =
|
|
642
|
+
pDom.querySelectorAll(":scope > *").forEach((fDom, fragmentIndex) => {
|
|
643
|
+
const fBox = fDom.getBoundingClientRect();
|
|
616
644
|
fragments.push({
|
|
617
645
|
paragraphIndex,
|
|
618
646
|
fragmentIndex,
|
|
@@ -621,37 +649,32 @@ class Measurer {
|
|
|
621
649
|
width: fBox.width,
|
|
622
650
|
height: fBox.height
|
|
623
651
|
});
|
|
624
|
-
|
|
625
|
-
if (
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
left: rect.left,
|
|
651
|
-
height: rect.height,
|
|
652
|
-
width: rect.width,
|
|
653
|
-
textWidth: -1,
|
|
654
|
-
textHeight: -1
|
|
652
|
+
let characterIndex = 0;
|
|
653
|
+
if (fDom.firstChild instanceof window.Text) {
|
|
654
|
+
this.measureDomText(fDom.firstChild).forEach((char) => {
|
|
655
|
+
characters.push({
|
|
656
|
+
...char,
|
|
657
|
+
newParagraphIndex: -1,
|
|
658
|
+
paragraphIndex,
|
|
659
|
+
fragmentIndex,
|
|
660
|
+
characterIndex: characterIndex++,
|
|
661
|
+
textWidth: -1,
|
|
662
|
+
textHeight: -1
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
} else {
|
|
666
|
+
fDom.querySelectorAll(":scope > *").forEach((cDom) => {
|
|
667
|
+
if (cDom.firstChild instanceof window.Text) {
|
|
668
|
+
this.measureDomText(cDom.firstChild).forEach((char) => {
|
|
669
|
+
characters.push({
|
|
670
|
+
...char,
|
|
671
|
+
newParagraphIndex: -1,
|
|
672
|
+
paragraphIndex,
|
|
673
|
+
fragmentIndex,
|
|
674
|
+
characterIndex: characterIndex++,
|
|
675
|
+
textWidth: -1,
|
|
676
|
+
textHeight: -1
|
|
677
|
+
});
|
|
655
678
|
});
|
|
656
679
|
}
|
|
657
680
|
});
|
|
@@ -664,10 +687,9 @@ class Measurer {
|
|
|
664
687
|
characters
|
|
665
688
|
};
|
|
666
689
|
}
|
|
667
|
-
|
|
668
|
-
const { paragraphs } = this._text;
|
|
690
|
+
measureParagraphDom(paragraphs, dom) {
|
|
669
691
|
const rect = dom.getBoundingClientRect();
|
|
670
|
-
const measured = this.
|
|
692
|
+
const measured = this.measureDom(dom);
|
|
671
693
|
measured.paragraphs.forEach((p) => {
|
|
672
694
|
const _p = paragraphs[p.paragraphIndex];
|
|
673
695
|
_p.lineBox.left = p.left - rect.left;
|
|
@@ -717,12 +739,12 @@ class Measurer {
|
|
|
717
739
|
boundingBox: new modernPath2d.BoundingBox(0, 0, rect.width, rect.height)
|
|
718
740
|
};
|
|
719
741
|
}
|
|
720
|
-
measure(dom) {
|
|
742
|
+
measure(paragraphs, rootStyle, dom) {
|
|
721
743
|
let destory;
|
|
722
744
|
if (!dom) {
|
|
723
|
-
({ dom, destory } = this.
|
|
745
|
+
({ dom, destory } = this.createParagraphDom(paragraphs, rootStyle));
|
|
724
746
|
}
|
|
725
|
-
const result = this.
|
|
747
|
+
const result = this.measureParagraphDom(paragraphs, dom);
|
|
726
748
|
destory?.();
|
|
727
749
|
return result;
|
|
728
750
|
}
|
|
@@ -825,33 +847,74 @@ class EventEmitter {
|
|
|
825
847
|
function highlight() {
|
|
826
848
|
const paths = [];
|
|
827
849
|
const clipRects = [];
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if (!
|
|
832
|
-
|
|
833
|
-
|
|
850
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
851
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
852
|
+
async function loadSvg(svg) {
|
|
853
|
+
if (!loaded.has(svg)) {
|
|
854
|
+
loaded.set(svg, svg);
|
|
855
|
+
try {
|
|
856
|
+
loaded.set(svg, await fetch(svg).then((rep) => rep.text()));
|
|
857
|
+
} catch (err) {
|
|
858
|
+
console.warn(err);
|
|
859
|
+
loaded.delete(svg);
|
|
834
860
|
}
|
|
835
|
-
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
function getPaths(svg) {
|
|
864
|
+
let result = parsed.get(svg);
|
|
865
|
+
if (!result) {
|
|
866
|
+
const dom = modernPath2d.parseSvgToDom(
|
|
867
|
+
needsFetch(svg) ? loaded.get(svg) ?? svg : svg
|
|
868
|
+
);
|
|
836
869
|
const paths2 = modernPath2d.parseSvg(dom);
|
|
837
870
|
result = { dom, paths: paths2 };
|
|
838
|
-
|
|
871
|
+
parsed.set(svg, result);
|
|
839
872
|
}
|
|
840
873
|
return result;
|
|
841
874
|
}
|
|
842
875
|
return definePlugin({
|
|
843
876
|
name: "highlight",
|
|
844
877
|
paths,
|
|
845
|
-
|
|
878
|
+
load: async (text) => {
|
|
879
|
+
const promises = [];
|
|
880
|
+
text.forEachCharacter((character) => {
|
|
881
|
+
const { computedStyle: style } = character;
|
|
882
|
+
const { highlightImage, highlightReferImage } = style;
|
|
883
|
+
if (needsFetch(highlightImage)) {
|
|
884
|
+
promises.push(loadSvg(highlightImage));
|
|
885
|
+
}
|
|
886
|
+
if (needsFetch(highlightReferImage)) {
|
|
887
|
+
promises.push(loadSvg(highlightReferImage));
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
await Promise.all(promises);
|
|
891
|
+
},
|
|
892
|
+
update: (text) => {
|
|
846
893
|
clipRects.length = 0;
|
|
847
894
|
paths.length = 0;
|
|
848
895
|
let groups = [];
|
|
849
896
|
let group;
|
|
850
897
|
let prevStyle;
|
|
851
898
|
text.forEachCharacter((character) => {
|
|
852
|
-
const {
|
|
853
|
-
|
|
854
|
-
|
|
899
|
+
const {
|
|
900
|
+
computedStyle: style
|
|
901
|
+
} = character;
|
|
902
|
+
const {
|
|
903
|
+
highlightImage
|
|
904
|
+
} = style;
|
|
905
|
+
if (!isNone(highlightImage)) {
|
|
906
|
+
const {
|
|
907
|
+
inlineBox,
|
|
908
|
+
isVertical
|
|
909
|
+
} = character;
|
|
910
|
+
const {
|
|
911
|
+
fontSize,
|
|
912
|
+
highlightColormap,
|
|
913
|
+
highlightLine,
|
|
914
|
+
highlightSize,
|
|
915
|
+
highlightThickness
|
|
916
|
+
} = style;
|
|
917
|
+
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) {
|
|
855
918
|
group.push(character);
|
|
856
919
|
} else {
|
|
857
920
|
group = [];
|
|
@@ -870,8 +933,12 @@ function highlight() {
|
|
|
870
933
|
for (let i = 0; i < groups.length; i++) {
|
|
871
934
|
const characters = groups[i];
|
|
872
935
|
const char = characters[0];
|
|
873
|
-
const groupBox = modernPath2d.BoundingBox.from(
|
|
874
|
-
|
|
936
|
+
const groupBox = modernPath2d.BoundingBox.from(
|
|
937
|
+
...characters.filter((c) => c.glyphBox).map((c) => c.glyphBox)
|
|
938
|
+
);
|
|
939
|
+
const {
|
|
940
|
+
computedStyle: style
|
|
941
|
+
} = char;
|
|
875
942
|
const {
|
|
876
943
|
fontSize,
|
|
877
944
|
writingMode,
|
|
@@ -885,7 +952,7 @@ function highlight() {
|
|
|
885
952
|
const isVertical = writingMode.includes("vertical");
|
|
886
953
|
const thickness = parseValueNumber(highlightThickness, { fontSize, total: groupBox.width }) / groupBox.width;
|
|
887
954
|
const colormap = parseColormap(highlightColormap);
|
|
888
|
-
const { paths: svgPaths, dom: svgDom } =
|
|
955
|
+
const { paths: svgPaths, dom: svgDom } = getPaths(highlightImage);
|
|
889
956
|
const aBox = modernPath2d.getPathsBoundingBox(svgPaths, true);
|
|
890
957
|
const styleScale = fontSize / aBox.width * 2;
|
|
891
958
|
const cBox = new modernPath2d.BoundingBox().copy(groupBox);
|
|
@@ -901,7 +968,7 @@ function highlight() {
|
|
|
901
968
|
cBox.width = userWidth;
|
|
902
969
|
}
|
|
903
970
|
if (!isNone(highlightReferImage) && isNone(highlightLine)) {
|
|
904
|
-
const bBox = modernPath2d.getPathsBoundingBox(
|
|
971
|
+
const bBox = modernPath2d.getPathsBoundingBox(getPaths(highlightReferImage).paths, true);
|
|
905
972
|
aBox.copy(bBox);
|
|
906
973
|
} else {
|
|
907
974
|
let line;
|
|
@@ -1052,8 +1119,16 @@ function listStyle() {
|
|
|
1052
1119
|
const { paragraphs, isVertical, fontSize } = text;
|
|
1053
1120
|
const padding = fontSize * 0.45;
|
|
1054
1121
|
paragraphs.forEach((paragraph) => {
|
|
1055
|
-
const {
|
|
1056
|
-
|
|
1122
|
+
const {
|
|
1123
|
+
computedStyle: style
|
|
1124
|
+
} = paragraph;
|
|
1125
|
+
const {
|
|
1126
|
+
color,
|
|
1127
|
+
listStyleImage,
|
|
1128
|
+
listStyleColormap,
|
|
1129
|
+
listStyleSize,
|
|
1130
|
+
listStyleType
|
|
1131
|
+
} = style;
|
|
1057
1132
|
const colormap = parseColormap(listStyleColormap);
|
|
1058
1133
|
let size = listStyleSize;
|
|
1059
1134
|
let image;
|
|
@@ -1082,18 +1157,10 @@ function listStyle() {
|
|
|
1082
1157
|
const m = new modernPath2d.Matrix3();
|
|
1083
1158
|
if (isVertical) {
|
|
1084
1159
|
const reScale = fontSize / imageBox.height * scale;
|
|
1085
|
-
m.translate(-imageBox.left, -imageBox.top);
|
|
1086
|
-
m.rotate(Math.PI / 2);
|
|
1087
|
-
m.scale(reScale, reScale);
|
|
1088
|
-
m.translate(fontSize / 2 - imageBox.height * reScale / 2, 0);
|
|
1089
|
-
m.translate(box.left + (box.width - fontSize) / 2, fBox.top - padding);
|
|
1160
|
+
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);
|
|
1090
1161
|
} else {
|
|
1091
1162
|
const reScale = fontSize / imageBox.height * scale;
|
|
1092
|
-
m.translate(-imageBox.left, -imageBox.top);
|
|
1093
|
-
m.translate(-imageBox.width, 0);
|
|
1094
|
-
m.scale(reScale, reScale);
|
|
1095
|
-
m.translate(0, fontSize / 2 - imageBox.height * reScale / 2);
|
|
1096
|
-
m.translate(fBox.left - padding, box.top + (box.height - fontSize) / 2);
|
|
1163
|
+
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);
|
|
1097
1164
|
}
|
|
1098
1165
|
paths.push(...imagePaths.map((p) => {
|
|
1099
1166
|
const path = p.clone();
|
|
@@ -1229,11 +1296,24 @@ function textDecoration() {
|
|
|
1229
1296
|
let group;
|
|
1230
1297
|
let prevStyle;
|
|
1231
1298
|
text.forEachCharacter((character) => {
|
|
1232
|
-
const {
|
|
1233
|
-
|
|
1299
|
+
const {
|
|
1300
|
+
computedStyle: style,
|
|
1301
|
+
isVertical,
|
|
1302
|
+
inlineBox,
|
|
1303
|
+
underlinePosition,
|
|
1304
|
+
underlineThickness,
|
|
1305
|
+
strikeoutPosition,
|
|
1306
|
+
strikeoutSize
|
|
1307
|
+
} = character;
|
|
1308
|
+
const {
|
|
1309
|
+
color,
|
|
1310
|
+
textDecoration: textDecoration2,
|
|
1311
|
+
writingMode
|
|
1312
|
+
} = style;
|
|
1313
|
+
if (!isNone(textDecoration2)) {
|
|
1234
1314
|
let flag = false;
|
|
1235
|
-
if (prevStyle?.textDecoration ===
|
|
1236
|
-
switch (
|
|
1315
|
+
if (prevStyle?.textDecoration === textDecoration2 && prevStyle?.writingMode === writingMode && prevStyle?.color === color && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top)) {
|
|
1316
|
+
switch (textDecoration2) {
|
|
1237
1317
|
case "underline":
|
|
1238
1318
|
if (group[0].underlinePosition === underlinePosition && group[0].underlineThickness === underlineThickness) {
|
|
1239
1319
|
flag = true;
|
|
@@ -1259,8 +1339,18 @@ function textDecoration() {
|
|
|
1259
1339
|
}
|
|
1260
1340
|
});
|
|
1261
1341
|
groups.forEach((group2) => {
|
|
1262
|
-
const {
|
|
1263
|
-
|
|
1342
|
+
const {
|
|
1343
|
+
computedStyle: style,
|
|
1344
|
+
isVertical,
|
|
1345
|
+
underlinePosition,
|
|
1346
|
+
underlineThickness,
|
|
1347
|
+
strikeoutPosition,
|
|
1348
|
+
strikeoutSize
|
|
1349
|
+
} = group2[0];
|
|
1350
|
+
const {
|
|
1351
|
+
color,
|
|
1352
|
+
textDecoration: textDecoration2
|
|
1353
|
+
} = style;
|
|
1264
1354
|
const { left, top, width, height } = modernPath2d.BoundingBox.from(...group2.map((c) => c.inlineBox));
|
|
1265
1355
|
let position = isVertical ? left + width : top;
|
|
1266
1356
|
const direction = isVertical ? -1 : 1;
|
|
@@ -1403,7 +1493,7 @@ class Text extends EventEmitter {
|
|
|
1403
1493
|
__publicField(this, "glyphBox", new modernPath2d.BoundingBox());
|
|
1404
1494
|
__publicField(this, "pathBox", new modernPath2d.BoundingBox());
|
|
1405
1495
|
__publicField(this, "boundingBox", new modernPath2d.BoundingBox());
|
|
1406
|
-
__publicField(this, "measurer", new Measurer(
|
|
1496
|
+
__publicField(this, "measurer", new Measurer());
|
|
1407
1497
|
__publicField(this, "plugins", /* @__PURE__ */ new Map());
|
|
1408
1498
|
__publicField(this, "fonts");
|
|
1409
1499
|
this.debug = options.debug ?? false;
|
|
@@ -1437,6 +1527,9 @@ class Text extends EventEmitter {
|
|
|
1437
1527
|
});
|
|
1438
1528
|
return this;
|
|
1439
1529
|
}
|
|
1530
|
+
async load() {
|
|
1531
|
+
await Promise.all(Array.from(this.plugins.values()).map((p) => p.load?.(this)));
|
|
1532
|
+
}
|
|
1440
1533
|
updateParagraphs() {
|
|
1441
1534
|
this.computedStyle = { ...defaultTextStyles, ...this.style };
|
|
1442
1535
|
let { content, computedStyle: style } = this;
|
|
@@ -1488,7 +1581,7 @@ class Text extends EventEmitter {
|
|
|
1488
1581
|
this.paragraphs = paragraphs;
|
|
1489
1582
|
return this;
|
|
1490
1583
|
}
|
|
1491
|
-
|
|
1584
|
+
measure(dom = this.measureDom) {
|
|
1492
1585
|
const old = {
|
|
1493
1586
|
paragraphs: this.paragraphs,
|
|
1494
1587
|
lineBox: this.lineBox,
|
|
@@ -1498,18 +1591,16 @@ class Text extends EventEmitter {
|
|
|
1498
1591
|
boundingBox: this.boundingBox
|
|
1499
1592
|
};
|
|
1500
1593
|
this.updateParagraphs();
|
|
1501
|
-
const result = this.measurer.measure(dom);
|
|
1594
|
+
const result = this.measurer.measure(this.paragraphs, this.computedStyle, dom);
|
|
1502
1595
|
this.paragraphs = result.paragraphs;
|
|
1503
1596
|
this.lineBox = result.boundingBox;
|
|
1504
1597
|
this.characters.forEach((c) => {
|
|
1505
1598
|
c.update(this.fonts);
|
|
1506
1599
|
});
|
|
1507
1600
|
this.rawGlyphBox = this.getGlyphBox();
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
await plugins[i].update?.(this);
|
|
1512
|
-
}
|
|
1601
|
+
Array.from(this.plugins.values()).sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
|
|
1602
|
+
plugin.update?.(this);
|
|
1603
|
+
});
|
|
1513
1604
|
this.glyphBox = this.getGlyphBox();
|
|
1514
1605
|
this.updatePathBox().updateBoundingBox();
|
|
1515
1606
|
for (const key in old) {
|
|
@@ -1565,30 +1656,28 @@ class Text extends EventEmitter {
|
|
|
1565
1656
|
this.needsUpdate = true;
|
|
1566
1657
|
return this;
|
|
1567
1658
|
}
|
|
1568
|
-
|
|
1569
|
-
const result =
|
|
1659
|
+
update() {
|
|
1660
|
+
const result = this.measure();
|
|
1570
1661
|
for (const key in result) {
|
|
1571
1662
|
this[key] = result[key];
|
|
1572
1663
|
}
|
|
1573
1664
|
this.emit("update", { text: this });
|
|
1665
|
+
return this;
|
|
1574
1666
|
}
|
|
1575
|
-
|
|
1667
|
+
render(options) {
|
|
1576
1668
|
const { view, pixelRatio = 2 } = options;
|
|
1577
1669
|
const ctx = view.getContext("2d");
|
|
1578
1670
|
if (!ctx) {
|
|
1579
1671
|
return;
|
|
1580
1672
|
}
|
|
1581
1673
|
if (this.needsUpdate) {
|
|
1582
|
-
|
|
1674
|
+
this.update();
|
|
1583
1675
|
}
|
|
1584
1676
|
setupView(ctx, pixelRatio, this.boundingBox);
|
|
1585
1677
|
uploadColors(ctx, this);
|
|
1586
|
-
|
|
1587
|
-
plugins.sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0));
|
|
1588
|
-
for (let i = 0; i < plugins.length; i++) {
|
|
1589
|
-
const plugin = plugins[i];
|
|
1678
|
+
Array.from(this.plugins.values()).sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0)).forEach((plugin) => {
|
|
1590
1679
|
if (plugin.render) {
|
|
1591
|
-
|
|
1680
|
+
plugin.render?.(ctx, this);
|
|
1592
1681
|
} else if (plugin.paths) {
|
|
1593
1682
|
const style = this.computedStyle;
|
|
1594
1683
|
plugin.paths.forEach((path) => {
|
|
@@ -1599,17 +1688,21 @@ class Text extends EventEmitter {
|
|
|
1599
1688
|
});
|
|
1600
1689
|
});
|
|
1601
1690
|
}
|
|
1602
|
-
}
|
|
1691
|
+
});
|
|
1603
1692
|
this.emit("render", { text: this, view, pixelRatio });
|
|
1604
1693
|
}
|
|
1605
1694
|
}
|
|
1606
1695
|
|
|
1607
|
-
function measureText(options) {
|
|
1608
|
-
|
|
1696
|
+
async function measureText(options) {
|
|
1697
|
+
const text = new Text(options);
|
|
1698
|
+
await text.load();
|
|
1699
|
+
return text.measure();
|
|
1609
1700
|
}
|
|
1610
1701
|
|
|
1611
|
-
function renderText(options) {
|
|
1612
|
-
|
|
1702
|
+
async function renderText(options) {
|
|
1703
|
+
const text = new Text(options);
|
|
1704
|
+
await text.load();
|
|
1705
|
+
text.render(options);
|
|
1613
1706
|
}
|
|
1614
1707
|
|
|
1615
1708
|
exports.Character = Character;
|
|
@@ -1629,6 +1722,7 @@ exports.isEqualValue = isEqualValue;
|
|
|
1629
1722
|
exports.isNone = isNone;
|
|
1630
1723
|
exports.listStyle = listStyle;
|
|
1631
1724
|
exports.measureText = measureText;
|
|
1725
|
+
exports.needsFetch = needsFetch;
|
|
1632
1726
|
exports.parseColor = parseColor;
|
|
1633
1727
|
exports.parseColormap = parseColormap;
|
|
1634
1728
|
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,17 +132,24 @@ declare class Measurer {
|
|
|
134
132
|
* </ul>
|
|
135
133
|
* </section>
|
|
136
134
|
*/
|
|
137
|
-
|
|
135
|
+
createParagraphDom(paragraphs: Paragraph[], rootStyle: TextStyle): {
|
|
138
136
|
dom: HTMLElement;
|
|
139
137
|
destory: () => void;
|
|
140
138
|
};
|
|
141
|
-
|
|
139
|
+
measureDomText(text: Text): {
|
|
140
|
+
content: string;
|
|
141
|
+
top: number;
|
|
142
|
+
left: number;
|
|
143
|
+
width: number;
|
|
144
|
+
height: number;
|
|
145
|
+
}[];
|
|
146
|
+
measureDom(dom: HTMLElement): {
|
|
142
147
|
paragraphs: MeasuredParagraph[];
|
|
143
148
|
fragments: MeasuredFragment[];
|
|
144
149
|
characters: MeasuredCharacter[];
|
|
145
150
|
};
|
|
146
|
-
|
|
147
|
-
measure(dom?: HTMLElement): MeasureDomResult;
|
|
151
|
+
measureParagraphDom(paragraphs: Paragraph[], dom: HTMLElement): MeasureDomResult;
|
|
152
|
+
measure(paragraphs: Paragraph[], rootStyle: TextStyle, dom?: HTMLElement): MeasureDomResult;
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
interface TextRenderOptions {
|
|
@@ -162,19 +167,19 @@ interface MeasureResult {
|
|
|
162
167
|
declare const defaultTextStyles: TextStyle;
|
|
163
168
|
interface TextEventMap {
|
|
164
169
|
update: {
|
|
165
|
-
text: Text;
|
|
170
|
+
text: Text$1;
|
|
166
171
|
};
|
|
167
172
|
measure: {
|
|
168
|
-
text: Text;
|
|
173
|
+
text: Text$1;
|
|
169
174
|
result: MeasureResult;
|
|
170
175
|
};
|
|
171
176
|
render: {
|
|
172
|
-
text: Text;
|
|
177
|
+
text: Text$1;
|
|
173
178
|
view: HTMLCanvasElement;
|
|
174
179
|
pixelRatio: number;
|
|
175
180
|
};
|
|
176
181
|
}
|
|
177
|
-
declare class Text extends EventEmitter<TextEventMap> {
|
|
182
|
+
declare class Text$1 extends EventEmitter<TextEventMap> {
|
|
178
183
|
debug: boolean;
|
|
179
184
|
content: TextContent;
|
|
180
185
|
style: Partial<TextStyle>;
|
|
@@ -201,14 +206,15 @@ declare class Text extends EventEmitter<TextEventMap> {
|
|
|
201
206
|
fragmentIndex: number;
|
|
202
207
|
characterIndex: number;
|
|
203
208
|
}) => void): this;
|
|
209
|
+
load(): Promise<void>;
|
|
204
210
|
updateParagraphs(): this;
|
|
205
|
-
measure(dom?: HTMLElement | undefined):
|
|
211
|
+
measure(dom?: HTMLElement | undefined): MeasureResult;
|
|
206
212
|
getGlyphBox(): BoundingBox;
|
|
207
213
|
updatePathBox(): this;
|
|
208
214
|
updateBoundingBox(): this;
|
|
209
215
|
requestUpdate(): this;
|
|
210
|
-
update():
|
|
211
|
-
render(options: TextRenderOptions):
|
|
216
|
+
update(): this;
|
|
217
|
+
render(options: TextRenderOptions): void;
|
|
212
218
|
}
|
|
213
219
|
|
|
214
220
|
type Sizeable = `${number}%` | `${number}rem` | number;
|
|
@@ -286,15 +292,15 @@ interface ParagraphContent extends Partial<TextStyle> {
|
|
|
286
292
|
fragments: FragmentContent[];
|
|
287
293
|
}
|
|
288
294
|
type TextContent = string | FragmentContent | ParagraphContent | (string | FragmentContent | ParagraphContent | (string | FragmentContent)[])[];
|
|
289
|
-
type PromiseLike<T> = T | Promise<T>;
|
|
290
295
|
interface TextPlugin {
|
|
291
296
|
name: string;
|
|
292
297
|
paths?: Path2D[];
|
|
293
|
-
getBoundingBox?: (text: Text) => BoundingBox | undefined;
|
|
298
|
+
getBoundingBox?: (text: Text$1) => BoundingBox | undefined;
|
|
299
|
+
update?: (text: Text$1) => void;
|
|
294
300
|
updateOrder?: number;
|
|
295
|
-
|
|
301
|
+
render?: (ctx: CanvasRenderingContext2D, text: Text$1) => void;
|
|
296
302
|
renderOrder?: number;
|
|
297
|
-
|
|
303
|
+
load?: (text: Text$1) => Promise<void>;
|
|
298
304
|
}
|
|
299
305
|
interface TextOptions {
|
|
300
306
|
debug?: boolean;
|
|
@@ -328,23 +334,23 @@ declare function drawPath(options: DrawShapePathsOptions): void;
|
|
|
328
334
|
|
|
329
335
|
declare function setupView(ctx: CanvasRenderingContext2D, pixelRatio: number, boundingBox: BoundingBox): void;
|
|
330
336
|
|
|
331
|
-
declare function uploadColors(ctx: CanvasRenderingContext2D, text: Text): void;
|
|
337
|
+
declare function uploadColors(ctx: CanvasRenderingContext2D, text: Text$1): void;
|
|
332
338
|
|
|
333
339
|
declare function definePlugin(options: TextPlugin): TextPlugin;
|
|
334
340
|
|
|
335
341
|
declare function measureText(options: TextOptions): Promise<MeasureResult>;
|
|
336
342
|
|
|
343
|
+
declare function renderText(options: TextOptions & TextRenderOptions): Promise<void>;
|
|
344
|
+
|
|
337
345
|
declare function highlight(): TextPlugin;
|
|
338
346
|
|
|
339
347
|
declare function listStyle(): TextPlugin;
|
|
340
348
|
|
|
341
349
|
declare function render(): TextPlugin;
|
|
342
|
-
declare function getTransform2D(text: Text, style: Partial<TextStyle>): Matrix3;
|
|
350
|
+
declare function getTransform2D(text: Text$1, style: Partial<TextStyle>): Matrix3;
|
|
343
351
|
|
|
344
352
|
declare function textDecoration(): TextPlugin;
|
|
345
353
|
|
|
346
|
-
declare function renderText(options: TextOptions & TextRenderOptions): Promise<void>;
|
|
347
|
-
|
|
348
354
|
interface ValueContext {
|
|
349
355
|
total: number;
|
|
350
356
|
fontSize: number;
|
|
@@ -356,5 +362,6 @@ declare function isEqualObject(obj1: Record<string, any>, obj2: Record<string, a
|
|
|
356
362
|
declare function isEqualValue(val1: any, val2: any): boolean;
|
|
357
363
|
declare function hexToRgb(hex: string): string | null;
|
|
358
364
|
declare function filterEmpty(val: Record<string, any> | undefined): Record<string, any> | undefined;
|
|
365
|
+
declare function needsFetch(source: string): boolean;
|
|
359
366
|
|
|
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 };
|
|
367
|
+
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$1 as 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 };
|