modern-text 0.2.36 → 0.2.38
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 +204 -209
- package/dist/index.d.cts +12 -16
- package/dist/index.d.mts +12 -16
- package/dist/index.d.ts +12 -16
- package/dist/index.js +3 -3
- package/dist/index.mjs +205 -209
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -76,27 +76,6 @@ function drawPath(options) {
|
|
|
76
76
|
ctx.restore();
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
function fillBackground(ctx, text) {
|
|
80
|
-
const { computedStyle: style, paragraphs } = text;
|
|
81
|
-
function fillBackground2(color, x, y, width, height) {
|
|
82
|
-
ctx.fillStyle = color;
|
|
83
|
-
ctx.fillRect(x, y, width, height);
|
|
84
|
-
}
|
|
85
|
-
if (style?.backgroundColor) {
|
|
86
|
-
fillBackground2(style.backgroundColor, 0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
87
|
-
}
|
|
88
|
-
paragraphs.forEach((paragraph) => {
|
|
89
|
-
if (paragraph.style?.backgroundColor) {
|
|
90
|
-
fillBackground2(paragraph.computedStyle.backgroundColor, ...paragraph.boundingBox.toArray());
|
|
91
|
-
}
|
|
92
|
-
paragraph.fragments.forEach((fragment) => {
|
|
93
|
-
if (fragment.style?.backgroundColor) {
|
|
94
|
-
fillBackground2(fragment.computedStyle.backgroundColor, ...fragment.boundingBox.toArray());
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
79
|
function setupView(ctx, pixelRatio, boundingBox) {
|
|
101
80
|
const { left, top, width, height } = boundingBox;
|
|
102
81
|
const view = ctx.canvas;
|
|
@@ -117,9 +96,9 @@ function uploadColors(ctx, text) {
|
|
|
117
96
|
const { paragraphs, computedStyle: style, renderBoundingBox } = text;
|
|
118
97
|
uploadColor(style, renderBoundingBox, ctx);
|
|
119
98
|
paragraphs.forEach((paragraph) => {
|
|
120
|
-
uploadColor(paragraph.computedStyle, paragraph.
|
|
99
|
+
uploadColor(paragraph.computedStyle, paragraph.lineBox, ctx);
|
|
121
100
|
paragraph.fragments.forEach((fragment) => {
|
|
122
|
-
uploadColor(fragment.computedStyle, fragment.
|
|
101
|
+
uploadColor(fragment.computedStyle, fragment.inlineBox, ctx);
|
|
123
102
|
});
|
|
124
103
|
});
|
|
125
104
|
}
|
|
@@ -161,13 +140,9 @@ class Character {
|
|
|
161
140
|
this.content = content;
|
|
162
141
|
this.index = index;
|
|
163
142
|
this.parent = parent;
|
|
164
|
-
|
|
165
|
-
__publicField$3(this, "
|
|
166
|
-
__publicField$3(this, "
|
|
167
|
-
__publicField$3(this, "textHeight", 0);
|
|
168
|
-
// font glyph
|
|
169
|
-
__publicField$3(this, "glyphHeight", 0);
|
|
170
|
-
__publicField$3(this, "glyphWidth", 0);
|
|
143
|
+
__publicField$3(this, "lineBox", new modernPath2d.BoundingBox());
|
|
144
|
+
__publicField$3(this, "inlineBox", new modernPath2d.BoundingBox());
|
|
145
|
+
__publicField$3(this, "glyphBox", new modernPath2d.BoundingBox());
|
|
171
146
|
__publicField$3(this, "underlinePosition", 0);
|
|
172
147
|
__publicField$3(this, "underlineThickness", 0);
|
|
173
148
|
__publicField$3(this, "yStrikeoutPosition", 0);
|
|
@@ -175,7 +150,6 @@ class Character {
|
|
|
175
150
|
__publicField$3(this, "baseline", 0);
|
|
176
151
|
__publicField$3(this, "centerDiviation", 0);
|
|
177
152
|
__publicField$3(this, "path", new modernPath2d.Path2D());
|
|
178
|
-
__publicField$3(this, "glyphBox", new modernPath2d.BoundingBox());
|
|
179
153
|
__publicField$3(this, "center", new modernPath2d.Vector2());
|
|
180
154
|
}
|
|
181
155
|
get computedStyle() {
|
|
@@ -187,6 +161,9 @@ class Character {
|
|
|
187
161
|
get fontSize() {
|
|
188
162
|
return this.computedStyle.fontSize;
|
|
189
163
|
}
|
|
164
|
+
get fontHeight() {
|
|
165
|
+
return this.fontSize * this.computedStyle.lineHeight;
|
|
166
|
+
}
|
|
190
167
|
_font() {
|
|
191
168
|
const font = modernFont.fonts.get(this.computedStyle.fontFamily)?.font;
|
|
192
169
|
if (font instanceof modernFont.Woff || font instanceof modernFont.Ttf) {
|
|
@@ -199,25 +176,24 @@ class Character {
|
|
|
199
176
|
return this;
|
|
200
177
|
}
|
|
201
178
|
const { unitsPerEm, ascender, descender, os2, post } = font;
|
|
202
|
-
const { content, computedStyle
|
|
203
|
-
const { height } = boundingBox;
|
|
179
|
+
const { content, computedStyle } = this;
|
|
204
180
|
const { fontSize } = computedStyle;
|
|
205
181
|
const rate = unitsPerEm / fontSize;
|
|
206
|
-
const
|
|
207
|
-
const
|
|
182
|
+
const advanceWidth = font.getAdvanceWidth(content, fontSize);
|
|
183
|
+
const advanceHeight = (ascender + Math.abs(descender)) / rate;
|
|
208
184
|
const baseline = ascender / rate;
|
|
209
185
|
const yStrikeoutPosition = (ascender - os2.yStrikeoutPosition) / rate;
|
|
210
186
|
const yStrikeoutSize = os2.yStrikeoutSize / rate;
|
|
211
187
|
const underlinePosition = (ascender - post.underlinePosition) / rate;
|
|
212
188
|
const underlineThickness = post.underlineThickness / rate;
|
|
213
|
-
this.
|
|
214
|
-
this.
|
|
189
|
+
this.inlineBox.width = advanceWidth;
|
|
190
|
+
this.inlineBox.height = advanceHeight;
|
|
215
191
|
this.underlinePosition = underlinePosition;
|
|
216
192
|
this.underlineThickness = underlineThickness;
|
|
217
193
|
this.yStrikeoutPosition = yStrikeoutPosition;
|
|
218
194
|
this.yStrikeoutSize = yStrikeoutSize;
|
|
219
195
|
this.baseline = baseline;
|
|
220
|
-
this.centerDiviation =
|
|
196
|
+
this.centerDiviation = advanceHeight / 2 - baseline;
|
|
221
197
|
return this;
|
|
222
198
|
}
|
|
223
199
|
updatePath() {
|
|
@@ -228,45 +204,39 @@ class Character {
|
|
|
228
204
|
const {
|
|
229
205
|
isVertical,
|
|
230
206
|
content,
|
|
231
|
-
textWidth,
|
|
232
|
-
textHeight,
|
|
233
|
-
boundingBox,
|
|
234
207
|
computedStyle,
|
|
235
208
|
baseline,
|
|
236
|
-
|
|
237
|
-
glyphWidth
|
|
209
|
+
inlineBox
|
|
238
210
|
} = this.updateGlyph(font);
|
|
239
211
|
const { os2, ascender, descender } = font;
|
|
240
|
-
const usWinAscent = ascender;
|
|
241
|
-
const usWinDescent = descender;
|
|
242
212
|
const typoAscender = os2.sTypoAscender;
|
|
243
|
-
const { left, top } =
|
|
213
|
+
const { left, top } = inlineBox;
|
|
244
214
|
const { fontSize, fontStyle } = computedStyle;
|
|
245
215
|
let x = left;
|
|
246
216
|
let y = top + baseline;
|
|
247
217
|
let glyphIndex;
|
|
248
218
|
const path = new modernPath2d.Path2D();
|
|
249
219
|
if (isVertical) {
|
|
250
|
-
x += (
|
|
251
|
-
if (Math.abs(
|
|
252
|
-
y -= (
|
|
220
|
+
x += (inlineBox.height - inlineBox.width) / 2;
|
|
221
|
+
if (Math.abs(inlineBox.width - inlineBox.height) > 0.1) {
|
|
222
|
+
y -= (ascender - typoAscender) / (ascender + Math.abs(descender)) * inlineBox.height;
|
|
253
223
|
}
|
|
254
224
|
glyphIndex = void 0;
|
|
255
225
|
}
|
|
256
226
|
if (isVertical && !set1.has(content) && (content.codePointAt(0) <= 256 || set2.has(content))) {
|
|
257
227
|
path.addCommands(
|
|
258
|
-
font.getPathCommands(content, x, top + baseline - (
|
|
228
|
+
font.getPathCommands(content, x, top + baseline - (inlineBox.height - inlineBox.width) / 2, fontSize) ?? []
|
|
259
229
|
);
|
|
260
230
|
const point = {
|
|
261
|
-
y: top - (
|
|
262
|
-
x: x +
|
|
231
|
+
y: top - (inlineBox.height - inlineBox.width) / 2 + inlineBox.height / 2,
|
|
232
|
+
x: x + inlineBox.width / 2
|
|
263
233
|
};
|
|
264
234
|
if (fontStyle === "italic") {
|
|
265
235
|
this._italic(
|
|
266
236
|
path,
|
|
267
237
|
isVertical ? {
|
|
268
238
|
x: point.x,
|
|
269
|
-
y: top - (
|
|
239
|
+
y: top - (inlineBox.height - inlineBox.width) / 2 + baseline
|
|
270
240
|
} : void 0
|
|
271
241
|
);
|
|
272
242
|
}
|
|
@@ -280,8 +250,8 @@ class Character {
|
|
|
280
250
|
this._italic(
|
|
281
251
|
path,
|
|
282
252
|
isVertical ? {
|
|
283
|
-
x: x +
|
|
284
|
-
y: top + typoAscender / (
|
|
253
|
+
x: x + inlineBox.width / 2,
|
|
254
|
+
y: top + typoAscender / (ascender + Math.abs(descender)) * inlineBox.height
|
|
285
255
|
} : void 0
|
|
286
256
|
);
|
|
287
257
|
}
|
|
@@ -292,7 +262,7 @@ class Character {
|
|
|
292
262
|
if (fontStyle === "italic") {
|
|
293
263
|
this._italic(
|
|
294
264
|
path,
|
|
295
|
-
isVertical ? { x: x +
|
|
265
|
+
isVertical ? { x: x + inlineBox.height / 2, y } : void 0
|
|
296
266
|
);
|
|
297
267
|
}
|
|
298
268
|
}
|
|
@@ -319,7 +289,7 @@ class Character {
|
|
|
319
289
|
_decoration() {
|
|
320
290
|
const { isVertical, underlinePosition, yStrikeoutPosition } = this;
|
|
321
291
|
const { textDecoration, fontSize } = this.computedStyle;
|
|
322
|
-
const { left, top, width, height } = this.
|
|
292
|
+
const { left, top, width, height } = this.inlineBox;
|
|
323
293
|
const lineWidth = 0.1 * fontSize;
|
|
324
294
|
let start;
|
|
325
295
|
switch (textDecoration) {
|
|
@@ -361,8 +331,8 @@ class Character {
|
|
|
361
331
|
}
|
|
362
332
|
_italic(path, startPoint) {
|
|
363
333
|
path.skew(-0.24, 0, startPoint || {
|
|
364
|
-
y: this.
|
|
365
|
-
x: this.
|
|
334
|
+
y: this.inlineBox.top + this.baseline,
|
|
335
|
+
x: this.inlineBox.left + this.inlineBox.width / 2
|
|
366
336
|
});
|
|
367
337
|
}
|
|
368
338
|
getGlyphMinMax(min, max, withStyle) {
|
|
@@ -371,9 +341,7 @@ class Character {
|
|
|
371
341
|
} else {
|
|
372
342
|
min ?? (min = modernPath2d.Vector2.MAX);
|
|
373
343
|
max ?? (max = modernPath2d.Vector2.MIN);
|
|
374
|
-
const { left, top } = this.
|
|
375
|
-
const right = left + this.glyphWidth;
|
|
376
|
-
const bottom = top + this.glyphHeight;
|
|
344
|
+
const { left, top, right, bottom } = this.inlineBox;
|
|
377
345
|
min.x = Math.min(min.x, left);
|
|
378
346
|
min.y = Math.min(min.y, top);
|
|
379
347
|
max.x = Math.max(max.x, right);
|
|
@@ -422,7 +390,7 @@ class Fragment {
|
|
|
422
390
|
this.content = content;
|
|
423
391
|
this.style = style;
|
|
424
392
|
this.parent = parent;
|
|
425
|
-
__publicField$2(this, "
|
|
393
|
+
__publicField$2(this, "inlineBox", new modernPath2d.BoundingBox());
|
|
426
394
|
this.updateComputedStyle().initCharacters();
|
|
427
395
|
}
|
|
428
396
|
get computedContent() {
|
|
@@ -457,7 +425,7 @@ class Paragraph {
|
|
|
457
425
|
constructor(style, parentStyle) {
|
|
458
426
|
this.style = style;
|
|
459
427
|
this.parentStyle = parentStyle;
|
|
460
|
-
__publicField$1(this, "
|
|
428
|
+
__publicField$1(this, "lineBox", new modernPath2d.BoundingBox());
|
|
461
429
|
__publicField$1(this, "fragments", []);
|
|
462
430
|
this.updateComputedStyle();
|
|
463
431
|
}
|
|
@@ -559,7 +527,7 @@ class Measurer {
|
|
|
559
527
|
height: pBox.height
|
|
560
528
|
});
|
|
561
529
|
li.querySelectorAll("span").forEach((span, fragmentIndex) => {
|
|
562
|
-
const fBox =
|
|
530
|
+
const fBox = span.getBoundingClientRect();
|
|
563
531
|
fragments.push({
|
|
564
532
|
paragraphIndex,
|
|
565
533
|
fragmentIndex,
|
|
@@ -612,64 +580,43 @@ class Measurer {
|
|
|
612
580
|
measureDom(dom) {
|
|
613
581
|
const { paragraphs } = this._text;
|
|
614
582
|
const rect = dom.getBoundingClientRect();
|
|
615
|
-
const innerEl = dom.querySelector("ul");
|
|
616
|
-
const isVertical = window.getComputedStyle(dom).writingMode.includes("vertical");
|
|
617
|
-
const oldLineHeight = innerEl.style.lineHeight;
|
|
618
|
-
innerEl.style.lineHeight = "4000px";
|
|
619
|
-
const _paragraphs = [[]];
|
|
620
|
-
let fragments = _paragraphs[0];
|
|
621
|
-
const { characters: oldCharacters } = this._measureDom(dom);
|
|
622
|
-
if (oldCharacters.length > 0) {
|
|
623
|
-
fragments.push(oldCharacters[0]);
|
|
624
|
-
oldCharacters.reduce((prev, current) => {
|
|
625
|
-
const attr = isVertical ? "left" : "top";
|
|
626
|
-
if (Math.abs(current[attr] - prev[attr]) > 4e3 / 2) {
|
|
627
|
-
fragments = [];
|
|
628
|
-
_paragraphs.push(fragments);
|
|
629
|
-
}
|
|
630
|
-
fragments.push(current);
|
|
631
|
-
return current;
|
|
632
|
-
});
|
|
633
|
-
}
|
|
634
|
-
innerEl.style.lineHeight = oldLineHeight;
|
|
635
583
|
const measured = this._measureDom(dom);
|
|
636
584
|
measured.paragraphs.forEach((p) => {
|
|
637
585
|
const _p = paragraphs[p.paragraphIndex];
|
|
638
|
-
_p.
|
|
639
|
-
_p.
|
|
640
|
-
_p.
|
|
641
|
-
_p.
|
|
586
|
+
_p.lineBox.left = p.left - rect.left;
|
|
587
|
+
_p.lineBox.top = p.top - rect.top;
|
|
588
|
+
_p.lineBox.width = p.width;
|
|
589
|
+
_p.lineBox.height = p.height;
|
|
642
590
|
});
|
|
643
591
|
measured.fragments.forEach((f) => {
|
|
644
592
|
const _f = paragraphs[f.paragraphIndex].fragments[f.fragmentIndex];
|
|
645
|
-
_f.
|
|
646
|
-
_f.
|
|
647
|
-
_f.
|
|
648
|
-
_f.
|
|
593
|
+
_f.inlineBox.left = f.left - rect.left;
|
|
594
|
+
_f.inlineBox.top = f.top - rect.top;
|
|
595
|
+
_f.inlineBox.width = f.width;
|
|
596
|
+
_f.inlineBox.height = f.height;
|
|
649
597
|
});
|
|
650
598
|
const results = [];
|
|
651
599
|
let i = 0;
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
textWidth: oldCharacter.width,
|
|
660
|
-
textHeight: oldCharacter.height,
|
|
661
|
-
left: character.left - rect.left,
|
|
662
|
-
top: character.top - rect.top
|
|
663
|
-
});
|
|
664
|
-
const item = paragraphs[paragraphIndex].fragments[fragmentIndex].characters[characterIndex];
|
|
665
|
-
item.boundingBox.left = results[i].left;
|
|
666
|
-
item.boundingBox.top = results[i].top;
|
|
667
|
-
item.boundingBox.width = results[i].width;
|
|
668
|
-
item.boundingBox.height = results[i].height;
|
|
669
|
-
item.textWidth = results[i].textWidth;
|
|
670
|
-
item.textHeight = results[i].textHeight;
|
|
671
|
-
i++;
|
|
600
|
+
measured.characters.forEach((character) => {
|
|
601
|
+
const { paragraphIndex, fragmentIndex, characterIndex } = character;
|
|
602
|
+
results.push({
|
|
603
|
+
...character,
|
|
604
|
+
newParagraphIndex: paragraphIndex,
|
|
605
|
+
left: character.left - rect.left,
|
|
606
|
+
top: character.top - rect.top
|
|
672
607
|
});
|
|
608
|
+
const item = paragraphs[paragraphIndex].fragments[fragmentIndex].characters[characterIndex];
|
|
609
|
+
const result = results[i];
|
|
610
|
+
item.inlineBox.left = result.left;
|
|
611
|
+
item.inlineBox.top = result.top;
|
|
612
|
+
item.inlineBox.width = result.width;
|
|
613
|
+
item.inlineBox.height = result.height;
|
|
614
|
+
const fontHeight = item.fontHeight;
|
|
615
|
+
item.lineBox.left = result.left;
|
|
616
|
+
item.lineBox.top = result.top + (result.height - fontHeight) / 2;
|
|
617
|
+
item.lineBox.height = fontHeight;
|
|
618
|
+
item.lineBox.width = result.width;
|
|
619
|
+
i++;
|
|
673
620
|
});
|
|
674
621
|
return {
|
|
675
622
|
paragraphs,
|
|
@@ -746,82 +693,6 @@ function definePlugin(options) {
|
|
|
746
693
|
return options;
|
|
747
694
|
}
|
|
748
695
|
|
|
749
|
-
const tempV1 = new modernPath2d.Vector2();
|
|
750
|
-
const tempM1 = new modernPath2d.Matrix3();
|
|
751
|
-
const tempM2 = new modernPath2d.Matrix3();
|
|
752
|
-
function effect() {
|
|
753
|
-
return definePlugin({
|
|
754
|
-
name: "effect",
|
|
755
|
-
getBoundingBox: (text) => {
|
|
756
|
-
const { characters, fontSize, effects } = text;
|
|
757
|
-
const boxes = [];
|
|
758
|
-
characters.forEach((character) => {
|
|
759
|
-
effects?.forEach((style) => {
|
|
760
|
-
const aabb = character.glyphBox.clone();
|
|
761
|
-
const m = getTransform2D(text, style);
|
|
762
|
-
tempV1.set(aabb.left, aabb.top);
|
|
763
|
-
tempV1.applyMatrix3(m);
|
|
764
|
-
aabb.left = tempV1.x;
|
|
765
|
-
aabb.top = tempV1.y;
|
|
766
|
-
tempV1.set(aabb.right, aabb.bottom);
|
|
767
|
-
tempV1.applyMatrix3(m);
|
|
768
|
-
aabb.width = tempV1.x - aabb.left;
|
|
769
|
-
aabb.height = tempV1.y - aabb.top;
|
|
770
|
-
const shadowOffsetX = (style.shadowOffsetX ?? 0) * fontSize;
|
|
771
|
-
const shadowOffsetY = (style.shadowOffsetY ?? 0) * fontSize;
|
|
772
|
-
const textStrokeWidth = Math.max(0.1, style.textStrokeWidth ?? 0) * fontSize;
|
|
773
|
-
aabb.left += shadowOffsetX - textStrokeWidth;
|
|
774
|
-
aabb.top += shadowOffsetY - textStrokeWidth;
|
|
775
|
-
aabb.width += textStrokeWidth * 2;
|
|
776
|
-
aabb.height += textStrokeWidth * 2;
|
|
777
|
-
boxes.push(aabb);
|
|
778
|
-
});
|
|
779
|
-
});
|
|
780
|
-
return boxes.length ? modernPath2d.BoundingBox.from(...boxes) : void 0;
|
|
781
|
-
},
|
|
782
|
-
render: (ctx, text) => {
|
|
783
|
-
const { characters, renderBoundingBox, effects } = text;
|
|
784
|
-
if (effects) {
|
|
785
|
-
effects.forEach((style) => {
|
|
786
|
-
uploadColor(style, renderBoundingBox, ctx);
|
|
787
|
-
ctx.save();
|
|
788
|
-
const [a, c, e, b, d, f] = getTransform2D(text, style).transpose().elements;
|
|
789
|
-
ctx.transform(a, b, c, d, e, f);
|
|
790
|
-
characters.forEach((character) => {
|
|
791
|
-
character.drawTo(ctx, style);
|
|
792
|
-
});
|
|
793
|
-
ctx.restore();
|
|
794
|
-
});
|
|
795
|
-
} else {
|
|
796
|
-
characters.forEach((character) => {
|
|
797
|
-
character.drawTo(ctx);
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
function getTransform2D(text, style) {
|
|
804
|
-
const { fontSize, renderBoundingBox } = text;
|
|
805
|
-
const offsetX = (style.offsetX ?? 0) * fontSize;
|
|
806
|
-
const offsetY = (style.offsetY ?? 0) * fontSize;
|
|
807
|
-
const PI_2 = Math.PI * 2;
|
|
808
|
-
const skewX = (style.skewX ?? 0) / 360 * PI_2;
|
|
809
|
-
const skewY = (style.skewY ?? 0) / 360 * PI_2;
|
|
810
|
-
const { left, top, width, height } = renderBoundingBox;
|
|
811
|
-
const centerX = left + width / 2;
|
|
812
|
-
const centerY = top + height / 2;
|
|
813
|
-
tempM1.identity();
|
|
814
|
-
tempM2.makeTranslation(offsetX, offsetY);
|
|
815
|
-
tempM1.multiply(tempM2);
|
|
816
|
-
tempM2.makeTranslation(centerX, centerY);
|
|
817
|
-
tempM1.multiply(tempM2);
|
|
818
|
-
tempM2.set(1, Math.tan(skewX), 0, Math.tan(skewY), 1, 0, 0, 0, 1);
|
|
819
|
-
tempM1.multiply(tempM2);
|
|
820
|
-
tempM2.makeTranslation(-centerX, -centerY);
|
|
821
|
-
tempM1.multiply(tempM2);
|
|
822
|
-
return tempM1.clone();
|
|
823
|
-
}
|
|
824
|
-
|
|
825
696
|
const defaultReferImage = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiIgdmlld0JveD0iMCAwIDcyIDcyIiBmaWxsPSJub25lIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMyLjQwMjkgMjhIMzUuMTU5NFYzMy4xNzcxQzM1Ljk4MjEgMzIuMzExNSAzNi45NzEgMzEuODczNyAzOC4wOTQ4IDMxLjg3MzdDMzkuNjY3NiAzMS44NzM3IDQwLjkxNjYgMzIuNDI5NSA0MS44MzkgMzMuNTQzN0w0MS44NDAzIDMzLjU0NTNDNDIuNjcxNyAzNC41NzA1IDQzLjA5MTUgMzUuODU1OSA0My4wOTE1IDM3LjM4NzdDNDMuMDkxNSAzOC45NzYxIDQyLjY3MjkgNDAuMzAyOCA0MS44MTgzIDQxLjMzMDRMNDEuODE3MSA0MS4zMzE4QzQwLjg3MzEgNDIuNDQ2MSAzOS41ODMyIDQzIDM3Ljk3MjEgNDNDMzYuNzQ3NyA0MyAzNS43NDg4IDQyLjY1OTkgMzQuOTk1OCA0MS45NjkzVjQyLjcyNDdIMzIuNDAyOVYyOFpNMzcuNTQyOCAzNC4wOTI0QzM2Ljg1NDkgMzQuMDkyNCAzNi4zMDE0IDM0LjM1NjEgMzUuODQ4NyAzNC45MDA0TDM1Ljg0NTIgMzQuOTA0NkMzNS4zMzU4IDM1LjQ4NTMgMzUuMDc3NiAzNi4yOTc2IDM1LjA3NzYgMzcuMzQ4NFYzNy41MDU3QzM1LjA3NzYgMzguNDY0IDM1LjI3NzIgMzkuMjQ0MyAzNS42OTQzIDM5LjgyNzlDMzYuMTQ0MSA0MC40NTg3IDM2Ljc3MjYgNDAuNzgxMyAzNy42MjQ1IDQwLjc4MTNDMzguNTg3NCA0MC43ODEzIDM5LjI3MDcgNDAuNDUyNyAzOS43MTUyIDM5LjgxMjdDNDAuMDcyOCAzOS4yNjg0IDQwLjI3MzcgMzguNDY3MyA0MC4yNzM3IDM3LjM4NzdDNDAuMjczNyAzNi4zMTA1IDQwLjA1MzMgMzUuNTMxMyAzOS42NzgzIDM1LjAwNzdDMzkuMjM3MSAzNC40MDcxIDM4LjUzNDIgMzQuMDkyNCAzNy41NDI4IDM0LjA5MjRaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZD0iTTQ5Ljg2MTQgMzEuODczN0M0OC4xNTM1IDMxLjg3MzcgNDYuODAxNiAzMi40MjM5IDQ1LjgzNDggMzMuNTM5MkM0NC45MzcgMzQuNTQ3MiA0NC40OTY2IDM1Ljg1NiA0NC40OTY2IDM3LjQyN0M0NC40OTY2IDM5LjAzNjggNDQuOTM2NyA0MC4zNjU5IDQ1Ljg1NTkgNDEuMzk0M0M0Ni44MDMxIDQyLjQ3MDYgNDguMTM0OCA0MyA0OS44MjA1IDQzQzUxLjIyNiA0MyA1Mi4zODI2IDQyLjY1NjMgNTMuMjQ3OSA0MS45Njk3QzU0LjEzNTkgNDEuMjYxNCA1NC43MDYxIDQwLjE4ODcgNTQuOTU3MyAzOC43NzkxTDU1IDM4LjUzOTdINTIuMjQ4NEw1Mi4yMjU5IDM4LjcyMDFDNTIuMTM3OSAzOS40MjUxIDUxLjg5MjUgMzkuOTI3OCA1MS41MTA5IDQwLjI1NThDNTEuMTI5NSA0MC41ODM1IDUwLjU4MzEgNDAuNzYxNiA0OS44NDA5IDQwLjc2MTZDNDkuMDAwMSA0MC43NjE2IDQ4LjM5NDkgNDAuNDcxNSA0Ny45OTA3IDM5LjkyMzdMNDcuOTg3NCAzOS45MTk0QzQ3LjUzNTYgMzkuMzQwMSA0Ny4zMTQ0IDM4LjUwNjIgNDcuMzE0NCAzNy40MDc0QzQ3LjMxNDQgMzYuMzMyMiA0Ny41NTQ0IDM1LjUxNzcgNDguMDA1OCAzNC45NTY4TDQ4LjAwNzggMzQuOTU0M0M0OC40NTM3IDM0LjM4MjUgNDkuMDYxOCAzNC4xMTIxIDQ5Ljg2MTQgMzQuMTEyMUM1MC41MjMgMzQuMTEyMSA1MS4wNDUxIDM0LjI2MTUgNTEuNDI3MiAzNC41NDA3QzUxLjc4ODQgMzQuODE5NCA1Mi4wNTMgMzUuMjQ0NyA1Mi4xODgxIDM1Ljg1NzFMNTIuMjIzOSAzNi4wMTk0SDU0Ljk1NDhMNTQuOTE3IDM1Ljc4MzVDNTQuNzA2MyAzNC40NjYgNTQuMTUzNiAzMy40NzAxIDUzLjI2MzQgMzIuODAxOUw1My4yNjAyIDMyLjc5OTVDNTIuMzk1MSAzMi4xNzU1IDUxLjI2MjEgMzEuODczNyA0OS44NjE0IDMxLjg3MzdaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNS43NTYxIDI4LjI3NTNIMjIuNzQ0TDE3IDQyLjcyNDdIMjAuMDE0MUwyMS4zNDI5IDM5LjIwNDlIMjcuMTU3MkwyOC40ODYgNDIuNzI0N0gzMS41MDAxTDI1Ljc1NjEgMjguMjc1M1pNMjIuMjEyNSAzNi45MDc2TDI0LjI1OTYgMzEuNDUzOUwyNi4yODg1IDM2LjkwNzZIMjIuMjEyNVoiIGZpbGw9IiMyMjI1MjkiLz48L3N2Zz4=";
|
|
826
697
|
function parseCharsPerRepeat(size, fontSize, total) {
|
|
827
698
|
if (size === "cover") {
|
|
@@ -853,17 +724,35 @@ function parseStrokeWidthScale(strokeWidth, fontSize, total) {
|
|
|
853
724
|
return strokeWidth / total;
|
|
854
725
|
}
|
|
855
726
|
}
|
|
856
|
-
function getTransformMatrix(a, b, c) {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
727
|
+
function getTransformMatrix(a, b, c, isVertical) {
|
|
728
|
+
let scale;
|
|
729
|
+
if (!isVertical) {
|
|
730
|
+
scale = {
|
|
731
|
+
x: c.width / b.width,
|
|
732
|
+
y: c.height / b.height
|
|
733
|
+
};
|
|
734
|
+
} else {
|
|
735
|
+
scale = {
|
|
736
|
+
x: c.width / b.height,
|
|
737
|
+
y: c.height / b.width
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
const offset = c.getCenterPoint().add(
|
|
741
|
+
a.getCenterPoint().sub(b.getCenterPoint()).scale(scale.x, scale.y)
|
|
742
|
+
).sub({
|
|
743
|
+
x: a.width / 2 * scale.x,
|
|
744
|
+
y: a.height / 2 * scale.y
|
|
745
|
+
});
|
|
746
|
+
const m = new modernPath2d.Matrix3();
|
|
747
|
+
m.translate(-a.left, -a.top);
|
|
748
|
+
if (isVertical) {
|
|
749
|
+
m.translate(-a.width / 2, -a.height / 2);
|
|
750
|
+
m.rotate(Math.PI / 2);
|
|
751
|
+
m.translate(a.width / 2, a.height / 2);
|
|
752
|
+
}
|
|
753
|
+
m.scale(scale.x, scale.y);
|
|
754
|
+
m.translate(offset.x, offset.y);
|
|
755
|
+
return m;
|
|
867
756
|
}
|
|
868
757
|
function highlight() {
|
|
869
758
|
const paths = [];
|
|
@@ -889,7 +778,7 @@ function highlight() {
|
|
|
889
778
|
characters.forEach((character) => {
|
|
890
779
|
const { isVertical, computedStyle: style } = character;
|
|
891
780
|
if (!isNone(style.highlightImage)) {
|
|
892
|
-
if (style.highlightSize !== "1rem" && prevStyle?.highlightImage === style.highlightImage && prevStyle?.highlightSize === style.highlightSize && prevStyle?.highlightStrokeWidth === style.highlightStrokeWidth && prevStyle?.highlightOverflow === style.highlightOverflow && group.length && (isVertical ? group[0].
|
|
781
|
+
if (style.highlightSize !== "1rem" && prevStyle?.highlightImage === style.highlightImage && prevStyle?.highlightSize === style.highlightSize && prevStyle?.highlightStrokeWidth === style.highlightStrokeWidth && prevStyle?.highlightOverflow === style.highlightOverflow && group.length && (isVertical ? group[0].inlineBox.left === character.inlineBox.left : group[0].inlineBox.top === character.inlineBox.top) && group[0].fontSize === character.fontSize) {
|
|
893
782
|
group.push(character);
|
|
894
783
|
} else {
|
|
895
784
|
group = [];
|
|
@@ -900,13 +789,16 @@ function highlight() {
|
|
|
900
789
|
prevStyle = style;
|
|
901
790
|
});
|
|
902
791
|
groups.filter((characters2) => characters2.length).map((characters2) => {
|
|
792
|
+
const char = characters2[0];
|
|
903
793
|
return {
|
|
904
|
-
style:
|
|
794
|
+
style: char.computedStyle,
|
|
795
|
+
baseline: char.baseline,
|
|
905
796
|
box: modernPath2d.BoundingBox.from(...characters2.map((c) => c.glyphBox))
|
|
906
797
|
};
|
|
907
798
|
}).forEach((group2) => {
|
|
908
|
-
const { style, box: groupBox } = group2;
|
|
909
|
-
const { fontSize } = style;
|
|
799
|
+
const { style, box: groupBox, baseline } = group2;
|
|
800
|
+
const { fontSize, writingMode } = style;
|
|
801
|
+
const isVertical = writingMode.includes("vertical");
|
|
910
802
|
const strokeWidthScale = parseStrokeWidthScale(style.highlightStrokeWidth, fontSize, groupBox.width);
|
|
911
803
|
const charsPerRepeat = parseCharsPerRepeat(style.highlightSize, fontSize, groupBox.width);
|
|
912
804
|
const highlightOverflow = isNone(style.highlightOverflow) ? charsPerRepeat ? "hidden" : "visible" : style.highlightOverflow;
|
|
@@ -914,8 +806,14 @@ function highlight() {
|
|
|
914
806
|
const svgPaths = getPaths(style.highlightImage);
|
|
915
807
|
const box = modernPath2d.getPathsBoundingBox(svgPaths, true);
|
|
916
808
|
const refBox = modernPath2d.getPathsBoundingBox(refPaths, false);
|
|
917
|
-
const
|
|
918
|
-
const
|
|
809
|
+
const unitWidth = charsPerRepeat ? fontSize * charsPerRepeat : isVertical ? groupBox.height : groupBox.width;
|
|
810
|
+
const unitHeight = baseline * 0.8;
|
|
811
|
+
const transform = getTransformMatrix(
|
|
812
|
+
box,
|
|
813
|
+
refBox,
|
|
814
|
+
new modernPath2d.BoundingBox(groupBox.left, groupBox.top, isVertical ? unitHeight : unitWidth, isVertical ? unitWidth : unitHeight),
|
|
815
|
+
isVertical
|
|
816
|
+
);
|
|
919
817
|
const styleScale = fontSize / box.width * 2;
|
|
920
818
|
const total = Math.ceil(groupBox.width / unitWidth);
|
|
921
819
|
for (let i = 0; i < total; i++) {
|
|
@@ -1032,6 +930,104 @@ function listStyle() {
|
|
|
1032
930
|
});
|
|
1033
931
|
}
|
|
1034
932
|
|
|
933
|
+
const tempV1 = new modernPath2d.Vector2();
|
|
934
|
+
const tempM1 = new modernPath2d.Matrix3();
|
|
935
|
+
const tempM2 = new modernPath2d.Matrix3();
|
|
936
|
+
function render() {
|
|
937
|
+
return definePlugin({
|
|
938
|
+
name: "render",
|
|
939
|
+
getBoundingBox: (text) => {
|
|
940
|
+
const { characters, fontSize, effects } = text;
|
|
941
|
+
const boxes = [];
|
|
942
|
+
characters.forEach((character) => {
|
|
943
|
+
effects?.forEach((style) => {
|
|
944
|
+
const aabb = character.glyphBox.clone();
|
|
945
|
+
const m = getTransform2D(text, style);
|
|
946
|
+
tempV1.set(aabb.left, aabb.top);
|
|
947
|
+
tempV1.applyMatrix3(m);
|
|
948
|
+
aabb.left = tempV1.x;
|
|
949
|
+
aabb.top = tempV1.y;
|
|
950
|
+
tempV1.set(aabb.right, aabb.bottom);
|
|
951
|
+
tempV1.applyMatrix3(m);
|
|
952
|
+
aabb.width = tempV1.x - aabb.left;
|
|
953
|
+
aabb.height = tempV1.y - aabb.top;
|
|
954
|
+
const shadowOffsetX = (style.shadowOffsetX ?? 0) * fontSize;
|
|
955
|
+
const shadowOffsetY = (style.shadowOffsetY ?? 0) * fontSize;
|
|
956
|
+
const textStrokeWidth = Math.max(0.1, style.textStrokeWidth ?? 0) * fontSize;
|
|
957
|
+
aabb.left += shadowOffsetX - textStrokeWidth;
|
|
958
|
+
aabb.top += shadowOffsetY - textStrokeWidth;
|
|
959
|
+
aabb.width += textStrokeWidth * 2;
|
|
960
|
+
aabb.height += textStrokeWidth * 2;
|
|
961
|
+
boxes.push(aabb);
|
|
962
|
+
});
|
|
963
|
+
});
|
|
964
|
+
return boxes.length ? modernPath2d.BoundingBox.from(...boxes) : void 0;
|
|
965
|
+
},
|
|
966
|
+
render: (ctx, text) => {
|
|
967
|
+
const { characters, paragraphs, renderBoundingBox, effects, style } = text;
|
|
968
|
+
function fillBackground(color, box) {
|
|
969
|
+
ctx.fillStyle = color;
|
|
970
|
+
ctx.fillRect(box.left, box.top, box.width, box.height);
|
|
971
|
+
}
|
|
972
|
+
if (style?.backgroundColor) {
|
|
973
|
+
fillBackground(style.backgroundColor, new modernPath2d.BoundingBox(0, 0, ctx.canvas.width, ctx.canvas.height));
|
|
974
|
+
}
|
|
975
|
+
paragraphs.forEach((paragraph) => {
|
|
976
|
+
if (paragraph.style?.backgroundColor) {
|
|
977
|
+
fillBackground(paragraph.style.backgroundColor, paragraph.lineBox);
|
|
978
|
+
}
|
|
979
|
+
});
|
|
980
|
+
if (effects) {
|
|
981
|
+
effects.forEach((style2) => {
|
|
982
|
+
uploadColor(style2, renderBoundingBox, ctx);
|
|
983
|
+
ctx.save();
|
|
984
|
+
const [a, c, e, b, d, f] = getTransform2D(text, style2).transpose().elements;
|
|
985
|
+
ctx.transform(a, b, c, d, e, f);
|
|
986
|
+
characters.forEach((character) => {
|
|
987
|
+
if (character.parent.style?.backgroundColor) {
|
|
988
|
+
fillBackground(character.parent.style.backgroundColor, character.inlineBox);
|
|
989
|
+
}
|
|
990
|
+
character.drawTo(ctx, style2);
|
|
991
|
+
});
|
|
992
|
+
ctx.restore();
|
|
993
|
+
});
|
|
994
|
+
} else {
|
|
995
|
+
paragraphs.forEach((paragraph) => {
|
|
996
|
+
paragraph.fragments.forEach((fragment) => {
|
|
997
|
+
if (fragment.style?.backgroundColor) {
|
|
998
|
+
fillBackground(fragment.computedStyle.backgroundColor, fragment.inlineBox);
|
|
999
|
+
}
|
|
1000
|
+
fragment.characters.forEach((character) => {
|
|
1001
|
+
character.drawTo(ctx);
|
|
1002
|
+
});
|
|
1003
|
+
});
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
function getTransform2D(text, style) {
|
|
1010
|
+
const { fontSize, renderBoundingBox } = text;
|
|
1011
|
+
const offsetX = (style.offsetX ?? 0) * fontSize;
|
|
1012
|
+
const offsetY = (style.offsetY ?? 0) * fontSize;
|
|
1013
|
+
const PI_2 = Math.PI * 2;
|
|
1014
|
+
const skewX = (style.skewX ?? 0) / 360 * PI_2;
|
|
1015
|
+
const skewY = (style.skewY ?? 0) / 360 * PI_2;
|
|
1016
|
+
const { left, top, width, height } = renderBoundingBox;
|
|
1017
|
+
const centerX = left + width / 2;
|
|
1018
|
+
const centerY = top + height / 2;
|
|
1019
|
+
tempM1.identity();
|
|
1020
|
+
tempM2.makeTranslation(offsetX, offsetY);
|
|
1021
|
+
tempM1.multiply(tempM2);
|
|
1022
|
+
tempM2.makeTranslation(centerX, centerY);
|
|
1023
|
+
tempM1.multiply(tempM2);
|
|
1024
|
+
tempM2.set(1, Math.tan(skewX), 0, Math.tan(skewY), 1, 0, 0, 0, 1);
|
|
1025
|
+
tempM1.multiply(tempM2);
|
|
1026
|
+
tempM2.makeTranslation(-centerX, -centerY);
|
|
1027
|
+
tempM1.multiply(tempM2);
|
|
1028
|
+
return tempM1.clone();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1035
1031
|
var __defProp = Object.defineProperty;
|
|
1036
1032
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1037
1033
|
var __publicField = (obj, key, value) => {
|
|
@@ -1098,7 +1094,7 @@ class Text {
|
|
|
1098
1094
|
this.style = style;
|
|
1099
1095
|
this.measureDom = measureDom;
|
|
1100
1096
|
this.effects = effects;
|
|
1101
|
-
this.use(
|
|
1097
|
+
this.use(render()).use(highlight()).use(listStyle());
|
|
1102
1098
|
}
|
|
1103
1099
|
get fontSize() {
|
|
1104
1100
|
return this.computedStyle.fontSize;
|
|
@@ -1188,14 +1184,13 @@ exports.Text = Text;
|
|
|
1188
1184
|
exports.defaultTextStyles = defaultTextStyles;
|
|
1189
1185
|
exports.definePlugin = definePlugin;
|
|
1190
1186
|
exports.drawPath = drawPath;
|
|
1191
|
-
exports.effect = effect;
|
|
1192
|
-
exports.fillBackground = fillBackground;
|
|
1193
1187
|
exports.filterEmpty = filterEmpty;
|
|
1194
1188
|
exports.getTransform2D = getTransform2D;
|
|
1195
1189
|
exports.highlight = highlight;
|
|
1196
1190
|
exports.isNone = isNone;
|
|
1197
1191
|
exports.listStyle = listStyle;
|
|
1198
1192
|
exports.parseColor = parseColor;
|
|
1193
|
+
exports.render = render;
|
|
1199
1194
|
exports.setupView = setupView;
|
|
1200
1195
|
exports.uploadColor = uploadColor;
|
|
1201
1196
|
exports.uploadColors = uploadColors;
|