modern-text 0.4.5 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +275 -149
- package/dist/index.d.cts +22 -13
- package/dist/index.d.mts +22 -13
- package/dist/index.d.ts +22 -13
- package/dist/index.js +4 -4
- package/dist/index.mjs +275 -151
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -152,6 +152,8 @@ class Character {
|
|
|
152
152
|
__publicField$3(this, "lineBox", new modernPath2d.BoundingBox());
|
|
153
153
|
__publicField$3(this, "inlineBox", new modernPath2d.BoundingBox());
|
|
154
154
|
__publicField$3(this, "glyphBox");
|
|
155
|
+
__publicField$3(this, "advanceWidth", 0);
|
|
156
|
+
__publicField$3(this, "advanceHeight", 0);
|
|
155
157
|
__publicField$3(this, "underlinePosition", 0);
|
|
156
158
|
__publicField$3(this, "underlineThickness", 0);
|
|
157
159
|
__publicField$3(this, "strikeoutPosition", 0);
|
|
@@ -198,14 +200,16 @@ class Character {
|
|
|
198
200
|
const unitsPerEm = head.unitsPerEm;
|
|
199
201
|
const ascender = hhea.ascent;
|
|
200
202
|
const descender = hhea.descent;
|
|
201
|
-
const { content, computedStyle } = this;
|
|
203
|
+
const { content, computedStyle, isVertical } = this;
|
|
202
204
|
const { fontSize } = computedStyle;
|
|
203
205
|
const rate = unitsPerEm / fontSize;
|
|
204
206
|
const advanceWidth = sfnt.getAdvanceWidth(content, fontSize);
|
|
205
207
|
const advanceHeight = (ascender + Math.abs(descender)) / rate;
|
|
206
208
|
const baseline = ascender / rate;
|
|
207
|
-
this.
|
|
208
|
-
this.
|
|
209
|
+
this.advanceWidth = advanceWidth;
|
|
210
|
+
this.advanceHeight = advanceHeight;
|
|
211
|
+
this.inlineBox.width = isVertical ? advanceHeight : advanceWidth;
|
|
212
|
+
this.inlineBox.height = isVertical ? advanceWidth : advanceHeight;
|
|
209
213
|
this.underlinePosition = (ascender - post.underlinePosition) / rate;
|
|
210
214
|
this.underlineThickness = post.underlineThickness / rate;
|
|
211
215
|
this.strikeoutPosition = (ascender - os2.yStrikeoutPosition) / rate;
|
|
@@ -239,7 +243,9 @@ class Character {
|
|
|
239
243
|
ascender,
|
|
240
244
|
descender,
|
|
241
245
|
typoAscender,
|
|
242
|
-
fontStyle
|
|
246
|
+
fontStyle,
|
|
247
|
+
advanceWidth,
|
|
248
|
+
advanceHeight
|
|
243
249
|
} = this;
|
|
244
250
|
const { left, top } = inlineBox;
|
|
245
251
|
const needsItalic = style.fontStyle === "italic" && fontStyle !== "italic";
|
|
@@ -248,26 +254,31 @@ class Character {
|
|
|
248
254
|
let glyphIndex;
|
|
249
255
|
const path = new modernPath2d.Path2D();
|
|
250
256
|
if (isVertical) {
|
|
251
|
-
x += (
|
|
252
|
-
if (Math.abs(
|
|
253
|
-
y -= (ascender - typoAscender) / (ascender + Math.abs(descender)) *
|
|
257
|
+
x += (advanceHeight - advanceWidth) / 2;
|
|
258
|
+
if (Math.abs(advanceWidth - advanceHeight) > 0.1) {
|
|
259
|
+
y -= (ascender - typoAscender) / (ascender + Math.abs(descender)) * advanceHeight;
|
|
254
260
|
}
|
|
255
261
|
glyphIndex = void 0;
|
|
256
262
|
}
|
|
257
263
|
if (isVertical && !set1.has(content) && (content.codePointAt(0) <= 256 || set2.has(content))) {
|
|
258
264
|
path.addCommands(
|
|
259
|
-
sfnt.getPathCommands(
|
|
265
|
+
sfnt.getPathCommands(
|
|
266
|
+
content,
|
|
267
|
+
x,
|
|
268
|
+
top + baseline - (advanceHeight - advanceWidth) / 2,
|
|
269
|
+
style.fontSize
|
|
270
|
+
)
|
|
260
271
|
);
|
|
261
272
|
const point = {
|
|
262
|
-
y: top - (
|
|
263
|
-
x: x +
|
|
273
|
+
y: top - (advanceHeight - advanceWidth) / 2 + advanceHeight / 2,
|
|
274
|
+
x: x + advanceWidth / 2
|
|
264
275
|
};
|
|
265
276
|
if (needsItalic) {
|
|
266
277
|
this._italic(
|
|
267
278
|
path,
|
|
268
279
|
isVertical ? {
|
|
269
280
|
x: point.x,
|
|
270
|
-
y: top - (
|
|
281
|
+
y: top - (advanceHeight - advanceWidth) / 2 + baseline
|
|
271
282
|
} : void 0
|
|
272
283
|
);
|
|
273
284
|
}
|
|
@@ -281,19 +292,17 @@ class Character {
|
|
|
281
292
|
this._italic(
|
|
282
293
|
path,
|
|
283
294
|
isVertical ? {
|
|
284
|
-
x: x +
|
|
285
|
-
y: top + typoAscender / (ascender + Math.abs(descender)) *
|
|
295
|
+
x: x + advanceWidth / 2,
|
|
296
|
+
y: top + typoAscender / (ascender + Math.abs(descender)) * advanceHeight
|
|
286
297
|
} : void 0
|
|
287
298
|
);
|
|
288
299
|
}
|
|
289
300
|
} else {
|
|
290
|
-
path.addCommands(
|
|
291
|
-
sfnt.getPathCommands(content, x, y, style.fontSize) ?? []
|
|
292
|
-
);
|
|
301
|
+
path.addCommands(sfnt.getPathCommands(content, x, y, style.fontSize));
|
|
293
302
|
if (needsItalic) {
|
|
294
303
|
this._italic(
|
|
295
304
|
path,
|
|
296
|
-
isVertical ? { x: x +
|
|
305
|
+
isVertical ? { x: x + advanceHeight / 2, y } : void 0
|
|
297
306
|
);
|
|
298
307
|
}
|
|
299
308
|
}
|
|
@@ -347,6 +356,23 @@ class Character {
|
|
|
347
356
|
function isNone(val) {
|
|
348
357
|
return !val || val === "none";
|
|
349
358
|
}
|
|
359
|
+
function isEqualObject(obj1, obj2) {
|
|
360
|
+
const keys1 = Object.keys(obj1);
|
|
361
|
+
const keys2 = Object.keys(obj2);
|
|
362
|
+
const keys = Array.from(/* @__PURE__ */ new Set([...keys1, ...keys2]));
|
|
363
|
+
return keys.every((key) => obj1[key] === obj2[key]);
|
|
364
|
+
}
|
|
365
|
+
function hexToRgb(hex) {
|
|
366
|
+
const cleanHex = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
367
|
+
const isValidHex = /^(?:[0-9A-F]{3}|[0-9A-F]{6})$/i.test(cleanHex);
|
|
368
|
+
if (!isValidHex)
|
|
369
|
+
return null;
|
|
370
|
+
const fullHex = cleanHex.length === 3 ? cleanHex.split("").map((char) => char + char).join("") : cleanHex;
|
|
371
|
+
const r = Number.parseInt(fullHex.slice(0, 2), 16);
|
|
372
|
+
const g = Number.parseInt(fullHex.slice(2, 4), 16);
|
|
373
|
+
const b = Number.parseInt(fullHex.slice(4, 6), 16);
|
|
374
|
+
return `rgb(${r}, ${g}, ${b})`;
|
|
375
|
+
}
|
|
350
376
|
function filterEmpty(val) {
|
|
351
377
|
if (!val)
|
|
352
378
|
return val;
|
|
@@ -594,16 +620,23 @@ class Measurer {
|
|
|
594
620
|
top: character.top - rect.top
|
|
595
621
|
});
|
|
596
622
|
const item = paragraphs[paragraphIndex].fragments[fragmentIndex].characters[characterIndex];
|
|
623
|
+
const { fontHeight, isVertical } = item;
|
|
597
624
|
const result = results[i];
|
|
598
625
|
item.inlineBox.left = result.left;
|
|
599
626
|
item.inlineBox.top = result.top;
|
|
600
627
|
item.inlineBox.width = result.width;
|
|
601
628
|
item.inlineBox.height = result.height;
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
629
|
+
if (isVertical) {
|
|
630
|
+
item.lineBox.left = result.left + (result.width - fontHeight) / 2;
|
|
631
|
+
item.lineBox.top = result.top;
|
|
632
|
+
item.lineBox.width = fontHeight;
|
|
633
|
+
item.lineBox.height = result.height;
|
|
634
|
+
} else {
|
|
635
|
+
item.lineBox.left = result.left;
|
|
636
|
+
item.lineBox.top = result.top + (result.height - fontHeight) / 2;
|
|
637
|
+
item.lineBox.width = result.width;
|
|
638
|
+
item.lineBox.height = fontHeight;
|
|
639
|
+
}
|
|
607
640
|
i++;
|
|
608
641
|
});
|
|
609
642
|
return {
|
|
@@ -622,7 +655,6 @@ class Measurer {
|
|
|
622
655
|
}
|
|
623
656
|
}
|
|
624
657
|
|
|
625
|
-
const defaultReferImage = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiIgdmlld0JveD0iMCAwIDcyIDcyIiBmaWxsPSJub25lIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMyLjQwMjkgMjhIMzUuMTU5NFYzMy4xNzcxQzM1Ljk4MjEgMzIuMzExNSAzNi45NzEgMzEuODczNyAzOC4wOTQ4IDMxLjg3MzdDMzkuNjY3NiAzMS44NzM3IDQwLjkxNjYgMzIuNDI5NSA0MS44MzkgMzMuNTQzN0w0MS44NDAzIDMzLjU0NTNDNDIuNjcxNyAzNC41NzA1IDQzLjA5MTUgMzUuODU1OSA0My4wOTE1IDM3LjM4NzdDNDMuMDkxNSAzOC45NzYxIDQyLjY3MjkgNDAuMzAyOCA0MS44MTgzIDQxLjMzMDRMNDEuODE3MSA0MS4zMzE4QzQwLjg3MzEgNDIuNDQ2MSAzOS41ODMyIDQzIDM3Ljk3MjEgNDNDMzYuNzQ3NyA0MyAzNS43NDg4IDQyLjY1OTkgMzQuOTk1OCA0MS45NjkzVjQyLjcyNDdIMzIuNDAyOVYyOFpNMzcuNTQyOCAzNC4wOTI0QzM2Ljg1NDkgMzQuMDkyNCAzNi4zMDE0IDM0LjM1NjEgMzUuODQ4NyAzNC45MDA0TDM1Ljg0NTIgMzQuOTA0NkMzNS4zMzU4IDM1LjQ4NTMgMzUuMDc3NiAzNi4yOTc2IDM1LjA3NzYgMzcuMzQ4NFYzNy41MDU3QzM1LjA3NzYgMzguNDY0IDM1LjI3NzIgMzkuMjQ0MyAzNS42OTQzIDM5LjgyNzlDMzYuMTQ0MSA0MC40NTg3IDM2Ljc3MjYgNDAuNzgxMyAzNy42MjQ1IDQwLjc4MTNDMzguNTg3NCA0MC43ODEzIDM5LjI3MDcgNDAuNDUyNyAzOS43MTUyIDM5LjgxMjdDNDAuMDcyOCAzOS4yNjg0IDQwLjI3MzcgMzguNDY3MyA0MC4yNzM3IDM3LjM4NzdDNDAuMjczNyAzNi4zMTA1IDQwLjA1MzMgMzUuNTMxMyAzOS42NzgzIDM1LjAwNzdDMzkuMjM3MSAzNC40MDcxIDM4LjUzNDIgMzQuMDkyNCAzNy41NDI4IDM0LjA5MjRaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZD0iTTQ5Ljg2MTQgMzEuODczN0M0OC4xNTM1IDMxLjg3MzcgNDYuODAxNiAzMi40MjM5IDQ1LjgzNDggMzMuNTM5MkM0NC45MzcgMzQuNTQ3MiA0NC40OTY2IDM1Ljg1NiA0NC40OTY2IDM3LjQyN0M0NC40OTY2IDM5LjAzNjggNDQuOTM2NyA0MC4zNjU5IDQ1Ljg1NTkgNDEuMzk0M0M0Ni44MDMxIDQyLjQ3MDYgNDguMTM0OCA0MyA0OS44MjA1IDQzQzUxLjIyNiA0MyA1Mi4zODI2IDQyLjY1NjMgNTMuMjQ3OSA0MS45Njk3QzU0LjEzNTkgNDEuMjYxNCA1NC43MDYxIDQwLjE4ODcgNTQuOTU3MyAzOC43NzkxTDU1IDM4LjUzOTdINTIuMjQ4NEw1Mi4yMjU5IDM4LjcyMDFDNTIuMTM3OSAzOS40MjUxIDUxLjg5MjUgMzkuOTI3OCA1MS41MTA5IDQwLjI1NThDNTEuMTI5NSA0MC41ODM1IDUwLjU4MzEgNDAuNzYxNiA0OS44NDA5IDQwLjc2MTZDNDkuMDAwMSA0MC43NjE2IDQ4LjM5NDkgNDAuNDcxNSA0Ny45OTA3IDM5LjkyMzdMNDcuOTg3NCAzOS45MTk0QzQ3LjUzNTYgMzkuMzQwMSA0Ny4zMTQ0IDM4LjUwNjIgNDcuMzE0NCAzNy40MDc0QzQ3LjMxNDQgMzYuMzMyMiA0Ny41NTQ0IDM1LjUxNzcgNDguMDA1OCAzNC45NTY4TDQ4LjAwNzggMzQuOTU0M0M0OC40NTM3IDM0LjM4MjUgNDkuMDYxOCAzNC4xMTIxIDQ5Ljg2MTQgMzQuMTEyMUM1MC41MjMgMzQuMTEyMSA1MS4wNDUxIDM0LjI2MTUgNTEuNDI3MiAzNC41NDA3QzUxLjc4ODQgMzQuODE5NCA1Mi4wNTMgMzUuMjQ0NyA1Mi4xODgxIDM1Ljg1NzFMNTIuMjIzOSAzNi4wMTk0SDU0Ljk1NDhMNTQuOTE3IDM1Ljc4MzVDNTQuNzA2MyAzNC40NjYgNTQuMTUzNiAzMy40NzAxIDUzLjI2MzQgMzIuODAxOUw1My4yNjAyIDMyLjc5OTVDNTIuMzk1MSAzMi4xNzU1IDUxLjI2MjEgMzEuODczNyA0OS44NjE0IDMxLjg3MzdaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNS43NTYxIDI4LjI3NTNIMjIuNzQ0TDE3IDQyLjcyNDdIMjAuMDE0MUwyMS4zNDI5IDM5LjIwNDlIMjcuMTU3MkwyOC40ODYgNDIuNzI0N0gzMS41MDAxTDI1Ljc1NjEgMjguMjc1M1pNMjIuMjEyNSAzNi45MDc2TDI0LjI1OTYgMzEuNDUzOUwyNi4yODg1IDM2LjkwNzZIMjIuMjEyNVoiIGZpbGw9IiMyMjI1MjkiLz48L3N2Zz4=";
|
|
626
658
|
function parseCharsPerRepeat(size, fontSize, total) {
|
|
627
659
|
if (size === "cover") {
|
|
628
660
|
return 0;
|
|
@@ -639,69 +671,33 @@ function parseCharsPerRepeat(size, fontSize, total) {
|
|
|
639
671
|
return Math.ceil(size / fontSize);
|
|
640
672
|
}
|
|
641
673
|
}
|
|
642
|
-
function
|
|
643
|
-
if (typeof
|
|
644
|
-
if (
|
|
645
|
-
return Number(
|
|
646
|
-
} else if (
|
|
647
|
-
const value = Number(
|
|
674
|
+
function parseThickness(thickness, fontSize, total) {
|
|
675
|
+
if (typeof thickness === "string") {
|
|
676
|
+
if (thickness.endsWith("%")) {
|
|
677
|
+
return Number(thickness.substring(0, thickness.length - 1)) / 100;
|
|
678
|
+
} else if (thickness.endsWith("rem")) {
|
|
679
|
+
const value = Number(thickness.substring(0, thickness.length - 3));
|
|
648
680
|
return value * fontSize / total;
|
|
649
681
|
} else {
|
|
650
|
-
return Number(
|
|
682
|
+
return Number(thickness) / total;
|
|
651
683
|
}
|
|
652
684
|
} else {
|
|
653
|
-
return
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
function getTransformMatrix(a, b, c, isVertical, type) {
|
|
657
|
-
let scale;
|
|
658
|
-
if (!isVertical) {
|
|
659
|
-
scale = {
|
|
660
|
-
x: c.width / b.width,
|
|
661
|
-
y: c.height / b.height
|
|
662
|
-
};
|
|
663
|
-
} else {
|
|
664
|
-
scale = {
|
|
665
|
-
x: c.width / b.height,
|
|
666
|
-
y: c.height / b.width
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
const offset = c.center.add(
|
|
670
|
-
a.center.sub(b.center).scale(scale.x, scale.y)
|
|
671
|
-
);
|
|
672
|
-
if (type === "line") {
|
|
673
|
-
offset.sub({
|
|
674
|
-
x: a.width / 2 * scale.x,
|
|
675
|
-
y: a.height * scale.y
|
|
676
|
-
});
|
|
677
|
-
} else {
|
|
678
|
-
offset.sub({
|
|
679
|
-
x: a.width / 2 * scale.x,
|
|
680
|
-
y: a.height / 2 * scale.y
|
|
681
|
-
});
|
|
685
|
+
return thickness / total;
|
|
682
686
|
}
|
|
683
|
-
const m = new modernPath2d.Matrix3();
|
|
684
|
-
m.translate(-a.left, -a.top);
|
|
685
|
-
if (isVertical) {
|
|
686
|
-
m.translate(-a.width / 2, -a.height / 2);
|
|
687
|
-
m.rotate(Math.PI / 2);
|
|
688
|
-
m.translate(a.width / 2, a.height / 2);
|
|
689
|
-
}
|
|
690
|
-
m.scale(scale.x, scale.y);
|
|
691
|
-
m.translate(offset.x, offset.y);
|
|
692
|
-
return m;
|
|
693
687
|
}
|
|
694
688
|
function highlight() {
|
|
695
689
|
const paths = [];
|
|
696
690
|
const clipRects = [];
|
|
697
691
|
const svgStringToSvgPaths = /* @__PURE__ */ new Map();
|
|
698
692
|
function getPaths(svg) {
|
|
699
|
-
let
|
|
700
|
-
if (!
|
|
701
|
-
|
|
702
|
-
|
|
693
|
+
let result = svgStringToSvgPaths.get(svg);
|
|
694
|
+
if (!result) {
|
|
695
|
+
const dom = modernPath2d.parseSvgToDom(svg);
|
|
696
|
+
const paths2 = modernPath2d.parseSvg(dom);
|
|
697
|
+
result = { dom, paths: paths2 };
|
|
698
|
+
svgStringToSvgPaths.set(svg, result);
|
|
703
699
|
}
|
|
704
|
-
return
|
|
700
|
+
return result;
|
|
705
701
|
}
|
|
706
702
|
return definePlugin({
|
|
707
703
|
name: "highlight",
|
|
@@ -714,7 +710,7 @@ function highlight() {
|
|
|
714
710
|
text.forEachCharacter((character) => {
|
|
715
711
|
const { isVertical, computedStyle: style, inlineBox, fontSize } = character;
|
|
716
712
|
if (!isNone(style.highlightImage) && character.glyphBox) {
|
|
717
|
-
if (style.highlightSize !== "1rem" && prevStyle
|
|
713
|
+
if (style.highlightSize !== "1rem" && (!prevStyle || prevStyle.highlightImage === style.highlightImage && isEqualObject(prevStyle.highlightImageColors, style.highlightImageColors) && prevStyle.highlightLine === style.highlightLine && prevStyle.highlightSize === style.highlightSize && prevStyle.highlightThickness === style.highlightThickness && prevStyle.highlightOverflow === style.highlightOverflow) && group?.length && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top) && group[0].fontSize === fontSize) {
|
|
718
714
|
group.push(character);
|
|
719
715
|
} else {
|
|
720
716
|
group = [];
|
|
@@ -732,41 +728,117 @@ function highlight() {
|
|
|
732
728
|
};
|
|
733
729
|
}).forEach((group2) => {
|
|
734
730
|
const { char, groupBox } = group2;
|
|
735
|
-
const style = char
|
|
736
|
-
const {
|
|
731
|
+
const { computedStyle: style } = char;
|
|
732
|
+
const {
|
|
733
|
+
fontSize,
|
|
734
|
+
writingMode,
|
|
735
|
+
highlightThickness,
|
|
736
|
+
highlightSize,
|
|
737
|
+
highlightLine,
|
|
738
|
+
highlightOverflow,
|
|
739
|
+
highlightImage,
|
|
740
|
+
highlightImageColors
|
|
741
|
+
} = style;
|
|
737
742
|
const isVertical = writingMode.includes("vertical");
|
|
738
|
-
const
|
|
739
|
-
const charsPerRepeat = parseCharsPerRepeat(
|
|
740
|
-
const
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
743
|
+
const thickness = parseThickness(highlightThickness, fontSize, groupBox.width);
|
|
744
|
+
const charsPerRepeat = parseCharsPerRepeat(highlightSize, fontSize, groupBox.width);
|
|
745
|
+
const overflow = isNone(highlightOverflow) ? charsPerRepeat ? "hidden" : "visible" : highlightOverflow;
|
|
746
|
+
const colors = Object.keys(highlightImageColors).reduce((obj, key) => {
|
|
747
|
+
let value = highlightImageColors[key];
|
|
748
|
+
const keyRgb = hexToRgb(key);
|
|
749
|
+
const valueRgb = hexToRgb(value);
|
|
750
|
+
if (keyRgb) {
|
|
751
|
+
key = keyRgb;
|
|
752
|
+
}
|
|
753
|
+
if (valueRgb) {
|
|
754
|
+
value = valueRgb;
|
|
755
|
+
}
|
|
756
|
+
obj[key] = value;
|
|
757
|
+
return obj;
|
|
758
|
+
}, {});
|
|
759
|
+
const { paths: svgPaths, dom: svgDom } = getPaths(highlightImage);
|
|
760
|
+
const aBox = modernPath2d.getPathsBoundingBox(svgPaths, true);
|
|
761
|
+
const cBox = new modernPath2d.BoundingBox().copy(groupBox);
|
|
762
|
+
cBox.width = charsPerRepeat ? fontSize * charsPerRepeat : isVertical ? groupBox.height : groupBox.width;
|
|
763
|
+
cBox.height = isVertical ? groupBox.width : groupBox.height;
|
|
764
|
+
const width = isVertical ? cBox.height : cBox.width;
|
|
765
|
+
let line;
|
|
766
|
+
if (isNone(highlightLine)) {
|
|
767
|
+
if (aBox.width / aBox.height > 4) {
|
|
768
|
+
line = "underline";
|
|
769
|
+
const viewBox = svgDom.getAttribute("viewBox");
|
|
770
|
+
if (viewBox) {
|
|
771
|
+
const aCenter = aBox.y + aBox.height / 2;
|
|
772
|
+
const [_x, y, _w, h] = viewBox.split(" ").map((v) => Number(v));
|
|
773
|
+
const vCenter = y + h / 2;
|
|
774
|
+
const diff = vCenter - aCenter;
|
|
775
|
+
if (Math.abs(diff) < aBox.height * 2) {
|
|
776
|
+
line = "line-through";
|
|
777
|
+
} else if (diff > 0) {
|
|
778
|
+
line = "overline";
|
|
779
|
+
} else {
|
|
780
|
+
line = "underline";
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
} else {
|
|
784
|
+
line = "outline";
|
|
785
|
+
}
|
|
751
786
|
} else {
|
|
752
|
-
|
|
753
|
-
unitHeight = char.inlineBox.top - groupBox.top + char.underlinePosition;
|
|
787
|
+
line = highlightLine;
|
|
754
788
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
789
|
+
switch (line) {
|
|
790
|
+
case "outline": {
|
|
791
|
+
const paddingX = cBox.width * 0.2;
|
|
792
|
+
const paddingY = cBox.height * 0.2;
|
|
793
|
+
cBox.width += paddingX;
|
|
794
|
+
cBox.height += paddingY;
|
|
795
|
+
if (isVertical) {
|
|
796
|
+
cBox.x -= paddingY / 2;
|
|
797
|
+
cBox.y -= paddingX / 2;
|
|
798
|
+
cBox.x += cBox.height;
|
|
799
|
+
} else {
|
|
800
|
+
cBox.x -= paddingX / 2;
|
|
801
|
+
cBox.y -= paddingY / 2;
|
|
802
|
+
}
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
case "overline":
|
|
806
|
+
cBox.height = char.underlineThickness * 2;
|
|
807
|
+
if (isVertical) {
|
|
808
|
+
cBox.x = char.inlineBox.left + char.inlineBox.width - cBox.height;
|
|
809
|
+
} else {
|
|
810
|
+
cBox.y = char.inlineBox.top;
|
|
811
|
+
}
|
|
812
|
+
break;
|
|
813
|
+
case "line-through":
|
|
814
|
+
cBox.height = char.strikeoutSize * 2;
|
|
815
|
+
if (isVertical) {
|
|
816
|
+
cBox.x = char.inlineBox.left + char.inlineBox.width - char.strikeoutPosition - cBox.height;
|
|
817
|
+
} else {
|
|
818
|
+
cBox.y = char.inlineBox.top + char.strikeoutPosition;
|
|
819
|
+
}
|
|
820
|
+
break;
|
|
821
|
+
case "underline":
|
|
822
|
+
cBox.height = char.underlineThickness * 2;
|
|
823
|
+
if (isVertical) {
|
|
824
|
+
cBox.x = char.inlineBox.left + char.inlineBox.width - char.underlinePosition - cBox.height;
|
|
825
|
+
} else {
|
|
826
|
+
cBox.y = char.inlineBox.top + char.underlinePosition;
|
|
827
|
+
}
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
const transform = new modernPath2d.Matrix3().translate(-aBox.x, -aBox.y).scale(cBox.width / aBox.width, cBox.height / aBox.height);
|
|
831
|
+
if (isVertical) {
|
|
832
|
+
transform.rotate(-Math.PI / 2);
|
|
833
|
+
}
|
|
834
|
+
transform.translate(cBox.x, cBox.y);
|
|
835
|
+
const styleScale = fontSize / aBox.width;
|
|
836
|
+
for (let i = 0, len = Math.ceil(groupBox.width / width); i < len; i++) {
|
|
837
|
+
const _transform = transform.clone().translate(i * width, 0);
|
|
838
|
+
svgPaths.forEach((originalPath) => {
|
|
839
|
+
const path = originalPath.clone().matrix(_transform);
|
|
768
840
|
if (path.style.strokeWidth) {
|
|
769
|
-
path.style.strokeWidth *= styleScale *
|
|
841
|
+
path.style.strokeWidth *= styleScale * thickness;
|
|
770
842
|
}
|
|
771
843
|
if (path.style.strokeMiterlimit) {
|
|
772
844
|
path.style.strokeMiterlimit *= styleScale;
|
|
@@ -777,8 +849,14 @@ function highlight() {
|
|
|
777
849
|
if (path.style.strokeDasharray) {
|
|
778
850
|
path.style.strokeDasharray = path.style.strokeDasharray.map((v) => v * styleScale);
|
|
779
851
|
}
|
|
852
|
+
if (path.style.fill && path.style.fill in colors) {
|
|
853
|
+
path.style.fill = colors[path.style.fill];
|
|
854
|
+
}
|
|
855
|
+
if (path.style.stroke && path.style.stroke in colors) {
|
|
856
|
+
path.style.stroke = colors[path.style.stroke];
|
|
857
|
+
}
|
|
780
858
|
paths.push(path);
|
|
781
|
-
clipRects[paths.length - 1] =
|
|
859
|
+
clipRects[paths.length - 1] = overflow === "hidden" ? new modernPath2d.BoundingBox(
|
|
782
860
|
groupBox.left,
|
|
783
861
|
groupBox.top - groupBox.height,
|
|
784
862
|
groupBox.width,
|
|
@@ -797,6 +875,12 @@ function highlight() {
|
|
|
797
875
|
clipRect: clipRects[index],
|
|
798
876
|
fontSize: text.computedStyle.fontSize
|
|
799
877
|
});
|
|
878
|
+
if (text.debug) {
|
|
879
|
+
const box = modernPath2d.getPathsBoundingBox([path]);
|
|
880
|
+
if (box) {
|
|
881
|
+
ctx.strokeRect(box.x, box.y, box.width, box.height);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
800
884
|
});
|
|
801
885
|
}
|
|
802
886
|
});
|
|
@@ -829,17 +913,31 @@ function listStyle() {
|
|
|
829
913
|
const padding = fontSize * 0.45;
|
|
830
914
|
paragraphs.forEach((paragraph) => {
|
|
831
915
|
const { computedStyle: style } = paragraph;
|
|
832
|
-
|
|
916
|
+
const { listStyleImage, listStyleImageColors, listStyleSize, listStyleType, color } = style;
|
|
917
|
+
const colors = Object.keys(listStyleImageColors).reduce((obj, key) => {
|
|
918
|
+
let value = listStyleImageColors[key];
|
|
919
|
+
const keyRgb = hexToRgb(key);
|
|
920
|
+
const valueRgb = hexToRgb(value);
|
|
921
|
+
if (keyRgb) {
|
|
922
|
+
key = keyRgb;
|
|
923
|
+
}
|
|
924
|
+
if (valueRgb) {
|
|
925
|
+
value = valueRgb;
|
|
926
|
+
}
|
|
927
|
+
obj[key] = value;
|
|
928
|
+
return obj;
|
|
929
|
+
}, {});
|
|
930
|
+
let size = listStyleSize;
|
|
833
931
|
let image;
|
|
834
|
-
if (!isNone(
|
|
835
|
-
image =
|
|
836
|
-
} else if (!isNone(
|
|
932
|
+
if (!isNone(listStyleImage)) {
|
|
933
|
+
image = listStyleImage;
|
|
934
|
+
} else if (!isNone(listStyleType)) {
|
|
837
935
|
const r = fontSize * 0.38 / 2;
|
|
838
|
-
|
|
839
|
-
switch (
|
|
936
|
+
size = size === "cover" ? r * 2 : size;
|
|
937
|
+
switch (listStyleType) {
|
|
840
938
|
case "disc":
|
|
841
939
|
image = `<svg width="${r * 2}" height="${r * 2}" xmlns="http://www.w3.org/2000/svg">
|
|
842
|
-
<circle cx="${r}" cy="${r}" r="${r}" fill="${
|
|
940
|
+
<circle cx="${r}" cy="${r}" r="${r}" fill="${color}" />
|
|
843
941
|
</svg>`;
|
|
844
942
|
break;
|
|
845
943
|
}
|
|
@@ -854,7 +952,7 @@ function listStyle() {
|
|
|
854
952
|
if (fBox) {
|
|
855
953
|
const m = new modernPath2d.Matrix3();
|
|
856
954
|
if (isVertical) {
|
|
857
|
-
const scale = parseScale(
|
|
955
|
+
const scale = parseScale(size, fontSize, fontSize);
|
|
858
956
|
const reScale = fontSize / imageBox.height * scale;
|
|
859
957
|
m.translate(-imageBox.left, -imageBox.top);
|
|
860
958
|
m.rotate(Math.PI / 2);
|
|
@@ -862,7 +960,7 @@ function listStyle() {
|
|
|
862
960
|
m.translate(fontSize / 2 - imageBox.height * reScale / 2, 0);
|
|
863
961
|
m.translate(box.left + (box.width - fontSize) / 2, fBox.top - padding);
|
|
864
962
|
} else {
|
|
865
|
-
const scale = parseScale(
|
|
963
|
+
const scale = parseScale(size, fontSize, fontSize);
|
|
866
964
|
const reScale = fontSize / imageBox.height * scale;
|
|
867
965
|
m.translate(-imageBox.left, -imageBox.top);
|
|
868
966
|
m.translate(-imageBox.width, 0);
|
|
@@ -870,7 +968,17 @@ function listStyle() {
|
|
|
870
968
|
m.translate(0, fontSize / 2 - imageBox.height * reScale / 2);
|
|
871
969
|
m.translate(fBox.left - padding, box.top + (box.height - fontSize) / 2);
|
|
872
970
|
}
|
|
873
|
-
paths.push(...imagePaths.map((p) =>
|
|
971
|
+
paths.push(...imagePaths.map((p) => {
|
|
972
|
+
const path = p.clone();
|
|
973
|
+
path.matrix(m);
|
|
974
|
+
if (path.style.fill && path.style.fill in colors) {
|
|
975
|
+
path.style.fill = colors[path.style.fill];
|
|
976
|
+
}
|
|
977
|
+
if (path.style.stroke && path.style.stroke in colors) {
|
|
978
|
+
path.style.stroke = colors[path.style.stroke];
|
|
979
|
+
}
|
|
980
|
+
return path;
|
|
981
|
+
}));
|
|
874
982
|
}
|
|
875
983
|
});
|
|
876
984
|
}
|
|
@@ -953,6 +1061,11 @@ function render() {
|
|
|
953
1061
|
});
|
|
954
1062
|
});
|
|
955
1063
|
}
|
|
1064
|
+
if (text.debug) {
|
|
1065
|
+
paragraphs.forEach((paragraph) => {
|
|
1066
|
+
ctx.strokeRect(paragraph.lineBox.x, paragraph.lineBox.y, paragraph.lineBox.width, paragraph.lineBox.height);
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
956
1069
|
}
|
|
957
1070
|
});
|
|
958
1071
|
}
|
|
@@ -992,7 +1105,7 @@ function textDecoration() {
|
|
|
992
1105
|
const { computedStyle: style, isVertical, inlineBox, underlinePosition, underlineThickness, strikeoutPosition, strikeoutSize } = character;
|
|
993
1106
|
if (!isNone(style.textDecoration)) {
|
|
994
1107
|
let flag = false;
|
|
995
|
-
if (prevStyle?.textDecoration === style.textDecoration && prevStyle?.writingMode === style.writingMode && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top)) {
|
|
1108
|
+
if (prevStyle?.textDecoration === style.textDecoration && prevStyle?.writingMode === style.writingMode && prevStyle?.color === style.color && (isVertical ? group[0].inlineBox.left === inlineBox.left : group[0].inlineBox.top === inlineBox.top)) {
|
|
996
1109
|
switch (style.textDecoration) {
|
|
997
1110
|
case "underline":
|
|
998
1111
|
if (group[0].underlinePosition === underlinePosition && group[0].underlineThickness === underlineThickness) {
|
|
@@ -1020,38 +1133,48 @@ function textDecoration() {
|
|
|
1020
1133
|
});
|
|
1021
1134
|
groups.forEach((group2) => {
|
|
1022
1135
|
const { computedStyle: style, isVertical, underlinePosition, underlineThickness, strikeoutPosition, strikeoutSize } = group2[0];
|
|
1023
|
-
const { textDecoration: textDecoration2 } = style;
|
|
1136
|
+
const { textDecoration: textDecoration2, color } = style;
|
|
1024
1137
|
const { left, top, width, height } = modernPath2d.BoundingBox.from(...group2.map((c) => c.inlineBox));
|
|
1025
|
-
let
|
|
1026
|
-
|
|
1138
|
+
let position = isVertical ? left + width : top;
|
|
1139
|
+
const direction = isVertical ? -1 : 1;
|
|
1140
|
+
let thickness = 0;
|
|
1027
1141
|
switch (textDecoration2) {
|
|
1142
|
+
case "overline":
|
|
1143
|
+
thickness = underlineThickness * 2;
|
|
1144
|
+
break;
|
|
1028
1145
|
case "underline":
|
|
1029
|
-
|
|
1030
|
-
|
|
1146
|
+
position += direction * underlinePosition;
|
|
1147
|
+
thickness = underlineThickness * 2;
|
|
1031
1148
|
break;
|
|
1032
1149
|
case "line-through":
|
|
1033
|
-
|
|
1034
|
-
|
|
1150
|
+
position += direction * strikeoutPosition;
|
|
1151
|
+
thickness = strikeoutSize * 2;
|
|
1035
1152
|
break;
|
|
1036
1153
|
}
|
|
1037
|
-
|
|
1154
|
+
position -= thickness;
|
|
1155
|
+
let path;
|
|
1038
1156
|
if (isVertical) {
|
|
1039
|
-
|
|
1040
|
-
{ type: "M", x:
|
|
1041
|
-
{ type: "L", x:
|
|
1042
|
-
{ type: "L", x:
|
|
1043
|
-
{ type: "L", x:
|
|
1157
|
+
path = new modernPath2d.Path2D([
|
|
1158
|
+
{ type: "M", x: position, y: top },
|
|
1159
|
+
{ type: "L", x: position, y: top + height },
|
|
1160
|
+
{ type: "L", x: position + thickness, y: top + height },
|
|
1161
|
+
{ type: "L", x: position + thickness, y: top },
|
|
1044
1162
|
{ type: "Z" }
|
|
1045
|
-
]
|
|
1163
|
+
], {
|
|
1164
|
+
fill: color
|
|
1165
|
+
});
|
|
1046
1166
|
} else {
|
|
1047
|
-
|
|
1048
|
-
{ type: "M", x: left, y:
|
|
1049
|
-
{ type: "L", x: left + width, y:
|
|
1050
|
-
{ type: "L", x: left + width, y:
|
|
1051
|
-
{ type: "L", x: left, y:
|
|
1167
|
+
path = new modernPath2d.Path2D([
|
|
1168
|
+
{ type: "M", x: left, y: position },
|
|
1169
|
+
{ type: "L", x: left + width, y: position },
|
|
1170
|
+
{ type: "L", x: left + width, y: position + thickness },
|
|
1171
|
+
{ type: "L", x: left, y: position + thickness },
|
|
1052
1172
|
{ type: "Z" }
|
|
1053
|
-
]
|
|
1173
|
+
], {
|
|
1174
|
+
fill: color
|
|
1175
|
+
});
|
|
1054
1176
|
}
|
|
1177
|
+
paths.push(path);
|
|
1055
1178
|
});
|
|
1056
1179
|
},
|
|
1057
1180
|
render: (ctx, text) => {
|
|
@@ -1066,7 +1189,6 @@ function textDecoration() {
|
|
|
1066
1189
|
ctx,
|
|
1067
1190
|
path,
|
|
1068
1191
|
fontSize: style.fontSize,
|
|
1069
|
-
color: style.color,
|
|
1070
1192
|
...effectStyle
|
|
1071
1193
|
});
|
|
1072
1194
|
});
|
|
@@ -1077,8 +1199,7 @@ function textDecoration() {
|
|
|
1077
1199
|
drawPath({
|
|
1078
1200
|
ctx,
|
|
1079
1201
|
path,
|
|
1080
|
-
fontSize: style.fontSize
|
|
1081
|
-
color: style.color
|
|
1202
|
+
fontSize: style.fontSize
|
|
1082
1203
|
});
|
|
1083
1204
|
});
|
|
1084
1205
|
}
|
|
@@ -1119,13 +1240,15 @@ const defaultTextStyles = {
|
|
|
1119
1240
|
// listStyle
|
|
1120
1241
|
listStyleType: "none",
|
|
1121
1242
|
listStyleImage: "none",
|
|
1243
|
+
listStyleImageColors: {},
|
|
1122
1244
|
listStyleSize: "cover",
|
|
1123
1245
|
listStylePosition: "outside",
|
|
1124
1246
|
// highlight
|
|
1125
|
-
highlightReferImage: "none",
|
|
1126
1247
|
highlightImage: "none",
|
|
1248
|
+
highlightImageColors: {},
|
|
1249
|
+
highlightLine: "none",
|
|
1127
1250
|
highlightSize: "cover",
|
|
1128
|
-
|
|
1251
|
+
highlightThickness: "100%",
|
|
1129
1252
|
highlightOverflow: "none",
|
|
1130
1253
|
// shadow
|
|
1131
1254
|
shadowColor: "rgba(0, 0, 0, 0)",
|
|
@@ -1139,6 +1262,7 @@ const defaultTextStyles = {
|
|
|
1139
1262
|
};
|
|
1140
1263
|
class Text {
|
|
1141
1264
|
constructor(options = {}) {
|
|
1265
|
+
__publicField(this, "debug");
|
|
1142
1266
|
__publicField(this, "content");
|
|
1143
1267
|
__publicField(this, "style");
|
|
1144
1268
|
__publicField(this, "effects");
|
|
@@ -1154,12 +1278,12 @@ class Text {
|
|
|
1154
1278
|
__publicField(this, "measurer", new Measurer(this));
|
|
1155
1279
|
__publicField(this, "plugins", /* @__PURE__ */ new Map());
|
|
1156
1280
|
__publicField(this, "fonts");
|
|
1157
|
-
|
|
1158
|
-
this.content = content;
|
|
1159
|
-
this.style = style;
|
|
1160
|
-
this.measureDom = measureDom;
|
|
1161
|
-
this.effects = effects;
|
|
1162
|
-
this.fonts = fonts;
|
|
1281
|
+
this.debug = options.debug ?? false;
|
|
1282
|
+
this.content = options.content ?? "";
|
|
1283
|
+
this.style = options.style ?? {};
|
|
1284
|
+
this.measureDom = options.measureDom;
|
|
1285
|
+
this.effects = options.effects;
|
|
1286
|
+
this.fonts = options.fonts;
|
|
1163
1287
|
this.use(listStyle()).use(textDecoration()).use(highlight()).use(render());
|
|
1164
1288
|
}
|
|
1165
1289
|
get fontSize() {
|
|
@@ -1367,7 +1491,9 @@ exports.definePlugin = definePlugin;
|
|
|
1367
1491
|
exports.drawPath = drawPath;
|
|
1368
1492
|
exports.filterEmpty = filterEmpty;
|
|
1369
1493
|
exports.getTransform2D = getTransform2D;
|
|
1494
|
+
exports.hexToRgb = hexToRgb;
|
|
1370
1495
|
exports.highlight = highlight;
|
|
1496
|
+
exports.isEqualObject = isEqualObject;
|
|
1371
1497
|
exports.isNone = isNone;
|
|
1372
1498
|
exports.listStyle = listStyle;
|
|
1373
1499
|
exports.measureText = measureText;
|