modern-text 1.3.1 → 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 +216 -139
- 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 +216 -140
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -21,29 +21,32 @@
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
|
+
import { fonts } from 'modern-font'
|
|
24
25
|
import { renderText } from 'modern-text'
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
document.
|
|
27
|
+
fonts.loadFallbackFont('/fallback.woff').then(() => {
|
|
28
|
+
const view = document.createElement('canvas')
|
|
29
|
+
document.body.append(view)
|
|
28
30
|
|
|
29
|
-
renderText({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
content: [
|
|
38
|
-
{
|
|
39
|
-
letterSpacing: 3,
|
|
40
|
-
fragments: [
|
|
41
|
-
{ content: 'He', color: 'red', fontSize: 12 },
|
|
42
|
-
{ content: 'llo', color: 'black' },
|
|
43
|
-
],
|
|
31
|
+
renderText({
|
|
32
|
+
view,
|
|
33
|
+
style: {
|
|
34
|
+
width: 100,
|
|
35
|
+
height: 200,
|
|
36
|
+
fontSize: 22,
|
|
37
|
+
textDecoration: 'underline',
|
|
44
38
|
},
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
letterSpacing: 3,
|
|
42
|
+
fragments: [
|
|
43
|
+
{ content: 'He', color: 'red', fontSize: 12 },
|
|
44
|
+
{ content: 'llo', color: 'black' },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
{ content: ', ', color: 'grey' },
|
|
48
|
+
{ content: 'World!', color: 'black' },
|
|
49
|
+
],
|
|
50
|
+
})
|
|
48
51
|
})
|
|
49
52
|
```
|
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) {
|
|
@@ -876,9 +916,64 @@ class EventEmitter {
|
|
|
876
916
|
}
|
|
877
917
|
|
|
878
918
|
function background() {
|
|
919
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
920
|
+
const loader = createSVGLoader();
|
|
921
|
+
const parser = createSVGParser(loader);
|
|
879
922
|
return {
|
|
880
|
-
name: "background"
|
|
881
|
-
|
|
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
|
+
}
|
|
882
977
|
};
|
|
883
978
|
}
|
|
884
979
|
|
|
@@ -902,53 +997,30 @@ function getHighlightStyle(style) {
|
|
|
902
997
|
};
|
|
903
998
|
}
|
|
904
999
|
function highlight() {
|
|
905
|
-
const
|
|
1000
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
906
1001
|
const clipRects = [];
|
|
907
|
-
const
|
|
908
|
-
const
|
|
909
|
-
async function loadSvg(svg) {
|
|
910
|
-
if (!loaded.has(svg)) {
|
|
911
|
-
loaded.set(svg, svg);
|
|
912
|
-
try {
|
|
913
|
-
loaded.set(svg, await fetch(svg).then((rep) => rep.text()));
|
|
914
|
-
} catch (err) {
|
|
915
|
-
console.warn(err);
|
|
916
|
-
loaded.delete(svg);
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
function getPaths(svg) {
|
|
921
|
-
let result = parsed.get(svg);
|
|
922
|
-
if (!result) {
|
|
923
|
-
const dom = modernPath2d.svgToDOM(
|
|
924
|
-
needsFetch(svg) ? loaded.get(svg) ?? svg : svg
|
|
925
|
-
);
|
|
926
|
-
const pathSet = modernPath2d.svgToPath2DSet(dom);
|
|
927
|
-
result = { dom, pathSet };
|
|
928
|
-
parsed.set(svg, result);
|
|
929
|
-
}
|
|
930
|
-
return result;
|
|
931
|
-
}
|
|
1002
|
+
const loader = createSVGLoader();
|
|
1003
|
+
const parser = createSVGParser(loader);
|
|
932
1004
|
return definePlugin({
|
|
933
1005
|
name: "highlight",
|
|
934
|
-
|
|
1006
|
+
pathSet,
|
|
935
1007
|
load: async (text) => {
|
|
936
1008
|
const set = /* @__PURE__ */ new Set();
|
|
937
1009
|
text.forEachCharacter((character) => {
|
|
938
1010
|
const { computedStyle: style } = character;
|
|
939
1011
|
const { image, referImage } = getHighlightStyle(style);
|
|
940
|
-
if (
|
|
1012
|
+
if (loader.needsLoad(image)) {
|
|
941
1013
|
set.add(image);
|
|
942
1014
|
}
|
|
943
|
-
if (
|
|
1015
|
+
if (loader.needsLoad(referImage)) {
|
|
944
1016
|
set.add(referImage);
|
|
945
1017
|
}
|
|
946
1018
|
});
|
|
947
|
-
await Promise.all(Array.from(set).map((src) =>
|
|
1019
|
+
await Promise.all(Array.from(set).map((src) => loader.load(src)));
|
|
948
1020
|
},
|
|
949
1021
|
update: (text) => {
|
|
950
1022
|
clipRects.length = 0;
|
|
951
|
-
paths.length = 0;
|
|
1023
|
+
pathSet.paths.length = 0;
|
|
952
1024
|
let groups = [];
|
|
953
1025
|
let group;
|
|
954
1026
|
let prevHighlight;
|
|
@@ -995,11 +1067,15 @@ function highlight() {
|
|
|
995
1067
|
...characters.filter((c) => c.glyphBox).map((c) => c.glyphBox)
|
|
996
1068
|
);
|
|
997
1069
|
const {
|
|
998
|
-
computedStyle: style
|
|
1070
|
+
computedStyle: style,
|
|
1071
|
+
isVertical,
|
|
1072
|
+
inlineBox,
|
|
1073
|
+
glyphBox = inlineBox,
|
|
1074
|
+
strikeoutPosition,
|
|
1075
|
+
underlinePosition
|
|
999
1076
|
} = char;
|
|
1000
1077
|
const {
|
|
1001
|
-
fontSize
|
|
1002
|
-
writingMode
|
|
1078
|
+
fontSize
|
|
1003
1079
|
} = style;
|
|
1004
1080
|
const {
|
|
1005
1081
|
image,
|
|
@@ -1009,39 +1085,40 @@ function highlight() {
|
|
|
1009
1085
|
size,
|
|
1010
1086
|
thickness
|
|
1011
1087
|
} = getHighlightStyle(style);
|
|
1012
|
-
const isVertical = writingMode.includes("vertical");
|
|
1013
1088
|
const _thickness = parseValueNumber(thickness, { fontSize, total: groupBox.width }) / groupBox.width;
|
|
1014
1089
|
const _colormap = parseColormap(colormap);
|
|
1015
|
-
const { pathSet:
|
|
1016
|
-
const
|
|
1017
|
-
const styleScale = fontSize /
|
|
1018
|
-
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);
|
|
1019
1094
|
if (isVertical) {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1095
|
+
targetBox.width = groupBox.height;
|
|
1096
|
+
targetBox.height = groupBox.width;
|
|
1097
|
+
targetBox.left = groupBox.left + groupBox.width;
|
|
1023
1098
|
}
|
|
1024
|
-
const rawWidth = Math.floor(
|
|
1099
|
+
const rawWidth = Math.floor(targetBox.width);
|
|
1025
1100
|
let userWidth = rawWidth;
|
|
1026
1101
|
if (size !== "cover") {
|
|
1027
1102
|
userWidth = parseValueNumber(size, { fontSize, total: groupBox.width }) || rawWidth;
|
|
1028
|
-
|
|
1103
|
+
targetBox.width = userWidth;
|
|
1029
1104
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1105
|
+
const hasReferImage = !isNone(referImage) && isNone(line);
|
|
1106
|
+
if (hasReferImage) {
|
|
1107
|
+
imageBox.copy(
|
|
1108
|
+
parser.parse(referImage).pathSet.getBoundingBox(true)
|
|
1109
|
+
);
|
|
1033
1110
|
} else {
|
|
1034
1111
|
let _line;
|
|
1035
1112
|
if (isNone(line)) {
|
|
1036
|
-
if (
|
|
1113
|
+
if (imageBox.width / imageBox.height > 4) {
|
|
1037
1114
|
_line = "underline";
|
|
1038
|
-
const viewBox =
|
|
1115
|
+
const viewBox = imageDom.getAttribute("viewBox");
|
|
1039
1116
|
if (viewBox) {
|
|
1040
1117
|
const [_x, y, _w, h] = viewBox.split(" ").map((v) => Number(v));
|
|
1041
1118
|
const viewCenter = y + h / 2;
|
|
1042
|
-
if (
|
|
1119
|
+
if (imageBox.y < viewCenter && imageBox.y + imageBox.height > viewCenter) {
|
|
1043
1120
|
_line = "line-through";
|
|
1044
|
-
} else if (
|
|
1121
|
+
} else if (imageBox.y + imageBox.height < viewCenter) {
|
|
1045
1122
|
_line = "overline";
|
|
1046
1123
|
} else {
|
|
1047
1124
|
_line = "underline";
|
|
@@ -1055,89 +1132,95 @@ function highlight() {
|
|
|
1055
1132
|
}
|
|
1056
1133
|
switch (_line) {
|
|
1057
1134
|
case "outline": {
|
|
1058
|
-
const paddingX =
|
|
1059
|
-
const paddingY =
|
|
1060
|
-
cBox.width += paddingX;
|
|
1061
|
-
cBox.height += paddingY;
|
|
1135
|
+
const paddingX = targetBox.width * 0.2;
|
|
1136
|
+
const paddingY = targetBox.height * 0.2;
|
|
1062
1137
|
if (isVertical) {
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1138
|
+
targetBox.x -= paddingY / 2;
|
|
1139
|
+
targetBox.y -= paddingX / 2;
|
|
1140
|
+
targetBox.x -= targetBox.height;
|
|
1066
1141
|
} else {
|
|
1067
|
-
|
|
1068
|
-
|
|
1142
|
+
targetBox.x -= paddingX / 2;
|
|
1143
|
+
targetBox.y -= paddingY / 2;
|
|
1069
1144
|
}
|
|
1145
|
+
targetBox.width += paddingX;
|
|
1146
|
+
targetBox.height += paddingY;
|
|
1070
1147
|
break;
|
|
1071
1148
|
}
|
|
1072
1149
|
case "overline":
|
|
1073
|
-
|
|
1150
|
+
targetBox.height = imageBox.height * styleScale;
|
|
1074
1151
|
if (isVertical) {
|
|
1075
|
-
|
|
1152
|
+
targetBox.x = inlineBox.left + inlineBox.width;
|
|
1076
1153
|
} else {
|
|
1077
|
-
|
|
1154
|
+
targetBox.y = inlineBox.top;
|
|
1078
1155
|
}
|
|
1079
1156
|
break;
|
|
1080
1157
|
case "line-through":
|
|
1081
|
-
|
|
1158
|
+
targetBox.height = imageBox.height * styleScale;
|
|
1082
1159
|
if (isVertical) {
|
|
1083
|
-
|
|
1160
|
+
targetBox.x = inlineBox.left + inlineBox.width - strikeoutPosition + targetBox.height / 2;
|
|
1084
1161
|
} else {
|
|
1085
|
-
|
|
1162
|
+
targetBox.y = inlineBox.top + strikeoutPosition - targetBox.height / 2;
|
|
1086
1163
|
}
|
|
1087
1164
|
break;
|
|
1088
1165
|
case "underline":
|
|
1089
|
-
|
|
1166
|
+
targetBox.height = imageBox.height * styleScale;
|
|
1090
1167
|
if (isVertical) {
|
|
1091
|
-
|
|
1168
|
+
targetBox.x = glyphBox.left + glyphBox.width - underlinePosition;
|
|
1092
1169
|
} else {
|
|
1093
|
-
|
|
1170
|
+
targetBox.y = inlineBox.top + underlinePosition;
|
|
1094
1171
|
}
|
|
1095
1172
|
break;
|
|
1096
1173
|
}
|
|
1097
1174
|
}
|
|
1098
|
-
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);
|
|
1099
1178
|
if (isVertical) {
|
|
1179
|
+
const tx = targetBox.width / 2;
|
|
1180
|
+
const ty = targetBox.height / 2;
|
|
1181
|
+
if (!hasReferImage) {
|
|
1182
|
+
transform.translate(-tx, -ty);
|
|
1183
|
+
}
|
|
1100
1184
|
transform.rotate(-Math.PI / 2);
|
|
1185
|
+
if (!hasReferImage) {
|
|
1186
|
+
transform.translate(ty, tx);
|
|
1187
|
+
}
|
|
1101
1188
|
}
|
|
1102
|
-
transform.translate(
|
|
1189
|
+
transform.translate(targetBox.x, targetBox.y);
|
|
1103
1190
|
for (let i2 = 0; i2 < Math.ceil(rawWidth / userWidth); i2++) {
|
|
1104
1191
|
const _transform = transform.clone();
|
|
1105
1192
|
if (isVertical) {
|
|
1106
|
-
_transform.translate(0, i2 *
|
|
1193
|
+
_transform.translate(0, i2 * targetBox.width);
|
|
1107
1194
|
} else {
|
|
1108
|
-
_transform.translate(i2 *
|
|
1195
|
+
_transform.translate(i2 * targetBox.width, 0);
|
|
1109
1196
|
}
|
|
1110
|
-
|
|
1197
|
+
imagePathSet.paths.forEach((originalPath) => {
|
|
1111
1198
|
const path = originalPath.clone().applyTransform(_transform);
|
|
1112
|
-
if (path.style.strokeWidth)
|
|
1199
|
+
if (path.style.strokeWidth)
|
|
1113
1200
|
path.style.strokeWidth *= styleScale * _thickness;
|
|
1114
|
-
|
|
1115
|
-
if (path.style.strokeMiterlimit) {
|
|
1201
|
+
if (path.style.strokeMiterlimit)
|
|
1116
1202
|
path.style.strokeMiterlimit *= styleScale;
|
|
1117
|
-
|
|
1118
|
-
if (path.style.strokeDashoffset) {
|
|
1203
|
+
if (path.style.strokeDashoffset)
|
|
1119
1204
|
path.style.strokeDashoffset *= styleScale;
|
|
1120
|
-
|
|
1121
|
-
if (path.style.strokeDasharray) {
|
|
1205
|
+
if (path.style.strokeDasharray)
|
|
1122
1206
|
path.style.strokeDasharray = path.style.strokeDasharray.map((v) => v * styleScale);
|
|
1123
|
-
}
|
|
1124
1207
|
if (path.style.fill && path.style.fill in _colormap) {
|
|
1125
1208
|
path.style.fill = _colormap[path.style.fill];
|
|
1126
1209
|
}
|
|
1127
1210
|
if (path.style.stroke && path.style.stroke in _colormap) {
|
|
1128
1211
|
path.style.stroke = _colormap[path.style.stroke];
|
|
1129
1212
|
}
|
|
1130
|
-
paths.push(path);
|
|
1213
|
+
pathSet.paths.push(path);
|
|
1131
1214
|
if (rawWidth !== userWidth) {
|
|
1132
1215
|
if (isVertical) {
|
|
1133
|
-
clipRects[paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1216
|
+
clipRects[pathSet.paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1134
1217
|
groupBox.left - groupBox.width * 2,
|
|
1135
1218
|
groupBox.top,
|
|
1136
1219
|
groupBox.width * 4,
|
|
1137
1220
|
groupBox.height
|
|
1138
1221
|
);
|
|
1139
1222
|
} else {
|
|
1140
|
-
clipRects[paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1223
|
+
clipRects[pathSet.paths.length - 1] = new modernPath2d.BoundingBox(
|
|
1141
1224
|
groupBox.left,
|
|
1142
1225
|
groupBox.top - groupBox.height * 2,
|
|
1143
1226
|
groupBox.width,
|
|
@@ -1150,8 +1233,24 @@ function highlight() {
|
|
|
1150
1233
|
}
|
|
1151
1234
|
},
|
|
1152
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
|
+
},
|
|
1153
1252
|
render: (ctx, text) => {
|
|
1154
|
-
paths.forEach((path, index) => {
|
|
1253
|
+
pathSet.paths.forEach((path, index) => {
|
|
1155
1254
|
drawPath({
|
|
1156
1255
|
ctx,
|
|
1157
1256
|
path,
|
|
@@ -1175,12 +1274,12 @@ function genDisc(r, color) {
|
|
|
1175
1274
|
</svg>`;
|
|
1176
1275
|
}
|
|
1177
1276
|
function listStyle() {
|
|
1178
|
-
const
|
|
1277
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
1179
1278
|
return definePlugin({
|
|
1180
1279
|
name: "listStyle",
|
|
1181
|
-
|
|
1280
|
+
pathSet,
|
|
1182
1281
|
update: (text) => {
|
|
1183
|
-
paths.length = 0;
|
|
1282
|
+
pathSet.paths.length = 0;
|
|
1184
1283
|
const { paragraphs, isVertical, fontSize } = text;
|
|
1185
1284
|
const padding = fontSize * 0.45;
|
|
1186
1285
|
paragraphs.forEach((paragraph) => {
|
|
@@ -1234,7 +1333,7 @@ function listStyle() {
|
|
|
1234
1333
|
inlineBox.top + (inlineBox.height - imageBox.height * _scale) / 2
|
|
1235
1334
|
);
|
|
1236
1335
|
}
|
|
1237
|
-
paths.push(...imagePathSet.paths.map((p) => {
|
|
1336
|
+
pathSet.paths.push(...imagePathSet.paths.map((p) => {
|
|
1238
1337
|
const path = p.clone();
|
|
1239
1338
|
path.applyTransform(m);
|
|
1240
1339
|
if (path.style.fill && path.style.fill in colormap) {
|
|
@@ -1297,39 +1396,21 @@ function render() {
|
|
|
1297
1396
|
return boxes.length ? modernPath2d.BoundingBox.from(...boxes) : void 0;
|
|
1298
1397
|
},
|
|
1299
1398
|
render: (ctx, text) => {
|
|
1300
|
-
const { paragraphs, glyphBox, effects
|
|
1301
|
-
function fillBackground(color, box) {
|
|
1302
|
-
ctx.fillStyle = color;
|
|
1303
|
-
ctx.fillRect(box.left, box.top, box.width, box.height);
|
|
1304
|
-
}
|
|
1305
|
-
if (style?.backgroundColor) {
|
|
1306
|
-
fillBackground(style.backgroundColor, new modernPath2d.BoundingBox(0, 0, ctx.canvas.width, ctx.canvas.height));
|
|
1307
|
-
}
|
|
1308
|
-
paragraphs.forEach((paragraph) => {
|
|
1309
|
-
if (paragraph.style?.backgroundColor) {
|
|
1310
|
-
fillBackground(paragraph.style.backgroundColor, paragraph.lineBox);
|
|
1311
|
-
}
|
|
1312
|
-
});
|
|
1399
|
+
const { paragraphs, glyphBox, effects } = text;
|
|
1313
1400
|
if (effects) {
|
|
1314
|
-
effects.forEach((
|
|
1315
|
-
uploadColor(
|
|
1401
|
+
effects.forEach((style) => {
|
|
1402
|
+
uploadColor(style, glyphBox, ctx);
|
|
1316
1403
|
ctx.save();
|
|
1317
|
-
const [a, c, e, b, d, f] = getTransform2D(text,
|
|
1404
|
+
const [a, c, e, b, d, f] = getTransform2D(text, style).transpose().elements;
|
|
1318
1405
|
ctx.transform(a, b, c, d, e, f);
|
|
1319
1406
|
text.forEachCharacter((character) => {
|
|
1320
|
-
|
|
1321
|
-
fillBackground(character.parent.style.backgroundColor, character.inlineBox);
|
|
1322
|
-
}
|
|
1323
|
-
character.drawTo(ctx, style2);
|
|
1407
|
+
character.drawTo(ctx, style);
|
|
1324
1408
|
});
|
|
1325
1409
|
ctx.restore();
|
|
1326
1410
|
});
|
|
1327
1411
|
} else {
|
|
1328
1412
|
paragraphs.forEach((paragraph) => {
|
|
1329
1413
|
paragraph.fragments.forEach((fragment) => {
|
|
1330
|
-
if (fragment.style?.backgroundColor) {
|
|
1331
|
-
fillBackground(fragment.computedStyle.backgroundColor, fragment.inlineBox);
|
|
1332
|
-
}
|
|
1333
1414
|
fragment.characters.forEach((character) => {
|
|
1334
1415
|
character.drawTo(ctx);
|
|
1335
1416
|
});
|
|
@@ -1367,12 +1448,12 @@ function getTransform2D(text, style) {
|
|
|
1367
1448
|
}
|
|
1368
1449
|
|
|
1369
1450
|
function textDecoration() {
|
|
1370
|
-
const
|
|
1451
|
+
const pathSet = new modernPath2d.Path2DSet();
|
|
1371
1452
|
return definePlugin({
|
|
1372
1453
|
name: "textDecoration",
|
|
1373
|
-
|
|
1454
|
+
pathSet,
|
|
1374
1455
|
update: (text) => {
|
|
1375
|
-
paths.length = 0;
|
|
1456
|
+
pathSet.paths.length = 0;
|
|
1376
1457
|
const groups = [];
|
|
1377
1458
|
let group;
|
|
1378
1459
|
let prevStyle;
|
|
@@ -1432,7 +1513,8 @@ function textDecoration() {
|
|
|
1432
1513
|
color,
|
|
1433
1514
|
textDecoration: textDecoration2
|
|
1434
1515
|
} = style;
|
|
1435
|
-
const
|
|
1516
|
+
const inlineBox = modernPath2d.BoundingBox.from(...group2.map((c) => c.inlineBox));
|
|
1517
|
+
const { left, top, width, height } = inlineBox;
|
|
1436
1518
|
let position = isVertical ? left + width : top;
|
|
1437
1519
|
const direction = isVertical ? -1 : 1;
|
|
1438
1520
|
let thickness = 0;
|
|
@@ -1472,7 +1554,7 @@ function textDecoration() {
|
|
|
1472
1554
|
fill: color
|
|
1473
1555
|
});
|
|
1474
1556
|
}
|
|
1475
|
-
paths.push(path);
|
|
1557
|
+
pathSet.paths.push(path);
|
|
1476
1558
|
});
|
|
1477
1559
|
},
|
|
1478
1560
|
render: (ctx, text) => {
|
|
@@ -1482,7 +1564,7 @@ function textDecoration() {
|
|
|
1482
1564
|
ctx.save();
|
|
1483
1565
|
const [a, c, e, b, d, f] = getTransform2D(text, effectStyle).transpose().elements;
|
|
1484
1566
|
ctx.transform(a, b, c, d, e, f);
|
|
1485
|
-
paths.forEach((path) => {
|
|
1567
|
+
pathSet.paths.forEach((path) => {
|
|
1486
1568
|
drawPath({
|
|
1487
1569
|
ctx,
|
|
1488
1570
|
path,
|
|
@@ -1493,7 +1575,7 @@ function textDecoration() {
|
|
|
1493
1575
|
ctx.restore();
|
|
1494
1576
|
});
|
|
1495
1577
|
} else {
|
|
1496
|
-
paths.forEach((path) => {
|
|
1578
|
+
pathSet.paths.forEach((path) => {
|
|
1497
1579
|
drawPath({
|
|
1498
1580
|
ctx,
|
|
1499
1581
|
path,
|
|
@@ -1663,22 +1745,16 @@ class Text extends EventEmitter {
|
|
|
1663
1745
|
this.pathBox = modernPath2d.BoundingBox.from(
|
|
1664
1746
|
this.glyphBox,
|
|
1665
1747
|
...Array.from(this.plugins.values()).map((plugin) => {
|
|
1666
|
-
return plugin.getBoundingBox ? plugin.getBoundingBox(this) :
|
|
1748
|
+
return plugin.getBoundingBox ? plugin.getBoundingBox(this) : plugin.pathSet?.getBoundingBox();
|
|
1667
1749
|
}).filter(Boolean)
|
|
1668
1750
|
);
|
|
1669
1751
|
return this;
|
|
1670
1752
|
}
|
|
1671
1753
|
updateBoundingBox() {
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
const bottom = Math.max(pathBox.bottom, pathBox.bottom + lineBox.bottom - rawGlyphBox.bottom);
|
|
1677
|
-
this.boundingBox = new modernPath2d.BoundingBox(
|
|
1678
|
-
left,
|
|
1679
|
-
top,
|
|
1680
|
-
right - left,
|
|
1681
|
-
bottom - top
|
|
1754
|
+
this.boundingBox = modernPath2d.BoundingBox.from(
|
|
1755
|
+
this.rawGlyphBox,
|
|
1756
|
+
this.lineBox,
|
|
1757
|
+
this.pathBox
|
|
1682
1758
|
);
|
|
1683
1759
|
return this;
|
|
1684
1760
|
}
|
|
@@ -1709,9 +1785,9 @@ class Text extends EventEmitter {
|
|
|
1709
1785
|
Array.from(this.plugins.values()).sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0)).forEach((plugin) => {
|
|
1710
1786
|
if (plugin.render) {
|
|
1711
1787
|
plugin.render?.(ctx, this);
|
|
1712
|
-
} else if (plugin.
|
|
1788
|
+
} else if (plugin.pathSet) {
|
|
1713
1789
|
const style = this.computedStyle;
|
|
1714
|
-
plugin.paths.forEach((path) => {
|
|
1790
|
+
plugin.pathSet.paths.forEach((path) => {
|
|
1715
1791
|
drawPath({
|
|
1716
1792
|
ctx,
|
|
1717
1793
|
path,
|
|
@@ -1750,6 +1826,8 @@ exports.Measurer = Measurer;
|
|
|
1750
1826
|
exports.Paragraph = Paragraph;
|
|
1751
1827
|
exports.Text = Text;
|
|
1752
1828
|
exports.background = background;
|
|
1829
|
+
exports.createSVGLoader = createSVGLoader;
|
|
1830
|
+
exports.createSVGParser = createSVGParser;
|
|
1753
1831
|
exports.definePlugin = definePlugin;
|
|
1754
1832
|
exports.drawPath = drawPath;
|
|
1755
1833
|
exports.filterEmpty = filterEmpty;
|
|
@@ -1762,7 +1840,6 @@ exports.isEqualValue = isEqualValue;
|
|
|
1762
1840
|
exports.isNone = isNone;
|
|
1763
1841
|
exports.listStyle = listStyle;
|
|
1764
1842
|
exports.measureText = measureText;
|
|
1765
|
-
exports.needsFetch = needsFetch;
|
|
1766
1843
|
exports.outline = outline;
|
|
1767
1844
|
exports.parseColor = parseColor;
|
|
1768
1845
|
exports.parseColormap = parseColormap;
|