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/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 = "top";
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
- boxSizing: "border-box",
594
- display: isFlex ? "inline-flex" : void 0,
595
- width: "max-content",
596
- height: "max-content",
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
- // TODO
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 paths = [];
1000
+ const pathSet = new modernPath2d.Path2DSet();
904
1001
  const clipRects = [];
905
- const loaded = /* @__PURE__ */ new Map();
906
- const parsed = /* @__PURE__ */ new Map();
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
- paths,
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 (needsFetch(image)) {
1012
+ if (loader.needsLoad(image)) {
939
1013
  set.add(image);
940
1014
  }
941
- if (needsFetch(referImage)) {
1015
+ if (loader.needsLoad(referImage)) {
942
1016
  set.add(referImage);
943
1017
  }
944
1018
  });
945
- await Promise.all(Array.from(set).map((src) => loadSvg(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: svgPathSet, dom: svgDom } = getPaths(image);
1014
- const aBox = svgPathSet.getBoundingBox(true);
1015
- const styleScale = fontSize / aBox.width * 2;
1016
- const cBox = new modernPath2d.BoundingBox().copy(groupBox);
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
- cBox.width = groupBox.height;
1019
- cBox.height = groupBox.width;
1020
- cBox.left = groupBox.left + groupBox.width;
1095
+ targetBox.width = groupBox.height;
1096
+ targetBox.height = groupBox.width;
1097
+ targetBox.left = groupBox.left + groupBox.width;
1021
1098
  }
1022
- const rawWidth = Math.floor(cBox.width);
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
- cBox.width = userWidth;
1103
+ targetBox.width = userWidth;
1027
1104
  }
1028
- if (!isNone(referImage) && isNone(line)) {
1029
- const bBox = getPaths(referImage).pathSet.getBoundingBox(true);
1030
- aBox.copy(bBox);
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 (aBox.width / aBox.height > 4) {
1113
+ if (imageBox.width / imageBox.height > 4) {
1035
1114
  _line = "underline";
1036
- const viewBox = svgDom.getAttribute("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 (aBox.y < viewCenter && aBox.y + aBox.height > viewCenter) {
1119
+ if (imageBox.y < viewCenter && imageBox.y + imageBox.height > viewCenter) {
1041
1120
  _line = "line-through";
1042
- } else if (aBox.y + aBox.height < viewCenter) {
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 = cBox.width * 0.2;
1057
- const paddingY = cBox.height * 0.2;
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
- cBox.x -= paddingY / 2;
1062
- cBox.y -= paddingX / 2;
1063
- cBox.x += cBox.height;
1138
+ targetBox.x -= paddingY / 2;
1139
+ targetBox.y -= paddingX / 2;
1140
+ targetBox.x -= targetBox.height;
1064
1141
  } else {
1065
- cBox.x -= paddingX / 2;
1066
- cBox.y -= paddingY / 2;
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
- cBox.height = aBox.height * styleScale;
1150
+ targetBox.height = imageBox.height * styleScale;
1072
1151
  if (isVertical) {
1073
- cBox.x = char.inlineBox.left + char.inlineBox.width;
1152
+ targetBox.x = inlineBox.left + inlineBox.width;
1074
1153
  } else {
1075
- cBox.y = char.inlineBox.top;
1154
+ targetBox.y = inlineBox.top;
1076
1155
  }
1077
1156
  break;
1078
1157
  case "line-through":
1079
- cBox.height = aBox.height * styleScale;
1158
+ targetBox.height = imageBox.height * styleScale;
1080
1159
  if (isVertical) {
1081
- cBox.x = char.inlineBox.left + char.inlineBox.width - char.strikeoutPosition + cBox.height / 2;
1160
+ targetBox.x = inlineBox.left + inlineBox.width - strikeoutPosition + targetBox.height / 2;
1082
1161
  } else {
1083
- cBox.y = char.inlineBox.top + char.strikeoutPosition - cBox.height / 2;
1162
+ targetBox.y = inlineBox.top + strikeoutPosition - targetBox.height / 2;
1084
1163
  }
1085
1164
  break;
1086
1165
  case "underline":
1087
- cBox.height = aBox.height * styleScale;
1166
+ targetBox.height = imageBox.height * styleScale;
1088
1167
  if (isVertical) {
1089
- cBox.x = char.inlineBox.left + char.inlineBox.width - char.underlinePosition;
1168
+ targetBox.x = glyphBox.left + glyphBox.width - underlinePosition;
1090
1169
  } else {
1091
- cBox.y = char.inlineBox.top + char.underlinePosition;
1170
+ targetBox.y = inlineBox.top + underlinePosition;
1092
1171
  }
1093
1172
  break;
1094
1173
  }
1095
1174
  }
1096
- const transform = new modernPath2d.Matrix3().translate(-aBox.x, -aBox.y).scale(cBox.width / aBox.width, cBox.height / aBox.height);
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(cBox.x, cBox.y);
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 * cBox.width);
1193
+ _transform.translate(0, i2 * targetBox.width);
1105
1194
  } else {
1106
- _transform.translate(i2 * cBox.width, 0);
1195
+ _transform.translate(i2 * targetBox.width, 0);
1107
1196
  }
1108
- svgPathSet.paths.forEach((originalPath) => {
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 paths = [];
1277
+ const pathSet = new modernPath2d.Path2DSet();
1177
1278
  return definePlugin({
1178
1279
  name: "listStyle",
1179
- paths,
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, style } = text;
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((style2) => {
1313
- uploadColor(style2, glyphBox, ctx);
1401
+ effects.forEach((style) => {
1402
+ uploadColor(style, glyphBox, ctx);
1314
1403
  ctx.save();
1315
- const [a, c, e, b, d, f] = getTransform2D(text, style2).transpose().elements;
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
- if (character.parent.style?.backgroundColor) {
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 paths = [];
1451
+ const pathSet = new modernPath2d.Path2DSet();
1369
1452
  return definePlugin({
1370
1453
  name: "textDecoration",
1371
- paths,
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 { left, top, width, height } = modernPath2d.BoundingBox.from(...group2.map((c) => c.inlineBox));
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) : new modernPath2d.Path2DSet(plugin.paths ?? []).getBoundingBox();
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
- const { lineBox, rawGlyphBox, pathBox } = this;
1671
- const left = Math.min(pathBox.left, pathBox.left + lineBox.left - rawGlyphBox.left);
1672
- const top = Math.min(pathBox.top, pathBox.top + lineBox.top - rawGlyphBox.top);
1673
- const right = Math.max(pathBox.right, pathBox.right + lineBox.right - rawGlyphBox.right);
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.paths) {
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;