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/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Path2D, BoundingBox, setCanvasContext, Path2DSet, Matrix3, svgToDOM, svgToPath2DSet, Vector2 } from 'modern-path2d';
1
+ import { Path2D, BoundingBox, setCanvasContext, svgToDOM, svgToPath2DSet, Path2DSet, Matrix3, Vector2 } from 'modern-path2d';
2
2
  import { getDefaultStyle } from 'modern-idoc';
3
3
  import { fonts } from 'modern-font';
4
4
 
@@ -375,6 +375,49 @@ class Character {
375
375
  }
376
376
  }
377
377
 
378
+ function createSVGLoader() {
379
+ const loaded = /* @__PURE__ */ new Map();
380
+ async function load(svg) {
381
+ if (!loaded.has(svg)) {
382
+ loaded.set(svg, svg);
383
+ try {
384
+ loaded.set(svg, await fetch(svg).then((rep) => rep.text()));
385
+ } catch (err) {
386
+ console.warn(err);
387
+ loaded.delete(svg);
388
+ }
389
+ }
390
+ }
391
+ function needsLoad(source) {
392
+ return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("blob://");
393
+ }
394
+ return {
395
+ loaded,
396
+ needsLoad,
397
+ load
398
+ };
399
+ }
400
+
401
+ function createSVGParser(loader) {
402
+ const parsed = /* @__PURE__ */ new Map();
403
+ function parse(svg) {
404
+ let result = parsed.get(svg);
405
+ if (!result) {
406
+ const dom = svgToDOM(
407
+ loader.needsLoad(svg) ? loader.loaded.get(svg) ?? svg : svg
408
+ );
409
+ const pathSet = svgToPath2DSet(dom);
410
+ result = { dom, pathSet };
411
+ parsed.set(svg, result);
412
+ }
413
+ return result;
414
+ }
415
+ return {
416
+ parsed,
417
+ parse
418
+ };
419
+ }
420
+
378
421
  function parseValueNumber(value, ctx) {
379
422
  if (typeof value === "number") {
380
423
  return value;
@@ -451,9 +494,6 @@ function filterEmpty(val) {
451
494
  }
452
495
  return res;
453
496
  }
454
- function needsFetch(source) {
455
- return source.startsWith("http://") || source.startsWith("https://") || source.startsWith("blob://");
456
- }
457
497
 
458
498
  class Fragment {
459
499
  constructor(content, style = {}, parent) {
@@ -874,9 +914,64 @@ class EventEmitter {
874
914
  }
875
915
 
876
916
  function background() {
917
+ const pathSet = new Path2DSet();
918
+ const loader = createSVGLoader();
919
+ const parser = createSVGParser(loader);
877
920
  return {
878
- name: "background"
879
- // TODO
921
+ name: "background",
922
+ pathSet,
923
+ load: async (text) => {
924
+ const { backgroundImage } = text.style;
925
+ if (backgroundImage && loader.needsLoad(backgroundImage)) {
926
+ await loader.load(backgroundImage);
927
+ }
928
+ },
929
+ update: (text) => {
930
+ pathSet.paths.length = 0;
931
+ const { style, lineBox, isVertical } = text;
932
+ if (isNone(style.backgroundImage))
933
+ return;
934
+ const { pathSet: imagePathSet } = parser.parse(style.backgroundImage);
935
+ const imageBox = imagePathSet.getBoundingBox(true);
936
+ const transform = new Matrix3();
937
+ transform.translate(-imageBox.x, -imageBox.y);
938
+ if (isVertical) {
939
+ transform.scale(lineBox.height / imageBox.width, lineBox.width / imageBox.height);
940
+ const tx = lineBox.height / 2;
941
+ const ty = lineBox.width / 2;
942
+ transform.translate(-tx, -ty);
943
+ transform.rotate(-Math.PI / 2);
944
+ transform.translate(ty, tx);
945
+ } else {
946
+ transform.scale(lineBox.width / imageBox.width, lineBox.height / imageBox.height);
947
+ }
948
+ transform.translate(lineBox.x, lineBox.y);
949
+ imagePathSet.paths.forEach((originalPath) => {
950
+ pathSet.paths.push(
951
+ originalPath.clone().applyTransform(transform)
952
+ );
953
+ });
954
+ },
955
+ render: (ctx, text) => {
956
+ const { boundingBox, computedStyle: style } = text;
957
+ if (!isNone(style.backgroundColor)) {
958
+ ctx.fillStyle = style.backgroundColor;
959
+ ctx.fillRect(...boundingBox.array);
960
+ }
961
+ pathSet.paths.forEach((path) => {
962
+ drawPath({
963
+ ctx,
964
+ path,
965
+ fontSize: style.fontSize
966
+ });
967
+ if (text.debug) {
968
+ const box = new Path2DSet([path]).getBoundingBox();
969
+ if (box) {
970
+ ctx.strokeRect(box.x, box.y, box.width, box.height);
971
+ }
972
+ }
973
+ });
974
+ }
880
975
  };
881
976
  }
882
977
 
@@ -900,53 +995,30 @@ function getHighlightStyle(style) {
900
995
  };
901
996
  }
902
997
  function highlight() {
903
- const paths = [];
998
+ const pathSet = new Path2DSet();
904
999
  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 = svgToDOM(
922
- needsFetch(svg) ? loaded.get(svg) ?? svg : svg
923
- );
924
- const pathSet = svgToPath2DSet(dom);
925
- result = { dom, pathSet };
926
- parsed.set(svg, result);
927
- }
928
- return result;
929
- }
1000
+ const loader = createSVGLoader();
1001
+ const parser = createSVGParser(loader);
930
1002
  return definePlugin({
931
1003
  name: "highlight",
932
- paths,
1004
+ pathSet,
933
1005
  load: async (text) => {
934
1006
  const set = /* @__PURE__ */ new Set();
935
1007
  text.forEachCharacter((character) => {
936
1008
  const { computedStyle: style } = character;
937
1009
  const { image, referImage } = getHighlightStyle(style);
938
- if (needsFetch(image)) {
1010
+ if (loader.needsLoad(image)) {
939
1011
  set.add(image);
940
1012
  }
941
- if (needsFetch(referImage)) {
1013
+ if (loader.needsLoad(referImage)) {
942
1014
  set.add(referImage);
943
1015
  }
944
1016
  });
945
- await Promise.all(Array.from(set).map((src) => loadSvg(src)));
1017
+ await Promise.all(Array.from(set).map((src) => loader.load(src)));
946
1018
  },
947
1019
  update: (text) => {
948
1020
  clipRects.length = 0;
949
- paths.length = 0;
1021
+ pathSet.paths.length = 0;
950
1022
  let groups = [];
951
1023
  let group;
952
1024
  let prevHighlight;
@@ -993,11 +1065,15 @@ function highlight() {
993
1065
  ...characters.filter((c) => c.glyphBox).map((c) => c.glyphBox)
994
1066
  );
995
1067
  const {
996
- computedStyle: style
1068
+ computedStyle: style,
1069
+ isVertical,
1070
+ inlineBox,
1071
+ glyphBox = inlineBox,
1072
+ strikeoutPosition,
1073
+ underlinePosition
997
1074
  } = char;
998
1075
  const {
999
- fontSize,
1000
- writingMode
1076
+ fontSize
1001
1077
  } = style;
1002
1078
  const {
1003
1079
  image,
@@ -1007,39 +1083,40 @@ function highlight() {
1007
1083
  size,
1008
1084
  thickness
1009
1085
  } = getHighlightStyle(style);
1010
- const isVertical = writingMode.includes("vertical");
1011
1086
  const _thickness = parseValueNumber(thickness, { fontSize, total: groupBox.width }) / groupBox.width;
1012
1087
  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 BoundingBox().copy(groupBox);
1088
+ const { pathSet: imagePathSet, dom: imageDom } = parser.parse(image);
1089
+ const imageBox = imagePathSet.getBoundingBox(true);
1090
+ const styleScale = fontSize / imageBox.width * 2;
1091
+ const targetBox = new BoundingBox().copy(groupBox);
1017
1092
  if (isVertical) {
1018
- cBox.width = groupBox.height;
1019
- cBox.height = groupBox.width;
1020
- cBox.left = groupBox.left + groupBox.width;
1093
+ targetBox.width = groupBox.height;
1094
+ targetBox.height = groupBox.width;
1095
+ targetBox.left = groupBox.left + groupBox.width;
1021
1096
  }
1022
- const rawWidth = Math.floor(cBox.width);
1097
+ const rawWidth = Math.floor(targetBox.width);
1023
1098
  let userWidth = rawWidth;
1024
1099
  if (size !== "cover") {
1025
1100
  userWidth = parseValueNumber(size, { fontSize, total: groupBox.width }) || rawWidth;
1026
- cBox.width = userWidth;
1101
+ targetBox.width = userWidth;
1027
1102
  }
1028
- if (!isNone(referImage) && isNone(line)) {
1029
- const bBox = getPaths(referImage).pathSet.getBoundingBox(true);
1030
- aBox.copy(bBox);
1103
+ const hasReferImage = !isNone(referImage) && isNone(line);
1104
+ if (hasReferImage) {
1105
+ imageBox.copy(
1106
+ parser.parse(referImage).pathSet.getBoundingBox(true)
1107
+ );
1031
1108
  } else {
1032
1109
  let _line;
1033
1110
  if (isNone(line)) {
1034
- if (aBox.width / aBox.height > 4) {
1111
+ if (imageBox.width / imageBox.height > 4) {
1035
1112
  _line = "underline";
1036
- const viewBox = svgDom.getAttribute("viewBox");
1113
+ const viewBox = imageDom.getAttribute("viewBox");
1037
1114
  if (viewBox) {
1038
1115
  const [_x, y, _w, h] = viewBox.split(" ").map((v) => Number(v));
1039
1116
  const viewCenter = y + h / 2;
1040
- if (aBox.y < viewCenter && aBox.y + aBox.height > viewCenter) {
1117
+ if (imageBox.y < viewCenter && imageBox.y + imageBox.height > viewCenter) {
1041
1118
  _line = "line-through";
1042
- } else if (aBox.y + aBox.height < viewCenter) {
1119
+ } else if (imageBox.y + imageBox.height < viewCenter) {
1043
1120
  _line = "overline";
1044
1121
  } else {
1045
1122
  _line = "underline";
@@ -1053,89 +1130,95 @@ function highlight() {
1053
1130
  }
1054
1131
  switch (_line) {
1055
1132
  case "outline": {
1056
- const paddingX = cBox.width * 0.2;
1057
- const paddingY = cBox.height * 0.2;
1058
- cBox.width += paddingX;
1059
- cBox.height += paddingY;
1133
+ const paddingX = targetBox.width * 0.2;
1134
+ const paddingY = targetBox.height * 0.2;
1060
1135
  if (isVertical) {
1061
- cBox.x -= paddingY / 2;
1062
- cBox.y -= paddingX / 2;
1063
- cBox.x += cBox.height;
1136
+ targetBox.x -= paddingY / 2;
1137
+ targetBox.y -= paddingX / 2;
1138
+ targetBox.x -= targetBox.height;
1064
1139
  } else {
1065
- cBox.x -= paddingX / 2;
1066
- cBox.y -= paddingY / 2;
1140
+ targetBox.x -= paddingX / 2;
1141
+ targetBox.y -= paddingY / 2;
1067
1142
  }
1143
+ targetBox.width += paddingX;
1144
+ targetBox.height += paddingY;
1068
1145
  break;
1069
1146
  }
1070
1147
  case "overline":
1071
- cBox.height = aBox.height * styleScale;
1148
+ targetBox.height = imageBox.height * styleScale;
1072
1149
  if (isVertical) {
1073
- cBox.x = char.inlineBox.left + char.inlineBox.width;
1150
+ targetBox.x = inlineBox.left + inlineBox.width;
1074
1151
  } else {
1075
- cBox.y = char.inlineBox.top;
1152
+ targetBox.y = inlineBox.top;
1076
1153
  }
1077
1154
  break;
1078
1155
  case "line-through":
1079
- cBox.height = aBox.height * styleScale;
1156
+ targetBox.height = imageBox.height * styleScale;
1080
1157
  if (isVertical) {
1081
- cBox.x = char.inlineBox.left + char.inlineBox.width - char.strikeoutPosition + cBox.height / 2;
1158
+ targetBox.x = inlineBox.left + inlineBox.width - strikeoutPosition + targetBox.height / 2;
1082
1159
  } else {
1083
- cBox.y = char.inlineBox.top + char.strikeoutPosition - cBox.height / 2;
1160
+ targetBox.y = inlineBox.top + strikeoutPosition - targetBox.height / 2;
1084
1161
  }
1085
1162
  break;
1086
1163
  case "underline":
1087
- cBox.height = aBox.height * styleScale;
1164
+ targetBox.height = imageBox.height * styleScale;
1088
1165
  if (isVertical) {
1089
- cBox.x = char.inlineBox.left + char.inlineBox.width - char.underlinePosition;
1166
+ targetBox.x = glyphBox.left + glyphBox.width - underlinePosition;
1090
1167
  } else {
1091
- cBox.y = char.inlineBox.top + char.underlinePosition;
1168
+ targetBox.y = inlineBox.top + underlinePosition;
1092
1169
  }
1093
1170
  break;
1094
1171
  }
1095
1172
  }
1096
- const transform = new Matrix3().translate(-aBox.x, -aBox.y).scale(cBox.width / aBox.width, cBox.height / aBox.height);
1173
+ const transform = new Matrix3();
1174
+ transform.translate(-imageBox.x, -imageBox.y);
1175
+ transform.scale(targetBox.width / imageBox.width, targetBox.height / imageBox.height);
1097
1176
  if (isVertical) {
1177
+ const tx = targetBox.width / 2;
1178
+ const ty = targetBox.height / 2;
1179
+ if (!hasReferImage) {
1180
+ transform.translate(-tx, -ty);
1181
+ }
1098
1182
  transform.rotate(-Math.PI / 2);
1183
+ if (!hasReferImage) {
1184
+ transform.translate(ty, tx);
1185
+ }
1099
1186
  }
1100
- transform.translate(cBox.x, cBox.y);
1187
+ transform.translate(targetBox.x, targetBox.y);
1101
1188
  for (let i2 = 0; i2 < Math.ceil(rawWidth / userWidth); i2++) {
1102
1189
  const _transform = transform.clone();
1103
1190
  if (isVertical) {
1104
- _transform.translate(0, i2 * cBox.width);
1191
+ _transform.translate(0, i2 * targetBox.width);
1105
1192
  } else {
1106
- _transform.translate(i2 * cBox.width, 0);
1193
+ _transform.translate(i2 * targetBox.width, 0);
1107
1194
  }
1108
- svgPathSet.paths.forEach((originalPath) => {
1195
+ imagePathSet.paths.forEach((originalPath) => {
1109
1196
  const path = originalPath.clone().applyTransform(_transform);
1110
- if (path.style.strokeWidth) {
1197
+ if (path.style.strokeWidth)
1111
1198
  path.style.strokeWidth *= styleScale * _thickness;
1112
- }
1113
- if (path.style.strokeMiterlimit) {
1199
+ if (path.style.strokeMiterlimit)
1114
1200
  path.style.strokeMiterlimit *= styleScale;
1115
- }
1116
- if (path.style.strokeDashoffset) {
1201
+ if (path.style.strokeDashoffset)
1117
1202
  path.style.strokeDashoffset *= styleScale;
1118
- }
1119
- if (path.style.strokeDasharray) {
1203
+ if (path.style.strokeDasharray)
1120
1204
  path.style.strokeDasharray = path.style.strokeDasharray.map((v) => v * styleScale);
1121
- }
1122
1205
  if (path.style.fill && path.style.fill in _colormap) {
1123
1206
  path.style.fill = _colormap[path.style.fill];
1124
1207
  }
1125
1208
  if (path.style.stroke && path.style.stroke in _colormap) {
1126
1209
  path.style.stroke = _colormap[path.style.stroke];
1127
1210
  }
1128
- paths.push(path);
1211
+ pathSet.paths.push(path);
1129
1212
  if (rawWidth !== userWidth) {
1130
1213
  if (isVertical) {
1131
- clipRects[paths.length - 1] = new BoundingBox(
1214
+ clipRects[pathSet.paths.length - 1] = new BoundingBox(
1132
1215
  groupBox.left - groupBox.width * 2,
1133
1216
  groupBox.top,
1134
1217
  groupBox.width * 4,
1135
1218
  groupBox.height
1136
1219
  );
1137
1220
  } else {
1138
- clipRects[paths.length - 1] = new BoundingBox(
1221
+ clipRects[pathSet.paths.length - 1] = new BoundingBox(
1139
1222
  groupBox.left,
1140
1223
  groupBox.top - groupBox.height * 2,
1141
1224
  groupBox.width,
@@ -1148,8 +1231,24 @@ function highlight() {
1148
1231
  }
1149
1232
  },
1150
1233
  renderOrder: -1,
1234
+ getBoundingBox: () => {
1235
+ const boundingBoxs = [];
1236
+ pathSet.paths.forEach((path, index) => {
1237
+ const clipRect = clipRects[index];
1238
+ let box = path.getBoundingBox();
1239
+ if (clipRect) {
1240
+ const x = Math.max(box.x, clipRect.x);
1241
+ const y = Math.max(box.y, clipRect.y);
1242
+ const right = Math.min(box.right, clipRect.right);
1243
+ const bottom = Math.min(box.bottom, clipRect.bottom);
1244
+ box = new BoundingBox(x, y, right - x, bottom - y);
1245
+ }
1246
+ boundingBoxs.push(box);
1247
+ });
1248
+ return BoundingBox.from(...boundingBoxs);
1249
+ },
1151
1250
  render: (ctx, text) => {
1152
- paths.forEach((path, index) => {
1251
+ pathSet.paths.forEach((path, index) => {
1153
1252
  drawPath({
1154
1253
  ctx,
1155
1254
  path,
@@ -1173,12 +1272,12 @@ function genDisc(r, color) {
1173
1272
  </svg>`;
1174
1273
  }
1175
1274
  function listStyle() {
1176
- const paths = [];
1275
+ const pathSet = new Path2DSet();
1177
1276
  return definePlugin({
1178
1277
  name: "listStyle",
1179
- paths,
1278
+ pathSet,
1180
1279
  update: (text) => {
1181
- paths.length = 0;
1280
+ pathSet.paths.length = 0;
1182
1281
  const { paragraphs, isVertical, fontSize } = text;
1183
1282
  const padding = fontSize * 0.45;
1184
1283
  paragraphs.forEach((paragraph) => {
@@ -1232,7 +1331,7 @@ function listStyle() {
1232
1331
  inlineBox.top + (inlineBox.height - imageBox.height * _scale) / 2
1233
1332
  );
1234
1333
  }
1235
- paths.push(...imagePathSet.paths.map((p) => {
1334
+ pathSet.paths.push(...imagePathSet.paths.map((p) => {
1236
1335
  const path = p.clone();
1237
1336
  path.applyTransform(m);
1238
1337
  if (path.style.fill && path.style.fill in colormap) {
@@ -1295,39 +1394,21 @@ function render() {
1295
1394
  return boxes.length ? BoundingBox.from(...boxes) : void 0;
1296
1395
  },
1297
1396
  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 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
- });
1397
+ const { paragraphs, glyphBox, effects } = text;
1311
1398
  if (effects) {
1312
- effects.forEach((style2) => {
1313
- uploadColor(style2, glyphBox, ctx);
1399
+ effects.forEach((style) => {
1400
+ uploadColor(style, glyphBox, ctx);
1314
1401
  ctx.save();
1315
- const [a, c, e, b, d, f] = getTransform2D(text, style2).transpose().elements;
1402
+ const [a, c, e, b, d, f] = getTransform2D(text, style).transpose().elements;
1316
1403
  ctx.transform(a, b, c, d, e, f);
1317
1404
  text.forEachCharacter((character) => {
1318
- if (character.parent.style?.backgroundColor) {
1319
- fillBackground(character.parent.style.backgroundColor, character.inlineBox);
1320
- }
1321
- character.drawTo(ctx, style2);
1405
+ character.drawTo(ctx, style);
1322
1406
  });
1323
1407
  ctx.restore();
1324
1408
  });
1325
1409
  } else {
1326
1410
  paragraphs.forEach((paragraph) => {
1327
1411
  paragraph.fragments.forEach((fragment) => {
1328
- if (fragment.style?.backgroundColor) {
1329
- fillBackground(fragment.computedStyle.backgroundColor, fragment.inlineBox);
1330
- }
1331
1412
  fragment.characters.forEach((character) => {
1332
1413
  character.drawTo(ctx);
1333
1414
  });
@@ -1365,12 +1446,12 @@ function getTransform2D(text, style) {
1365
1446
  }
1366
1447
 
1367
1448
  function textDecoration() {
1368
- const paths = [];
1449
+ const pathSet = new Path2DSet();
1369
1450
  return definePlugin({
1370
1451
  name: "textDecoration",
1371
- paths,
1452
+ pathSet,
1372
1453
  update: (text) => {
1373
- paths.length = 0;
1454
+ pathSet.paths.length = 0;
1374
1455
  const groups = [];
1375
1456
  let group;
1376
1457
  let prevStyle;
@@ -1430,7 +1511,8 @@ function textDecoration() {
1430
1511
  color,
1431
1512
  textDecoration: textDecoration2
1432
1513
  } = style;
1433
- const { left, top, width, height } = BoundingBox.from(...group2.map((c) => c.inlineBox));
1514
+ const inlineBox = BoundingBox.from(...group2.map((c) => c.inlineBox));
1515
+ const { left, top, width, height } = inlineBox;
1434
1516
  let position = isVertical ? left + width : top;
1435
1517
  const direction = isVertical ? -1 : 1;
1436
1518
  let thickness = 0;
@@ -1470,7 +1552,7 @@ function textDecoration() {
1470
1552
  fill: color
1471
1553
  });
1472
1554
  }
1473
- paths.push(path);
1555
+ pathSet.paths.push(path);
1474
1556
  });
1475
1557
  },
1476
1558
  render: (ctx, text) => {
@@ -1480,7 +1562,7 @@ function textDecoration() {
1480
1562
  ctx.save();
1481
1563
  const [a, c, e, b, d, f] = getTransform2D(text, effectStyle).transpose().elements;
1482
1564
  ctx.transform(a, b, c, d, e, f);
1483
- paths.forEach((path) => {
1565
+ pathSet.paths.forEach((path) => {
1484
1566
  drawPath({
1485
1567
  ctx,
1486
1568
  path,
@@ -1491,7 +1573,7 @@ function textDecoration() {
1491
1573
  ctx.restore();
1492
1574
  });
1493
1575
  } else {
1494
- paths.forEach((path) => {
1576
+ pathSet.paths.forEach((path) => {
1495
1577
  drawPath({
1496
1578
  ctx,
1497
1579
  path,
@@ -1661,22 +1743,16 @@ class Text extends EventEmitter {
1661
1743
  this.pathBox = BoundingBox.from(
1662
1744
  this.glyphBox,
1663
1745
  ...Array.from(this.plugins.values()).map((plugin) => {
1664
- return plugin.getBoundingBox ? plugin.getBoundingBox(this) : new Path2DSet(plugin.paths ?? []).getBoundingBox();
1746
+ return plugin.getBoundingBox ? plugin.getBoundingBox(this) : plugin.pathSet?.getBoundingBox();
1665
1747
  }).filter(Boolean)
1666
1748
  );
1667
1749
  return this;
1668
1750
  }
1669
1751
  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 BoundingBox(
1676
- left,
1677
- top,
1678
- right - left,
1679
- bottom - top
1752
+ this.boundingBox = BoundingBox.from(
1753
+ this.rawGlyphBox,
1754
+ this.lineBox,
1755
+ this.pathBox
1680
1756
  );
1681
1757
  return this;
1682
1758
  }
@@ -1707,9 +1783,9 @@ class Text extends EventEmitter {
1707
1783
  Array.from(this.plugins.values()).sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0)).forEach((plugin) => {
1708
1784
  if (plugin.render) {
1709
1785
  plugin.render?.(ctx, this);
1710
- } else if (plugin.paths) {
1786
+ } else if (plugin.pathSet) {
1711
1787
  const style = this.computedStyle;
1712
- plugin.paths.forEach((path) => {
1788
+ plugin.pathSet.paths.forEach((path) => {
1713
1789
  drawPath({
1714
1790
  ctx,
1715
1791
  path,
@@ -1742,4 +1818,4 @@ function renderText(options, load) {
1742
1818
  return text.render(options);
1743
1819
  }
1744
1820
 
1745
- export { Character, Fragment, Measurer, Paragraph, Text, background, definePlugin, drawPath, filterEmpty, getHighlightStyle, getTransform2D, hexToRgb, highlight, isEqualObject, isEqualValue, isNone, listStyle, measureText, needsFetch, outline, parseColor, parseColormap, parseValueNumber, render, renderText, setupView, textDecoration, textDefaultStyle, uploadColor, uploadColors };
1821
+ export { Character, Fragment, Measurer, Paragraph, Text, background, createSVGLoader, createSVGParser, definePlugin, drawPath, filterEmpty, getHighlightStyle, getTransform2D, hexToRgb, highlight, isEqualObject, isEqualValue, isNone, listStyle, measureText, outline, parseColor, parseColormap, parseValueNumber, render, renderText, setupView, textDecoration, textDefaultStyle, uploadColor, uploadColors };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modern-text",
3
3
  "type": "module",
4
- "version": "1.3.1",
4
+ "version": "1.3.2",
5
5
  "packageManager": "pnpm@9.15.1",
6
6
  "description": "Measure and render text in a way that describes the DOM.",
7
7
  "author": "wxm",
@@ -57,22 +57,22 @@
57
57
  "prepare": "simple-git-hooks"
58
58
  },
59
59
  "dependencies": {
60
- "modern-font": "^0.4.0",
61
- "modern-idoc": "^0.2.11",
62
- "modern-path2d": "^1.2.10"
60
+ "modern-font": "^0.4.1",
61
+ "modern-idoc": "^0.2.13",
62
+ "modern-path2d": "^1.2.18"
63
63
  },
64
64
  "devDependencies": {
65
- "@antfu/eslint-config": "^4.8.1",
66
- "@types/node": "^22.13.10",
67
- "bumpp": "^10.0.3",
65
+ "@antfu/eslint-config": "^4.11.0",
66
+ "@types/node": "^22.13.14",
67
+ "bumpp": "^10.1.0",
68
68
  "conventional-changelog-cli": "^5.0.0",
69
- "eslint": "^9.22.0",
70
- "lint-staged": "^15.4.3",
71
- "simple-git-hooks": "^2.11.1",
69
+ "eslint": "^9.23.0",
70
+ "lint-staged": "^15.5.0",
71
+ "simple-git-hooks": "^2.12.1",
72
72
  "typescript": "^5.8.2",
73
73
  "unbuild": "^3.5.0",
74
- "vite": "^6.2.1",
75
- "vitest": "^3.0.8"
74
+ "vite": "^6.2.3",
75
+ "vitest": "^3.0.9"
76
76
  },
77
77
  "simple-git-hooks": {
78
78
  "pre-commit": "pnpm lint-staged"