modern-text 1.3.0 → 1.3.2
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/README.md +23 -20
- package/dist/index.cjs +227 -148
- package/dist/index.d.cts +23 -4
- package/dist/index.d.mts +23 -4
- package/dist/index.d.ts +23 -4
- package/dist/index.js +5 -5
- package/dist/index.mjs +227 -149
- package/package.json +12 -12
package/dist/index.cjs
CHANGED
|
@@ -377,6 +377,49 @@ class Character {
|
|
|
377
377
|
}
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
function createSVGLoader() {
|
|
381
|
+
const loaded = /* @__PURE__ */ new Map();
|
|
382
|
+
async function load(svg) {
|
|
383
|
+
if (!loaded.has(svg)) {
|
|
384
|
+
loaded.set(svg, svg);
|
|
385
|
+
try {
|
|
386
|
+
loaded.set(svg, await fetch(svg).then((rep) => rep.text()));
|
|
387
|
+
} catch (err) {
|
|
388
|
+
console.warn(err);
|
|
389
|
+
loaded.delete(svg);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
function needsLoad(source) {
|
|
394
|
+
return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("blob://");
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
loaded,
|
|
398
|
+
needsLoad,
|
|
399
|
+
load
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function createSVGParser(loader) {
|
|
404
|
+
const parsed = /* @__PURE__ */ new Map();
|
|
405
|
+
function parse(svg) {
|
|
406
|
+
let result = parsed.get(svg);
|
|
407
|
+
if (!result) {
|
|
408
|
+
const dom = modernPath2d.svgToDOM(
|
|
409
|
+
loader.needsLoad(svg) ? loader.loaded.get(svg) ?? svg : svg
|
|
410
|
+
);
|
|
411
|
+
const pathSet = modernPath2d.svgToPath2DSet(dom);
|
|
412
|
+
result = { dom, pathSet };
|
|
413
|
+
parsed.set(svg, result);
|
|
414
|
+
}
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
parsed,
|
|
419
|
+
parse
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
380
423
|
function parseValueNumber(value, ctx) {
|
|
381
424
|
if (typeof value === "number") {
|
|
382
425
|
return value;
|
|
@@ -453,9 +496,6 @@ function filterEmpty(val) {
|
|
|
453
496
|
}
|
|
454
497
|
return res;
|
|
455
498
|
}
|
|
456
|
-
function needsFetch(source) {
|
|
457
|
-
return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("blob://");
|
|
458
|
-
}
|
|
459
499
|
|
|
460
500
|
class Fragment {
|
|
461
501
|
constructor(content, style = {}, parent) {
|
|
@@ -567,36 +607,38 @@ class Measurer {
|
|
|
567
607
|
switch (rootStyle.textAlign) {
|
|
568
608
|
case "start":
|
|
569
609
|
case "left":
|
|
570
|
-
style.justifyContent = "start";
|
|
610
|
+
style.justifyContent = "flex-start";
|
|
571
611
|
break;
|
|
572
612
|
case "center":
|
|
573
613
|
style.justifyContent = "center";
|
|
574
614
|
break;
|
|
575
615
|
case "end":
|
|
576
616
|
case "right":
|
|
577
|
-
style.justifyContent = "end";
|
|
617
|
+
style.justifyContent = "flex-end";
|
|
578
618
|
break;
|
|
579
619
|
}
|
|
580
620
|
switch (rootStyle.verticalAlign) {
|
|
581
621
|
case "top":
|
|
582
|
-
style.alignItems = "
|
|
622
|
+
style.alignItems = "flex-start";
|
|
583
623
|
break;
|
|
584
624
|
case "middle":
|
|
585
625
|
style.alignItems = "center";
|
|
586
626
|
break;
|
|
587
627
|
case "bottom":
|
|
588
|
-
style.alignItems = "end";
|
|
628
|
+
style.alignItems = "flex-end";
|
|
589
629
|
break;
|
|
590
630
|
}
|
|
591
631
|
const isFlex = Boolean(style.justifyContent || style.alignItems);
|
|
592
632
|
Object.assign(dom.style, {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
633
|
+
...this._styleToDomStyle({
|
|
634
|
+
...style,
|
|
635
|
+
boxSizing: style.boxSizing ?? "border-box",
|
|
636
|
+
display: style.display ?? (isFlex ? "inline-flex" : void 0),
|
|
637
|
+
width: style.width ?? "max-content",
|
|
638
|
+
height: style.height ?? "max-content"
|
|
639
|
+
}),
|
|
597
640
|
whiteSpace: "pre-wrap",
|
|
598
641
|
wordBreak: "break-all",
|
|
599
|
-
...this._styleToDomStyle(style),
|
|
600
642
|
position: "fixed",
|
|
601
643
|
visibility: "hidden"
|
|
602
644
|
});
|
|
@@ -874,9 +916,64 @@ class EventEmitter {
|
|
|
874
916
|
}
|
|
875
917
|
|
|
876
918
|
function background() {
|
|
919
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
920
|
+
const loader = createSVGLoader();
|
|
921
|
+
const parser = createSVGParser(loader);
|
|
877
922
|
return {
|
|
878
|
-
name: "background"
|
|
879
|
-
|
|
923
|
+
name: "background",
|
|
924
|
+
pathSet,
|
|
925
|
+
load: async (text) => {
|
|
926
|
+
const { backgroundImage } = text.style;
|
|
927
|
+
if (backgroundImage && loader.needsLoad(backgroundImage)) {
|
|
928
|
+
await loader.load(backgroundImage);
|
|
929
|
+
}
|
|
930
|
+
},
|
|
931
|
+
update: (text) => {
|
|
932
|
+
pathSet.paths.length = 0;
|
|
933
|
+
const { style, lineBox, isVertical } = text;
|
|
934
|
+
if (isNone(style.backgroundImage))
|
|
935
|
+
return;
|
|
936
|
+
const { pathSet: imagePathSet } = parser.parse(style.backgroundImage);
|
|
937
|
+
const imageBox = imagePathSet.getBoundingBox(true);
|
|
938
|
+
const transform = new modernPath2d.Matrix3();
|
|
939
|
+
transform.translate(-imageBox.x, -imageBox.y);
|
|
940
|
+
if (isVertical) {
|
|
941
|
+
transform.scale(lineBox.height / imageBox.width, lineBox.width / imageBox.height);
|
|
942
|
+
const tx = lineBox.height / 2;
|
|
943
|
+
const ty = lineBox.width / 2;
|
|
944
|
+
transform.translate(-tx, -ty);
|
|
945
|
+
transform.rotate(-Math.PI / 2);
|
|
946
|
+
transform.translate(ty, tx);
|
|
947
|
+
} else {
|
|
948
|
+
transform.scale(lineBox.width / imageBox.width, lineBox.height / imageBox.height);
|
|
949
|
+
}
|
|
950
|
+
transform.translate(lineBox.x, lineBox.y);
|
|
951
|
+
imagePathSet.paths.forEach((originalPath) => {
|
|
952
|
+
pathSet.paths.push(
|
|
953
|
+
originalPath.clone().applyTransform(transform)
|
|
954
|
+
);
|
|
955
|
+
});
|
|
956
|
+
},
|
|
957
|
+
render: (ctx, text) => {
|
|
958
|
+
const { boundingBox, computedStyle: style } = text;
|
|
959
|
+
if (!isNone(style.backgroundColor)) {
|
|
960
|
+
ctx.fillStyle = style.backgroundColor;
|
|
961
|
+
ctx.fillRect(...boundingBox.array);
|
|
962
|
+
}
|
|
963
|
+
pathSet.paths.forEach((path) => {
|
|
964
|
+
drawPath({
|
|
965
|
+
ctx,
|
|
966
|
+
path,
|
|
967
|
+
fontSize: style.fontSize
|
|
968
|
+
});
|
|
969
|
+
if (text.debug) {
|
|
970
|
+
const box = new modernPath2d.Path2DSet([path]).getBoundingBox();
|
|
971
|
+
if (box) {
|
|
972
|
+
ctx.strokeRect(box.x, box.y, box.width, box.height);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
}
|
|
880
977
|
};
|
|
881
978
|
}
|
|
882
979
|
|
|
@@ -900,53 +997,30 @@ function getHighlightStyle(style) {
|
|
|
900
997
|
};
|
|
901
998
|
}
|
|
902
999
|
function highlight() {
|
|
903
|
-
const
|
|
1000
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
904
1001
|
const clipRects = [];
|
|
905
|
-
const
|
|
906
|
-
const
|
|
907
|
-
async function loadSvg(svg) {
|
|
908
|
-
if (!loaded.has(svg)) {
|
|
909
|
-
loaded.set(svg, svg);
|
|
910
|
-
try {
|
|
911
|
-
loaded.set(svg, await fetch(svg).then((rep) => rep.text()));
|
|
912
|
-
} catch (err) {
|
|
913
|
-
console.warn(err);
|
|
914
|
-
loaded.delete(svg);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
function getPaths(svg) {
|
|
919
|
-
let result = parsed.get(svg);
|
|
920
|
-
if (!result) {
|
|
921
|
-
const dom = modernPath2d.svgToDOM(
|
|
922
|
-
needsFetch(svg) ? loaded.get(svg) ?? svg : svg
|
|
923
|
-
);
|
|
924
|
-
const pathSet = modernPath2d.svgToPath2DSet(dom);
|
|
925
|
-
result = { dom, pathSet };
|
|
926
|
-
parsed.set(svg, result);
|
|
927
|
-
}
|
|
928
|
-
return result;
|
|
929
|
-
}
|
|
1002
|
+
const loader = createSVGLoader();
|
|
1003
|
+
const parser = createSVGParser(loader);
|
|
930
1004
|
return definePlugin({
|
|
931
1005
|
name: "highlight",
|
|
932
|
-
|
|
1006
|
+
pathSet,
|
|
933
1007
|
load: async (text) => {
|
|
934
1008
|
const set = /* @__PURE__ */ new Set();
|
|
935
1009
|
text.forEachCharacter((character) => {
|
|
936
1010
|
const { computedStyle: style } = character;
|
|
937
1011
|
const { image, referImage } = getHighlightStyle(style);
|
|
938
|
-
if (
|
|
1012
|
+
if (loader.needsLoad(image)) {
|
|
939
1013
|
set.add(image);
|
|
940
1014
|
}
|
|
941
|
-
if (
|
|
1015
|
+
if (loader.needsLoad(referImage)) {
|
|
942
1016
|
set.add(referImage);
|
|
943
1017
|
}
|
|
944
1018
|
});
|
|
945
|
-
await Promise.all(Array.from(set).map((src) =>
|
|
1019
|
+
await Promise.all(Array.from(set).map((src) => loader.load(src)));
|
|
946
1020
|
},
|
|
947
1021
|
update: (text) => {
|
|
948
1022
|
clipRects.length = 0;
|
|
949
|
-
paths.length = 0;
|
|
1023
|
+
pathSet.paths.length = 0;
|
|
950
1024
|
let groups = [];
|
|
951
1025
|
let group;
|
|
952
1026
|
let prevHighlight;
|
|
@@ -993,11 +1067,15 @@ function highlight() {
|
|
|
993
1067
|
...characters.filter((c) => c.glyphBox).map((c) => c.glyphBox)
|
|
994
1068
|
);
|
|
995
1069
|
const {
|
|
996
|
-
computedStyle: style
|
|
1070
|
+
computedStyle: style,
|
|
1071
|
+
isVertical,
|
|
1072
|
+
inlineBox,
|
|
1073
|
+
glyphBox = inlineBox,
|
|
1074
|
+
strikeoutPosition,
|
|
1075
|
+
underlinePosition
|
|
997
1076
|
} = char;
|
|
998
1077
|
const {
|
|
999
|
-
fontSize
|
|
1000
|
-
writingMode
|
|
1078
|
+
fontSize
|
|
1001
1079
|
} = style;
|
|
1002
1080
|
const {
|
|
1003
1081
|
image,
|
|
@@ -1007,39 +1085,40 @@ function highlight() {
|
|
|
1007
1085
|
size,
|
|
1008
1086
|
thickness
|
|
1009
1087
|
} = getHighlightStyle(style);
|
|
1010
|
-
const isVertical = writingMode.includes("vertical");
|
|
1011
1088
|
const _thickness = parseValueNumber(thickness, { fontSize, total: groupBox.width }) / groupBox.width;
|
|
1012
1089
|
const _colormap = parseColormap(colormap);
|
|
1013
|
-
const { pathSet:
|
|
1014
|
-
const
|
|
1015
|
-
const styleScale = fontSize /
|
|
1016
|
-
const
|
|
1090
|
+
const { pathSet: imagePathSet, dom: imageDom } = parser.parse(image);
|
|
1091
|
+
const imageBox = imagePathSet.getBoundingBox(true);
|
|
1092
|
+
const styleScale = fontSize / imageBox.width * 2;
|
|
1093
|
+
const targetBox = new modernPath2d.BoundingBox().copy(groupBox);
|
|
1017
1094
|
if (isVertical) {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1095
|
+
targetBox.width = groupBox.height;
|
|
1096
|
+
targetBox.height = groupBox.width;
|
|
1097
|
+
targetBox.left = groupBox.left + groupBox.width;
|
|
1021
1098
|
}
|
|
1022
|
-
const rawWidth = Math.floor(
|
|
1099
|
+
const rawWidth = Math.floor(targetBox.width);
|
|
1023
1100
|
let userWidth = rawWidth;
|
|
1024
1101
|
if (size !== "cover") {
|
|
1025
1102
|
userWidth = parseValueNumber(size, { fontSize, total: groupBox.width }) || rawWidth;
|
|
1026
|
-
|
|
1103
|
+
targetBox.width = userWidth;
|
|
1027
1104
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1105
|
+
const hasReferImage = !isNone(referImage) && isNone(line);
|
|
1106
|
+
if (hasReferImage) {
|
|
1107
|
+
imageBox.copy(
|
|
1108
|
+
parser.parse(referImage).pathSet.getBoundingBox(true)
|
|
1109
|
+
);
|
|
1031
1110
|
} else {
|
|
1032
1111
|
let _line;
|
|
1033
1112
|
if (isNone(line)) {
|
|
1034
|
-
if (
|
|
1113
|
+
if (imageBox.width / imageBox.height > 4) {
|
|
1035
1114
|
_line = "underline";
|
|
1036
|
-
const viewBox =
|
|
1115
|
+
const viewBox = imageDom.getAttribute("viewBox");
|
|
1037
1116
|
if (viewBox) {
|
|
1038
1117
|
const [_x, y, _w, h] = viewBox.split(" ").map((v) => Number(v));
|
|
1039
1118
|
const viewCenter = y + h / 2;
|
|
1040
|
-
if (
|
|
1119
|
+
if (imageBox.y < viewCenter && imageBox.y + imageBox.height > viewCenter) {
|
|
1041
1120
|
_line = "line-through";
|
|
1042
|
-
} else if (
|
|
1121
|
+
} else if (imageBox.y + imageBox.height < viewCenter) {
|
|
1043
1122
|
_line = "overline";
|
|
1044
1123
|
} else {
|
|
1045
1124
|
_line = "underline";
|
|
@@ -1053,89 +1132,95 @@ function highlight() {
|
|
|
1053
1132
|
}
|
|
1054
1133
|
switch (_line) {
|
|
1055
1134
|
case "outline": {
|
|
1056
|
-
const paddingX =
|
|
1057
|
-
const paddingY =
|
|
1058
|
-
cBox.width += paddingX;
|
|
1059
|
-
cBox.height += paddingY;
|
|
1135
|
+
const paddingX = targetBox.width * 0.2;
|
|
1136
|
+
const paddingY = targetBox.height * 0.2;
|
|
1060
1137
|
if (isVertical) {
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1138
|
+
targetBox.x -= paddingY / 2;
|
|
1139
|
+
targetBox.y -= paddingX / 2;
|
|
1140
|
+
targetBox.x -= targetBox.height;
|
|
1064
1141
|
} else {
|
|
1065
|
-
|
|
1066
|
-
|
|
1142
|
+
targetBox.x -= paddingX / 2;
|
|
1143
|
+
targetBox.y -= paddingY / 2;
|
|
1067
1144
|
}
|
|
1145
|
+
targetBox.width += paddingX;
|
|
1146
|
+
targetBox.height += paddingY;
|
|
1068
1147
|
break;
|
|
1069
1148
|
}
|
|
1070
1149
|
case "overline":
|
|
1071
|
-
|
|
1150
|
+
targetBox.height = imageBox.height * styleScale;
|
|
1072
1151
|
if (isVertical) {
|
|
1073
|
-
|
|
1152
|
+
targetBox.x = inlineBox.left + inlineBox.width;
|
|
1074
1153
|
} else {
|
|
1075
|
-
|
|
1154
|
+
targetBox.y = inlineBox.top;
|
|
1076
1155
|
}
|
|
1077
1156
|
break;
|
|
1078
1157
|
case "line-through":
|
|
1079
|
-
|
|
1158
|
+
targetBox.height = imageBox.height * styleScale;
|
|
1080
1159
|
if (isVertical) {
|
|
1081
|
-
|
|
1160
|
+
targetBox.x = inlineBox.left + inlineBox.width - strikeoutPosition + targetBox.height / 2;
|
|
1082
1161
|
} else {
|
|
1083
|
-
|
|
1162
|
+
targetBox.y = inlineBox.top + strikeoutPosition - targetBox.height / 2;
|
|
1084
1163
|
}
|
|
1085
1164
|
break;
|
|
1086
1165
|
case "underline":
|
|
1087
|
-
|
|
1166
|
+
targetBox.height = imageBox.height * styleScale;
|
|
1088
1167
|
if (isVertical) {
|
|
1089
|
-
|
|
1168
|
+
targetBox.x = glyphBox.left + glyphBox.width - underlinePosition;
|
|
1090
1169
|
} else {
|
|
1091
|
-
|
|
1170
|
+
targetBox.y = inlineBox.top + underlinePosition;
|
|
1092
1171
|
}
|
|
1093
1172
|
break;
|
|
1094
1173
|
}
|
|
1095
1174
|
}
|
|
1096
|
-
const transform = new modernPath2d.Matrix3()
|
|
1175
|
+
const transform = new modernPath2d.Matrix3();
|
|
1176
|
+
transform.translate(-imageBox.x, -imageBox.y);
|
|
1177
|
+
transform.scale(targetBox.width / imageBox.width, targetBox.height / imageBox.height);
|
|
1097
1178
|
if (isVertical) {
|
|
1179
|
+
const tx = targetBox.width / 2;
|
|
1180
|
+
const ty = targetBox.height / 2;
|
|
1181
|
+
if (!hasReferImage) {
|
|
1182
|
+
transform.translate(-tx, -ty);
|
|
1183
|
+
}
|
|
1098
1184
|
transform.rotate(-Math.PI / 2);
|
|
1185
|
+
if (!hasReferImage) {
|
|
1186
|
+
transform.translate(ty, tx);
|
|
1187
|
+
}
|
|
1099
1188
|
}
|
|
1100
|
-
transform.translate(
|
|
1189
|
+
transform.translate(targetBox.x, targetBox.y);
|
|
1101
1190
|
for (let i2 = 0; i2 < Math.ceil(rawWidth / userWidth); i2++) {
|
|
1102
1191
|
const _transform = transform.clone();
|
|
1103
1192
|
if (isVertical) {
|
|
1104
|
-
_transform.translate(0, i2 *
|
|
1193
|
+
_transform.translate(0, i2 * targetBox.width);
|
|
1105
1194
|
} else {
|
|
1106
|
-
_transform.translate(i2 *
|
|
1195
|
+
_transform.translate(i2 * targetBox.width, 0);
|
|
1107
1196
|
}
|
|
1108
|
-
|
|
1197
|
+
imagePathSet.paths.forEach((originalPath) => {
|
|
1109
1198
|
const path = originalPath.clone().applyTransform(_transform);
|
|
1110
|
-
if (path.style.strokeWidth)
|
|
1199
|
+
if (path.style.strokeWidth)
|
|
1111
1200
|
path.style.strokeWidth *= styleScale * _thickness;
|
|
1112
|
-
|
|
1113
|
-
if (path.style.strokeMiterlimit) {
|
|
1201
|
+
if (path.style.strokeMiterlimit)
|
|
1114
1202
|
path.style.strokeMiterlimit *= styleScale;
|
|
1115
|
-
|
|
1116
|
-
if (path.style.strokeDashoffset) {
|
|
1203
|
+
if (path.style.strokeDashoffset)
|
|
1117
1204
|
path.style.strokeDashoffset *= styleScale;
|
|
1118
|
-
|
|
1119
|
-
if (path.style.strokeDasharray) {
|
|
1205
|
+
if (path.style.strokeDasharray)
|
|
1120
1206
|
path.style.strokeDasharray = path.style.strokeDasharray.map((v) => v * styleScale);
|
|
1121
|
-
}
|
|
1122
1207
|
if (path.style.fill && path.style.fill in _colormap) {
|
|
1123
1208
|
path.style.fill = _colormap[path.style.fill];
|
|
1124
1209
|
}
|
|
1125
1210
|
if (path.style.stroke && path.style.stroke in _colormap) {
|
|
1126
1211
|
path.style.stroke = _colormap[path.style.stroke];
|
|
1127
1212
|
}
|
|
1128
|
-
paths.push(path);
|
|
1213
|
+
pathSet.paths.push(path);
|
|
1129
1214
|
if (rawWidth !== userWidth) {
|
|
1130
1215
|
if (isVertical) {
|
|
1131
|
-
clipRects[paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1216
|
+
clipRects[pathSet.paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1132
1217
|
groupBox.left - groupBox.width * 2,
|
|
1133
1218
|
groupBox.top,
|
|
1134
1219
|
groupBox.width * 4,
|
|
1135
1220
|
groupBox.height
|
|
1136
1221
|
);
|
|
1137
1222
|
} else {
|
|
1138
|
-
clipRects[paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1223
|
+
clipRects[pathSet.paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1139
1224
|
groupBox.left,
|
|
1140
1225
|
groupBox.top - groupBox.height * 2,
|
|
1141
1226
|
groupBox.width,
|
|
@@ -1148,8 +1233,24 @@ function highlight() {
|
|
|
1148
1233
|
}
|
|
1149
1234
|
},
|
|
1150
1235
|
renderOrder: -1,
|
|
1236
|
+
getBoundingBox: () => {
|
|
1237
|
+
const boundingBoxs = [];
|
|
1238
|
+
pathSet.paths.forEach((path, index) => {
|
|
1239
|
+
const clipRect = clipRects[index];
|
|
1240
|
+
let box = path.getBoundingBox();
|
|
1241
|
+
if (clipRect) {
|
|
1242
|
+
const x = Math.max(box.x, clipRect.x);
|
|
1243
|
+
const y = Math.max(box.y, clipRect.y);
|
|
1244
|
+
const right = Math.min(box.right, clipRect.right);
|
|
1245
|
+
const bottom = Math.min(box.bottom, clipRect.bottom);
|
|
1246
|
+
box = new modernPath2d.BoundingBox(x, y, right - x, bottom - y);
|
|
1247
|
+
}
|
|
1248
|
+
boundingBoxs.push(box);
|
|
1249
|
+
});
|
|
1250
|
+
return modernPath2d.BoundingBox.from(...boundingBoxs);
|
|
1251
|
+
},
|
|
1151
1252
|
render: (ctx, text) => {
|
|
1152
|
-
paths.forEach((path, index) => {
|
|
1253
|
+
pathSet.paths.forEach((path, index) => {
|
|
1153
1254
|
drawPath({
|
|
1154
1255
|
ctx,
|
|
1155
1256
|
path,
|
|
@@ -1173,12 +1274,12 @@ function genDisc(r, color) {
|
|
|
1173
1274
|
</svg>`;
|
|
1174
1275
|
}
|
|
1175
1276
|
function listStyle() {
|
|
1176
|
-
const
|
|
1277
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
1177
1278
|
return definePlugin({
|
|
1178
1279
|
name: "listStyle",
|
|
1179
|
-
|
|
1280
|
+
pathSet,
|
|
1180
1281
|
update: (text) => {
|
|
1181
|
-
paths.length = 0;
|
|
1282
|
+
pathSet.paths.length = 0;
|
|
1182
1283
|
const { paragraphs, isVertical, fontSize } = text;
|
|
1183
1284
|
const padding = fontSize * 0.45;
|
|
1184
1285
|
paragraphs.forEach((paragraph) => {
|
|
@@ -1232,7 +1333,7 @@ function listStyle() {
|
|
|
1232
1333
|
inlineBox.top + (inlineBox.height - imageBox.height * _scale) / 2
|
|
1233
1334
|
);
|
|
1234
1335
|
}
|
|
1235
|
-
paths.push(...imagePathSet.paths.map((p) => {
|
|
1336
|
+
pathSet.paths.push(...imagePathSet.paths.map((p) => {
|
|
1236
1337
|
const path = p.clone();
|
|
1237
1338
|
path.applyTransform(m);
|
|
1238
1339
|
if (path.style.fill && path.style.fill in colormap) {
|
|
@@ -1295,39 +1396,21 @@ function render() {
|
|
|
1295
1396
|
return boxes.length ? modernPath2d.BoundingBox.from(...boxes) : void 0;
|
|
1296
1397
|
},
|
|
1297
1398
|
render: (ctx, text) => {
|
|
1298
|
-
const { paragraphs, glyphBox, effects
|
|
1299
|
-
function fillBackground(color, box) {
|
|
1300
|
-
ctx.fillStyle = color;
|
|
1301
|
-
ctx.fillRect(box.left, box.top, box.width, box.height);
|
|
1302
|
-
}
|
|
1303
|
-
if (style?.backgroundColor) {
|
|
1304
|
-
fillBackground(style.backgroundColor, new modernPath2d.BoundingBox(0, 0, ctx.canvas.width, ctx.canvas.height));
|
|
1305
|
-
}
|
|
1306
|
-
paragraphs.forEach((paragraph) => {
|
|
1307
|
-
if (paragraph.style?.backgroundColor) {
|
|
1308
|
-
fillBackground(paragraph.style.backgroundColor, paragraph.lineBox);
|
|
1309
|
-
}
|
|
1310
|
-
});
|
|
1399
|
+
const { paragraphs, glyphBox, effects } = text;
|
|
1311
1400
|
if (effects) {
|
|
1312
|
-
effects.forEach((
|
|
1313
|
-
uploadColor(
|
|
1401
|
+
effects.forEach((style) => {
|
|
1402
|
+
uploadColor(style, glyphBox, ctx);
|
|
1314
1403
|
ctx.save();
|
|
1315
|
-
const [a, c, e, b, d, f] = getTransform2D(text,
|
|
1404
|
+
const [a, c, e, b, d, f] = getTransform2D(text, style).transpose().elements;
|
|
1316
1405
|
ctx.transform(a, b, c, d, e, f);
|
|
1317
1406
|
text.forEachCharacter((character) => {
|
|
1318
|
-
|
|
1319
|
-
fillBackground(character.parent.style.backgroundColor, character.inlineBox);
|
|
1320
|
-
}
|
|
1321
|
-
character.drawTo(ctx, style2);
|
|
1407
|
+
character.drawTo(ctx, style);
|
|
1322
1408
|
});
|
|
1323
1409
|
ctx.restore();
|
|
1324
1410
|
});
|
|
1325
1411
|
} else {
|
|
1326
1412
|
paragraphs.forEach((paragraph) => {
|
|
1327
1413
|
paragraph.fragments.forEach((fragment) => {
|
|
1328
|
-
if (fragment.style?.backgroundColor) {
|
|
1329
|
-
fillBackground(fragment.computedStyle.backgroundColor, fragment.inlineBox);
|
|
1330
|
-
}
|
|
1331
1414
|
fragment.characters.forEach((character) => {
|
|
1332
1415
|
character.drawTo(ctx);
|
|
1333
1416
|
});
|
|
@@ -1365,12 +1448,12 @@ function getTransform2D(text, style) {
|
|
|
1365
1448
|
}
|
|
1366
1449
|
|
|
1367
1450
|
function textDecoration() {
|
|
1368
|
-
const
|
|
1451
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
1369
1452
|
return definePlugin({
|
|
1370
1453
|
name: "textDecoration",
|
|
1371
|
-
|
|
1454
|
+
pathSet,
|
|
1372
1455
|
update: (text) => {
|
|
1373
|
-
paths.length = 0;
|
|
1456
|
+
pathSet.paths.length = 0;
|
|
1374
1457
|
const groups = [];
|
|
1375
1458
|
let group;
|
|
1376
1459
|
let prevStyle;
|
|
@@ -1430,7 +1513,8 @@ function textDecoration() {
|
|
|
1430
1513
|
color,
|
|
1431
1514
|
textDecoration: textDecoration2
|
|
1432
1515
|
} = style;
|
|
1433
|
-
const
|
|
1516
|
+
const inlineBox = modernPath2d.BoundingBox.from(...group2.map((c) => c.inlineBox));
|
|
1517
|
+
const { left, top, width, height } = inlineBox;
|
|
1434
1518
|
let position = isVertical ? left + width : top;
|
|
1435
1519
|
const direction = isVertical ? -1 : 1;
|
|
1436
1520
|
let thickness = 0;
|
|
@@ -1470,7 +1554,7 @@ function textDecoration() {
|
|
|
1470
1554
|
fill: color
|
|
1471
1555
|
});
|
|
1472
1556
|
}
|
|
1473
|
-
paths.push(path);
|
|
1557
|
+
pathSet.paths.push(path);
|
|
1474
1558
|
});
|
|
1475
1559
|
},
|
|
1476
1560
|
render: (ctx, text) => {
|
|
@@ -1480,7 +1564,7 @@ function textDecoration() {
|
|
|
1480
1564
|
ctx.save();
|
|
1481
1565
|
const [a, c, e, b, d, f] = getTransform2D(text, effectStyle).transpose().elements;
|
|
1482
1566
|
ctx.transform(a, b, c, d, e, f);
|
|
1483
|
-
paths.forEach((path) => {
|
|
1567
|
+
pathSet.paths.forEach((path) => {
|
|
1484
1568
|
drawPath({
|
|
1485
1569
|
ctx,
|
|
1486
1570
|
path,
|
|
@@ -1491,7 +1575,7 @@ function textDecoration() {
|
|
|
1491
1575
|
ctx.restore();
|
|
1492
1576
|
});
|
|
1493
1577
|
} else {
|
|
1494
|
-
paths.forEach((path) => {
|
|
1578
|
+
pathSet.paths.forEach((path) => {
|
|
1495
1579
|
drawPath({
|
|
1496
1580
|
ctx,
|
|
1497
1581
|
path,
|
|
@@ -1661,22 +1745,16 @@ class Text extends EventEmitter {
|
|
|
1661
1745
|
this.pathBox = modernPath2d.BoundingBox.from(
|
|
1662
1746
|
this.glyphBox,
|
|
1663
1747
|
...Array.from(this.plugins.values()).map((plugin) => {
|
|
1664
|
-
return plugin.getBoundingBox ? plugin.getBoundingBox(this) :
|
|
1748
|
+
return plugin.getBoundingBox ? plugin.getBoundingBox(this) : plugin.pathSet?.getBoundingBox();
|
|
1665
1749
|
}).filter(Boolean)
|
|
1666
1750
|
);
|
|
1667
1751
|
return this;
|
|
1668
1752
|
}
|
|
1669
1753
|
updateBoundingBox() {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
const bottom = Math.max(pathBox.bottom, pathBox.bottom + lineBox.bottom - rawGlyphBox.bottom);
|
|
1675
|
-
this.boundingBox = new modernPath2d.BoundingBox(
|
|
1676
|
-
left,
|
|
1677
|
-
top,
|
|
1678
|
-
right - left,
|
|
1679
|
-
bottom - top
|
|
1754
|
+
this.boundingBox = modernPath2d.BoundingBox.from(
|
|
1755
|
+
this.rawGlyphBox,
|
|
1756
|
+
this.lineBox,
|
|
1757
|
+
this.pathBox
|
|
1680
1758
|
);
|
|
1681
1759
|
return this;
|
|
1682
1760
|
}
|
|
@@ -1707,9 +1785,9 @@ class Text extends EventEmitter {
|
|
|
1707
1785
|
Array.from(this.plugins.values()).sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0)).forEach((plugin) => {
|
|
1708
1786
|
if (plugin.render) {
|
|
1709
1787
|
plugin.render?.(ctx, this);
|
|
1710
|
-
} else if (plugin.
|
|
1788
|
+
} else if (plugin.pathSet) {
|
|
1711
1789
|
const style = this.computedStyle;
|
|
1712
|
-
plugin.paths.forEach((path) => {
|
|
1790
|
+
plugin.pathSet.paths.forEach((path) => {
|
|
1713
1791
|
drawPath({
|
|
1714
1792
|
ctx,
|
|
1715
1793
|
path,
|
|
@@ -1748,6 +1826,8 @@ exports.Measurer = Measurer;
|
|
|
1748
1826
|
exports.Paragraph = Paragraph;
|
|
1749
1827
|
exports.Text = Text;
|
|
1750
1828
|
exports.background = background;
|
|
1829
|
+
exports.createSVGLoader = createSVGLoader;
|
|
1830
|
+
exports.createSVGParser = createSVGParser;
|
|
1751
1831
|
exports.definePlugin = definePlugin;
|
|
1752
1832
|
exports.drawPath = drawPath;
|
|
1753
1833
|
exports.filterEmpty = filterEmpty;
|
|
@@ -1760,7 +1840,6 @@ exports.isEqualValue = isEqualValue;
|
|
|
1760
1840
|
exports.isNone = isNone;
|
|
1761
1841
|
exports.listStyle = listStyle;
|
|
1762
1842
|
exports.measureText = measureText;
|
|
1763
|
-
exports.needsFetch = needsFetch;
|
|
1764
1843
|
exports.outline = outline;
|
|
1765
1844
|
exports.parseColor = parseColor;
|
|
1766
1845
|
exports.parseColormap = parseColormap;
|