modern-text 0.2.30 → 0.2.32
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 +423 -369
- package/dist/index.d.cts +141 -155
- package/dist/index.d.mts +141 -155
- package/dist/index.d.ts +141 -155
- package/dist/index.js +4 -2
- package/dist/index.mjs +414 -365
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { BoundingBox, Path2D, Vector2, Matrix3, parseSvg, parseSvgToDom } from 'modern-path2d';
|
|
1
|
+
import { BoundingBox, Path2D, Vector2, Matrix3, parseSvg, parseSvgToDom, getPathsBoundingBox } from 'modern-path2d';
|
|
2
|
+
export * from 'modern-path2d';
|
|
2
3
|
import { fonts, Woff, Ttf } from 'modern-font';
|
|
3
4
|
export * from 'modern-font';
|
|
4
5
|
|
|
@@ -75,10 +76,58 @@ function drawPath(options) {
|
|
|
75
76
|
ctx.restore();
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
function setupView(ctx, pixelRatio, boundingBox) {
|
|
101
|
+
const { left, top, width, height } = boundingBox;
|
|
102
|
+
const view = ctx.canvas;
|
|
103
|
+
view.dataset.viewbox = String(`${left} ${top} ${width} ${height}`);
|
|
104
|
+
view.dataset.pixelRatio = String(pixelRatio);
|
|
105
|
+
view.width = Math.max(1, Math.ceil(width * pixelRatio));
|
|
106
|
+
view.height = Math.max(1, Math.ceil(height * pixelRatio));
|
|
107
|
+
view.style.marginTop = `${top}px`;
|
|
108
|
+
view.style.marginLeft = `${left}px`;
|
|
109
|
+
view.style.width = `${width}px`;
|
|
110
|
+
view.style.height = `${height}px`;
|
|
111
|
+
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
112
|
+
ctx.scale(pixelRatio, pixelRatio);
|
|
113
|
+
ctx.translate(-left, -top);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function uploadColors(ctx, text) {
|
|
117
|
+
const { paragraphs, computedStyle: style, renderBoundingBox } = text;
|
|
118
|
+
uploadColor(style, renderBoundingBox, ctx);
|
|
119
|
+
paragraphs.forEach((paragraph) => {
|
|
120
|
+
uploadColor(paragraph.computedStyle, paragraph.boundingBox, ctx);
|
|
121
|
+
paragraph.fragments.forEach((fragment) => {
|
|
122
|
+
uploadColor(fragment.computedStyle, fragment.boundingBox, ctx);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
var __defProp$3 = Object.defineProperty;
|
|
128
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
129
|
+
var __publicField$3 = (obj, key, value) => {
|
|
130
|
+
__defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
82
131
|
return value;
|
|
83
132
|
};
|
|
84
133
|
const set1 = /* @__PURE__ */ new Set(["\xA9", "\xAE", "\xF7"]);
|
|
@@ -113,21 +162,21 @@ class Character {
|
|
|
113
162
|
this.index = index;
|
|
114
163
|
this.parent = parent;
|
|
115
164
|
// measure dom
|
|
116
|
-
__publicField$
|
|
117
|
-
__publicField$
|
|
118
|
-
__publicField$
|
|
165
|
+
__publicField$3(this, "boundingBox", new BoundingBox());
|
|
166
|
+
__publicField$3(this, "textWidth", 0);
|
|
167
|
+
__publicField$3(this, "textHeight", 0);
|
|
119
168
|
// font glyph
|
|
120
|
-
__publicField$
|
|
121
|
-
__publicField$
|
|
122
|
-
__publicField$
|
|
123
|
-
__publicField$
|
|
124
|
-
__publicField$
|
|
125
|
-
__publicField$
|
|
126
|
-
__publicField$
|
|
127
|
-
__publicField$
|
|
128
|
-
__publicField$
|
|
129
|
-
__publicField$
|
|
130
|
-
__publicField$
|
|
169
|
+
__publicField$3(this, "glyphHeight", 0);
|
|
170
|
+
__publicField$3(this, "glyphWidth", 0);
|
|
171
|
+
__publicField$3(this, "underlinePosition", 0);
|
|
172
|
+
__publicField$3(this, "underlineThickness", 0);
|
|
173
|
+
__publicField$3(this, "yStrikeoutPosition", 0);
|
|
174
|
+
__publicField$3(this, "yStrikeoutSize", 0);
|
|
175
|
+
__publicField$3(this, "baseline", 0);
|
|
176
|
+
__publicField$3(this, "centerDiviation", 0);
|
|
177
|
+
__publicField$3(this, "path", new Path2D());
|
|
178
|
+
__publicField$3(this, "glyphBox", new BoundingBox());
|
|
179
|
+
__publicField$3(this, "center", new Vector2());
|
|
131
180
|
}
|
|
132
181
|
get computedStyle() {
|
|
133
182
|
return this.parent.computedStyle;
|
|
@@ -319,10 +368,6 @@ class Character {
|
|
|
319
368
|
} else {
|
|
320
369
|
min ?? (min = Vector2.MAX);
|
|
321
370
|
max ?? (max = Vector2.MIN);
|
|
322
|
-
min.x = Math.min(min.x, this.boundingBox.left);
|
|
323
|
-
min.y = Math.min(min.y, this.boundingBox.top);
|
|
324
|
-
max.x = Math.max(max.x, this.boundingBox.right);
|
|
325
|
-
max.y = Math.max(max.y, this.boundingBox.bottom);
|
|
326
371
|
return { min, max };
|
|
327
372
|
}
|
|
328
373
|
}
|
|
@@ -341,6 +386,9 @@ class Character {
|
|
|
341
386
|
}
|
|
342
387
|
}
|
|
343
388
|
|
|
389
|
+
function isNone(val) {
|
|
390
|
+
return !val || val === "none";
|
|
391
|
+
}
|
|
344
392
|
function filterEmpty(val) {
|
|
345
393
|
if (!val)
|
|
346
394
|
return val;
|
|
@@ -353,10 +401,10 @@ function filterEmpty(val) {
|
|
|
353
401
|
return res;
|
|
354
402
|
}
|
|
355
403
|
|
|
356
|
-
var __defProp$
|
|
357
|
-
var __defNormalProp$
|
|
358
|
-
var __publicField$
|
|
359
|
-
__defNormalProp$
|
|
404
|
+
var __defProp$2 = Object.defineProperty;
|
|
405
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
406
|
+
var __publicField$2 = (obj, key, value) => {
|
|
407
|
+
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
360
408
|
return value;
|
|
361
409
|
};
|
|
362
410
|
class Fragment {
|
|
@@ -364,8 +412,7 @@ class Fragment {
|
|
|
364
412
|
this.content = content;
|
|
365
413
|
this.style = style;
|
|
366
414
|
this.parent = parent;
|
|
367
|
-
__publicField$
|
|
368
|
-
__publicField$3(this, "highlight");
|
|
415
|
+
__publicField$2(this, "boundingBox", new BoundingBox());
|
|
369
416
|
this.updateComputedStyle().initCharacters();
|
|
370
417
|
}
|
|
371
418
|
get computedContent() {
|
|
@@ -390,18 +437,18 @@ class Fragment {
|
|
|
390
437
|
}
|
|
391
438
|
}
|
|
392
439
|
|
|
393
|
-
var __defProp$
|
|
394
|
-
var __defNormalProp$
|
|
395
|
-
var __publicField$
|
|
396
|
-
__defNormalProp$
|
|
440
|
+
var __defProp$1 = Object.defineProperty;
|
|
441
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
442
|
+
var __publicField$1 = (obj, key, value) => {
|
|
443
|
+
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
397
444
|
return value;
|
|
398
445
|
};
|
|
399
446
|
class Paragraph {
|
|
400
447
|
constructor(style, parentStyle) {
|
|
401
448
|
this.style = style;
|
|
402
449
|
this.parentStyle = parentStyle;
|
|
403
|
-
__publicField$
|
|
404
|
-
__publicField$
|
|
450
|
+
__publicField$1(this, "boundingBox", new BoundingBox());
|
|
451
|
+
__publicField$1(this, "fragments", []);
|
|
405
452
|
this.updateComputedStyle();
|
|
406
453
|
}
|
|
407
454
|
updateComputedStyle() {
|
|
@@ -418,231 +465,10 @@ class Paragraph {
|
|
|
418
465
|
}
|
|
419
466
|
}
|
|
420
467
|
|
|
421
|
-
class
|
|
468
|
+
class Measurer {
|
|
422
469
|
constructor(_text) {
|
|
423
470
|
this._text = _text;
|
|
424
471
|
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
class Deformer extends Feature {
|
|
428
|
-
deform() {
|
|
429
|
-
this._text.deformation?.();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const tempM1 = new Matrix3();
|
|
434
|
-
const tempM2 = new Matrix3();
|
|
435
|
-
const tempV1 = new Vector2();
|
|
436
|
-
class Effector extends Feature {
|
|
437
|
-
getTransform2D(style) {
|
|
438
|
-
const { fontSize, renderBoundingBox } = this._text;
|
|
439
|
-
const offsetX = (style.offsetX ?? 0) * fontSize;
|
|
440
|
-
const offsetY = (style.offsetY ?? 0) * fontSize;
|
|
441
|
-
const PI_2 = Math.PI * 2;
|
|
442
|
-
const skewX = (style.skewX ?? 0) / 360 * PI_2;
|
|
443
|
-
const skewY = (style.skewY ?? 0) / 360 * PI_2;
|
|
444
|
-
const { left, top, width, height } = renderBoundingBox;
|
|
445
|
-
const centerX = left + width / 2;
|
|
446
|
-
const centerY = top + height / 2;
|
|
447
|
-
tempM1.identity();
|
|
448
|
-
tempM2.makeTranslation(offsetX, offsetY);
|
|
449
|
-
tempM1.multiply(tempM2);
|
|
450
|
-
tempM2.makeTranslation(centerX, centerY);
|
|
451
|
-
tempM1.multiply(tempM2);
|
|
452
|
-
tempM2.set(1, Math.tan(skewX), 0, Math.tan(skewY), 1, 0, 0, 0, 1);
|
|
453
|
-
tempM1.multiply(tempM2);
|
|
454
|
-
tempM2.makeTranslation(-centerX, -centerY);
|
|
455
|
-
tempM1.multiply(tempM2);
|
|
456
|
-
return tempM1.clone();
|
|
457
|
-
}
|
|
458
|
-
getBoundingBox() {
|
|
459
|
-
const { characters, effects, fontSize } = this._text;
|
|
460
|
-
const boxes = [];
|
|
461
|
-
characters.forEach((character) => {
|
|
462
|
-
effects?.forEach((style) => {
|
|
463
|
-
const aabb = character.glyphBox.clone();
|
|
464
|
-
const m = this.getTransform2D(style);
|
|
465
|
-
tempV1.set(aabb.left, aabb.top);
|
|
466
|
-
tempV1.applyMatrix3(m);
|
|
467
|
-
aabb.left = tempV1.x;
|
|
468
|
-
aabb.top = tempV1.y;
|
|
469
|
-
tempV1.set(aabb.right, aabb.bottom);
|
|
470
|
-
tempV1.applyMatrix3(m);
|
|
471
|
-
aabb.width = tempV1.x - aabb.left;
|
|
472
|
-
aabb.height = tempV1.y - aabb.top;
|
|
473
|
-
const shadowOffsetX = (style.shadowOffsetX ?? 0) * fontSize;
|
|
474
|
-
const shadowOffsetY = (style.shadowOffsetY ?? 0) * fontSize;
|
|
475
|
-
const textStrokeWidth = Math.max(0.1, style.textStrokeWidth ?? 0) * fontSize;
|
|
476
|
-
aabb.left += shadowOffsetX - textStrokeWidth;
|
|
477
|
-
aabb.top += shadowOffsetY - textStrokeWidth;
|
|
478
|
-
aabb.width += textStrokeWidth * 2;
|
|
479
|
-
aabb.height += textStrokeWidth * 2;
|
|
480
|
-
boxes.push(aabb);
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
|
-
return BoundingBox.from(...boxes);
|
|
484
|
-
}
|
|
485
|
-
draw(options) {
|
|
486
|
-
const { ctx } = options;
|
|
487
|
-
const { effects, characters, renderBoundingBox } = this._text;
|
|
488
|
-
if (effects) {
|
|
489
|
-
effects.forEach((style) => {
|
|
490
|
-
uploadColor(style, renderBoundingBox, ctx);
|
|
491
|
-
ctx.save();
|
|
492
|
-
const [a, c, e, b, d, f] = this.getTransform2D(style).transpose().elements;
|
|
493
|
-
ctx.transform(a, b, c, d, e, f);
|
|
494
|
-
characters.forEach((character) => {
|
|
495
|
-
character.drawTo(ctx, style);
|
|
496
|
-
});
|
|
497
|
-
ctx.restore();
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
return this;
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
var __defProp$1 = Object.defineProperty;
|
|
505
|
-
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
506
|
-
var __publicField$1 = (obj, key, value) => {
|
|
507
|
-
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
508
|
-
return value;
|
|
509
|
-
};
|
|
510
|
-
const defaultHighlightRefer = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiIgdmlld0JveD0iMCAwIDcyIDcyIiBmaWxsPSJub25lIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMyLjQwMjkgMjhIMzUuMTU5NFYzMy4xNzcxQzM1Ljk4MjEgMzIuMzExNSAzNi45NzEgMzEuODczNyAzOC4wOTQ4IDMxLjg3MzdDMzkuNjY3NiAzMS44NzM3IDQwLjkxNjYgMzIuNDI5NSA0MS44MzkgMzMuNTQzN0w0MS44NDAzIDMzLjU0NTNDNDIuNjcxNyAzNC41NzA1IDQzLjA5MTUgMzUuODU1OSA0My4wOTE1IDM3LjM4NzdDNDMuMDkxNSAzOC45NzYxIDQyLjY3MjkgNDAuMzAyOCA0MS44MTgzIDQxLjMzMDRMNDEuODE3MSA0MS4zMzE4QzQwLjg3MzEgNDIuNDQ2MSAzOS41ODMyIDQzIDM3Ljk3MjEgNDNDMzYuNzQ3NyA0MyAzNS43NDg4IDQyLjY1OTkgMzQuOTk1OCA0MS45NjkzVjQyLjcyNDdIMzIuNDAyOVYyOFpNMzcuNTQyOCAzNC4wOTI0QzM2Ljg1NDkgMzQuMDkyNCAzNi4zMDE0IDM0LjM1NjEgMzUuODQ4NyAzNC45MDA0TDM1Ljg0NTIgMzQuOTA0NkMzNS4zMzU4IDM1LjQ4NTMgMzUuMDc3NiAzNi4yOTc2IDM1LjA3NzYgMzcuMzQ4NFYzNy41MDU3QzM1LjA3NzYgMzguNDY0IDM1LjI3NzIgMzkuMjQ0MyAzNS42OTQzIDM5LjgyNzlDMzYuMTQ0MSA0MC40NTg3IDM2Ljc3MjYgNDAuNzgxMyAzNy42MjQ1IDQwLjc4MTNDMzguNTg3NCA0MC43ODEzIDM5LjI3MDcgNDAuNDUyNyAzOS43MTUyIDM5LjgxMjdDNDAuMDcyOCAzOS4yNjg0IDQwLjI3MzcgMzguNDY3MyA0MC4yNzM3IDM3LjM4NzdDNDAuMjczNyAzNi4zMTA1IDQwLjA1MzMgMzUuNTMxMyAzOS42NzgzIDM1LjAwNzdDMzkuMjM3MSAzNC40MDcxIDM4LjUzNDIgMzQuMDkyNCAzNy41NDI4IDM0LjA5MjRaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZD0iTTQ5Ljg2MTQgMzEuODczN0M0OC4xNTM1IDMxLjg3MzcgNDYuODAxNiAzMi40MjM5IDQ1LjgzNDggMzMuNTM5MkM0NC45MzcgMzQuNTQ3MiA0NC40OTY2IDM1Ljg1NiA0NC40OTY2IDM3LjQyN0M0NC40OTY2IDM5LjAzNjggNDQuOTM2NyA0MC4zNjU5IDQ1Ljg1NTkgNDEuMzk0M0M0Ni44MDMxIDQyLjQ3MDYgNDguMTM0OCA0MyA0OS44MjA1IDQzQzUxLjIyNiA0MyA1Mi4zODI2IDQyLjY1NjMgNTMuMjQ3OSA0MS45Njk3QzU0LjEzNTkgNDEuMjYxNCA1NC43MDYxIDQwLjE4ODcgNTQuOTU3MyAzOC43NzkxTDU1IDM4LjUzOTdINTIuMjQ4NEw1Mi4yMjU5IDM4LjcyMDFDNTIuMTM3OSAzOS40MjUxIDUxLjg5MjUgMzkuOTI3OCA1MS41MTA5IDQwLjI1NThDNTEuMTI5NSA0MC41ODM1IDUwLjU4MzEgNDAuNzYxNiA0OS44NDA5IDQwLjc2MTZDNDkuMDAwMSA0MC43NjE2IDQ4LjM5NDkgNDAuNDcxNSA0Ny45OTA3IDM5LjkyMzdMNDcuOTg3NCAzOS45MTk0QzQ3LjUzNTYgMzkuMzQwMSA0Ny4zMTQ0IDM4LjUwNjIgNDcuMzE0NCAzNy40MDc0QzQ3LjMxNDQgMzYuMzMyMiA0Ny41NTQ0IDM1LjUxNzcgNDguMDA1OCAzNC45NTY4TDQ4LjAwNzggMzQuOTU0M0M0OC40NTM3IDM0LjM4MjUgNDkuMDYxOCAzNC4xMTIxIDQ5Ljg2MTQgMzQuMTEyMUM1MC41MjMgMzQuMTEyMSA1MS4wNDUxIDM0LjI2MTUgNTEuNDI3MiAzNC41NDA3QzUxLjc4ODQgMzQuODE5NCA1Mi4wNTMgMzUuMjQ0NyA1Mi4xODgxIDM1Ljg1NzFMNTIuMjIzOSAzNi4wMTk0SDU0Ljk1NDhMNTQuOTE3IDM1Ljc4MzVDNTQuNzA2MyAzNC40NjYgNTQuMTUzNiAzMy40NzAxIDUzLjI2MzQgMzIuODAxOUw1My4yNjAyIDMyLjc5OTVDNTIuMzk1MSAzMi4xNzU1IDUxLjI2MjEgMzEuODczNyA0OS44NjE0IDMxLjg3MzdaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNS43NTYxIDI4LjI3NTNIMjIuNzQ0TDE3IDQyLjcyNDdIMjAuMDE0MUwyMS4zNDI5IDM5LjIwNDlIMjcuMTU3MkwyOC40ODYgNDIuNzI0N0gzMS41MDAxTDI1Ljc1NjEgMjguMjc1M1pNMjIuMjEyNSAzNi45MDc2TDI0LjI1OTYgMzEuNDUzOUwyNi4yODg1IDM2LjkwNzZIMjIuMjEyNVoiIGZpbGw9IiMyMjI1MjkiLz48L3N2Zz4=";
|
|
511
|
-
const _Highlighter = class _Highlighter extends Feature {
|
|
512
|
-
constructor() {
|
|
513
|
-
super(...arguments);
|
|
514
|
-
__publicField$1(this, "paths", []);
|
|
515
|
-
}
|
|
516
|
-
static get refer() {
|
|
517
|
-
return this._refer;
|
|
518
|
-
}
|
|
519
|
-
static set refer(refer) {
|
|
520
|
-
this._refer = refer;
|
|
521
|
-
this.parsedRefer = parseSvg(refer);
|
|
522
|
-
}
|
|
523
|
-
getReferBoundingBox() {
|
|
524
|
-
const max = Vector2.MIN;
|
|
525
|
-
const min = Vector2.MAX;
|
|
526
|
-
_Highlighter.parsedRefer.forEach((path) => {
|
|
527
|
-
path.getMinMax(min, max);
|
|
528
|
-
});
|
|
529
|
-
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
530
|
-
}
|
|
531
|
-
getBoundingBox() {
|
|
532
|
-
if (!this.paths.length) {
|
|
533
|
-
return void 0;
|
|
534
|
-
}
|
|
535
|
-
const min = Vector2.MAX;
|
|
536
|
-
const max = Vector2.MIN;
|
|
537
|
-
this.paths.forEach((v) => v.path.getMinMax(min, max));
|
|
538
|
-
return new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
539
|
-
}
|
|
540
|
-
highlight(perChar = false) {
|
|
541
|
-
const { characters } = this._text;
|
|
542
|
-
let group;
|
|
543
|
-
const groups = [];
|
|
544
|
-
let prevHighlight;
|
|
545
|
-
characters.forEach((character) => {
|
|
546
|
-
const highlight = character.parent.highlight;
|
|
547
|
-
if (highlight?.url) {
|
|
548
|
-
if (!perChar && prevHighlight?.url === highlight.url && prevHighlight?.strokeWidth === highlight.strokeWidth && prevHighlight?.repeatXByFontsize === highlight.repeatXByFontsize && prevHighlight?.overflowXHidden === highlight.overflowXHidden && group.length && group[0].boundingBox.top === character.boundingBox.top && group[0].fontSize === character.fontSize) {
|
|
549
|
-
group.push(character);
|
|
550
|
-
} else {
|
|
551
|
-
group = [];
|
|
552
|
-
group.push(character);
|
|
553
|
-
groups.push(group);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
prevHighlight = highlight;
|
|
557
|
-
});
|
|
558
|
-
this.paths = groups.filter((characters2) => characters2.length).map((characters2) => {
|
|
559
|
-
return {
|
|
560
|
-
highlight: characters2[0].parent.highlight,
|
|
561
|
-
box: BoundingBox.from(...characters2.map((c) => c.glyphBox)),
|
|
562
|
-
baseline: Math.max(...characters2.map((c) => c.baseline)),
|
|
563
|
-
fontSize: characters2[0].fontSize
|
|
564
|
-
};
|
|
565
|
-
}).map((group2) => this._parseGroup(group2)).flat();
|
|
566
|
-
}
|
|
567
|
-
_parseSvg(url) {
|
|
568
|
-
const svg = parseSvgToDom(url);
|
|
569
|
-
const paths = parseSvg(svg);
|
|
570
|
-
const min = Vector2.MAX;
|
|
571
|
-
const max = Vector2.MIN;
|
|
572
|
-
paths.forEach((path) => path.getMinMax(min, max));
|
|
573
|
-
return {
|
|
574
|
-
paths,
|
|
575
|
-
box: new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y)
|
|
576
|
-
};
|
|
577
|
-
}
|
|
578
|
-
_parseGroup(group) {
|
|
579
|
-
const { highlight, box: groupBox, fontSize } = group;
|
|
580
|
-
const {
|
|
581
|
-
strokeWidth = 1,
|
|
582
|
-
repeatXByFontsize = 0,
|
|
583
|
-
overflowXHidden = Boolean(repeatXByFontsize)
|
|
584
|
-
} = highlight;
|
|
585
|
-
const { box, paths } = this._parseSvg(highlight.url);
|
|
586
|
-
const result = [];
|
|
587
|
-
const referBoundingBox = this.getReferBoundingBox();
|
|
588
|
-
const scale = {
|
|
589
|
-
x: repeatXByFontsize ? fontSize * repeatXByFontsize * (box.width / referBoundingBox.width) / box.width : groupBox.width * (box.width / referBoundingBox.width) / box.width,
|
|
590
|
-
y: groupBox.height * (box.height / referBoundingBox.height) / box.height
|
|
591
|
-
};
|
|
592
|
-
const styleScale = fontSize / box.width * 2;
|
|
593
|
-
const unitWidth = box.width * scale.x;
|
|
594
|
-
const total = Math.ceil(groupBox.width / unitWidth);
|
|
595
|
-
const offset = {
|
|
596
|
-
x: (box.left - referBoundingBox.left) * scale.x,
|
|
597
|
-
y: (box.top - referBoundingBox.top) * scale.y
|
|
598
|
-
};
|
|
599
|
-
const transform = new Matrix3().translate(-box.left, -box.top).scale(scale.x, scale.y).translate(groupBox.left, groupBox.top).translate(offset.x, offset.y);
|
|
600
|
-
for (let i = 0; i < total; i++) {
|
|
601
|
-
const _transform = transform.clone().translate(i * unitWidth, 0);
|
|
602
|
-
paths.forEach((original) => {
|
|
603
|
-
const path = original.clone().matrix(_transform);
|
|
604
|
-
if (path.style.strokeWidth) {
|
|
605
|
-
path.style.strokeWidth *= styleScale * strokeWidth;
|
|
606
|
-
}
|
|
607
|
-
if (path.style.strokeMiterlimit) {
|
|
608
|
-
path.style.strokeMiterlimit *= styleScale;
|
|
609
|
-
}
|
|
610
|
-
if (path.style.strokeDashoffset) {
|
|
611
|
-
path.style.strokeDashoffset *= styleScale;
|
|
612
|
-
}
|
|
613
|
-
if (path.style.strokeDasharray) {
|
|
614
|
-
path.style.strokeDasharray = path.style.strokeDasharray.map((v) => v * styleScale);
|
|
615
|
-
}
|
|
616
|
-
result.push({
|
|
617
|
-
clipRect: overflowXHidden ? new BoundingBox(
|
|
618
|
-
groupBox.left + offset.x * 2,
|
|
619
|
-
groupBox.top - groupBox.height,
|
|
620
|
-
groupBox.width - offset.x * 2,
|
|
621
|
-
groupBox.height * 3
|
|
622
|
-
) : void 0,
|
|
623
|
-
path
|
|
624
|
-
});
|
|
625
|
-
});
|
|
626
|
-
}
|
|
627
|
-
return result;
|
|
628
|
-
}
|
|
629
|
-
draw({ ctx }) {
|
|
630
|
-
this.paths.forEach((v) => {
|
|
631
|
-
drawPath({
|
|
632
|
-
ctx,
|
|
633
|
-
path: v.path,
|
|
634
|
-
clipRect: v.clipRect,
|
|
635
|
-
fontSize: this._text.computedStyle.fontSize
|
|
636
|
-
});
|
|
637
|
-
});
|
|
638
|
-
return this;
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
__publicField$1(_Highlighter, "_refer", defaultHighlightRefer);
|
|
642
|
-
__publicField$1(_Highlighter, "parsedRefer", parseSvg(_Highlighter._refer));
|
|
643
|
-
let Highlighter = _Highlighter;
|
|
644
|
-
|
|
645
|
-
class Measurer extends Feature {
|
|
646
472
|
_styleToDomStyle(style) {
|
|
647
473
|
const _style = { ...style };
|
|
648
474
|
for (const key in style) {
|
|
@@ -686,7 +512,7 @@ class Measurer extends Feature {
|
|
|
686
512
|
});
|
|
687
513
|
const ul = document.createElement("ul");
|
|
688
514
|
Object.assign(ul.style, {
|
|
689
|
-
|
|
515
|
+
listStyleType: "inherit",
|
|
690
516
|
padding: "0",
|
|
691
517
|
margin: "0"
|
|
692
518
|
});
|
|
@@ -851,7 +677,10 @@ class Measurer extends Feature {
|
|
|
851
677
|
}
|
|
852
678
|
}
|
|
853
679
|
|
|
854
|
-
class Parser
|
|
680
|
+
class Parser {
|
|
681
|
+
constructor(_text) {
|
|
682
|
+
this._text = _text;
|
|
683
|
+
}
|
|
855
684
|
parse() {
|
|
856
685
|
let { content, computedStyle: style } = this._text;
|
|
857
686
|
const paragraphs = [];
|
|
@@ -872,10 +701,9 @@ class Parser extends Feature {
|
|
|
872
701
|
if (typeof f === "string") {
|
|
873
702
|
paragraph.addFragment(f);
|
|
874
703
|
} else {
|
|
875
|
-
const { content: content2,
|
|
704
|
+
const { content: content2, ...fStyle } = f;
|
|
876
705
|
if (content2 !== void 0) {
|
|
877
|
-
|
|
878
|
-
fragment.highlight = highlight;
|
|
706
|
+
paragraph.addFragment(content2, fStyle);
|
|
879
707
|
}
|
|
880
708
|
}
|
|
881
709
|
});
|
|
@@ -884,19 +712,17 @@ class Parser extends Feature {
|
|
|
884
712
|
const { fragments, ...pStyle } = p;
|
|
885
713
|
const paragraph = new Paragraph(pStyle, style);
|
|
886
714
|
fragments.forEach((f) => {
|
|
887
|
-
const { content: content2,
|
|
715
|
+
const { content: content2, ...fStyle } = f;
|
|
888
716
|
if (content2 !== void 0) {
|
|
889
|
-
|
|
890
|
-
fragment.highlight = highlight;
|
|
717
|
+
paragraph.addFragment(content2, fStyle);
|
|
891
718
|
}
|
|
892
719
|
});
|
|
893
720
|
paragraphs.push(paragraph);
|
|
894
721
|
} else if ("content" in p) {
|
|
895
|
-
const { content: pData,
|
|
722
|
+
const { content: pData, ...pStyle } = p;
|
|
896
723
|
if (pData !== void 0) {
|
|
897
724
|
const paragraph = new Paragraph(pStyle, style);
|
|
898
|
-
|
|
899
|
-
fragment.highlight = highlight;
|
|
725
|
+
paragraph.addFragment(pData);
|
|
900
726
|
paragraphs.push(paragraph);
|
|
901
727
|
}
|
|
902
728
|
}
|
|
@@ -906,71 +732,274 @@ class Parser extends Feature {
|
|
|
906
732
|
}
|
|
907
733
|
}
|
|
908
734
|
|
|
909
|
-
|
|
910
|
-
|
|
735
|
+
function definePlugin(options) {
|
|
736
|
+
return options;
|
|
911
737
|
}
|
|
912
738
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
739
|
+
const tempV1 = new Vector2();
|
|
740
|
+
const tempM1 = new Matrix3();
|
|
741
|
+
const tempM2 = new Matrix3();
|
|
742
|
+
function effect() {
|
|
743
|
+
return definePlugin({
|
|
744
|
+
name: "effect",
|
|
745
|
+
getBoundingBox: (text) => {
|
|
746
|
+
const { characters, fontSize, effects } = text;
|
|
747
|
+
const boxes = [];
|
|
748
|
+
characters.forEach((character) => {
|
|
749
|
+
effects?.forEach((style) => {
|
|
750
|
+
const aabb = character.glyphBox.clone();
|
|
751
|
+
const m = getTransform2D(text, style);
|
|
752
|
+
tempV1.set(aabb.left, aabb.top);
|
|
753
|
+
tempV1.applyMatrix3(m);
|
|
754
|
+
aabb.left = tempV1.x;
|
|
755
|
+
aabb.top = tempV1.y;
|
|
756
|
+
tempV1.set(aabb.right, aabb.bottom);
|
|
757
|
+
tempV1.applyMatrix3(m);
|
|
758
|
+
aabb.width = tempV1.x - aabb.left;
|
|
759
|
+
aabb.height = tempV1.y - aabb.top;
|
|
760
|
+
const shadowOffsetX = (style.shadowOffsetX ?? 0) * fontSize;
|
|
761
|
+
const shadowOffsetY = (style.shadowOffsetY ?? 0) * fontSize;
|
|
762
|
+
const textStrokeWidth = Math.max(0.1, style.textStrokeWidth ?? 0) * fontSize;
|
|
763
|
+
aabb.left += shadowOffsetX - textStrokeWidth;
|
|
764
|
+
aabb.top += shadowOffsetY - textStrokeWidth;
|
|
765
|
+
aabb.width += textStrokeWidth * 2;
|
|
766
|
+
aabb.height += textStrokeWidth * 2;
|
|
767
|
+
boxes.push(aabb);
|
|
768
|
+
});
|
|
940
769
|
});
|
|
941
|
-
|
|
942
|
-
|
|
770
|
+
return boxes.length ? BoundingBox.from(...boxes) : void 0;
|
|
771
|
+
},
|
|
772
|
+
render: (ctx, text) => {
|
|
773
|
+
const { characters, renderBoundingBox, effects } = text;
|
|
774
|
+
if (effects) {
|
|
775
|
+
effects.forEach((style) => {
|
|
776
|
+
uploadColor(style, renderBoundingBox, ctx);
|
|
777
|
+
ctx.save();
|
|
778
|
+
const [a, c, e, b, d, f] = getTransform2D(text, style).transpose().elements;
|
|
779
|
+
ctx.transform(a, b, c, d, e, f);
|
|
780
|
+
characters.forEach((character) => {
|
|
781
|
+
character.drawTo(ctx, style);
|
|
782
|
+
});
|
|
783
|
+
ctx.restore();
|
|
784
|
+
});
|
|
785
|
+
} else {
|
|
786
|
+
characters.forEach((character) => {
|
|
787
|
+
character.drawTo(ctx);
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
function getTransform2D(text, style) {
|
|
794
|
+
const { fontSize, renderBoundingBox } = text;
|
|
795
|
+
const offsetX = (style.offsetX ?? 0) * fontSize;
|
|
796
|
+
const offsetY = (style.offsetY ?? 0) * fontSize;
|
|
797
|
+
const PI_2 = Math.PI * 2;
|
|
798
|
+
const skewX = (style.skewX ?? 0) / 360 * PI_2;
|
|
799
|
+
const skewY = (style.skewY ?? 0) / 360 * PI_2;
|
|
800
|
+
const { left, top, width, height } = renderBoundingBox;
|
|
801
|
+
const centerX = left + width / 2;
|
|
802
|
+
const centerY = top + height / 2;
|
|
803
|
+
tempM1.identity();
|
|
804
|
+
tempM2.makeTranslation(offsetX, offsetY);
|
|
805
|
+
tempM1.multiply(tempM2);
|
|
806
|
+
tempM2.makeTranslation(centerX, centerY);
|
|
807
|
+
tempM1.multiply(tempM2);
|
|
808
|
+
tempM2.set(1, Math.tan(skewX), 0, Math.tan(skewY), 1, 0, 0, 0, 1);
|
|
809
|
+
tempM1.multiply(tempM2);
|
|
810
|
+
tempM2.makeTranslation(-centerX, -centerY);
|
|
811
|
+
tempM1.multiply(tempM2);
|
|
812
|
+
return tempM1.clone();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const defaultOptions = {
|
|
816
|
+
referImage: "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI3MiIgaGVpZ2h0PSI3MiIgdmlld0JveD0iMCAwIDcyIDcyIiBmaWxsPSJub25lIj48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTMyLjQwMjkgMjhIMzUuMTU5NFYzMy4xNzcxQzM1Ljk4MjEgMzIuMzExNSAzNi45NzEgMzEuODczNyAzOC4wOTQ4IDMxLjg3MzdDMzkuNjY3NiAzMS44NzM3IDQwLjkxNjYgMzIuNDI5NSA0MS44MzkgMzMuNTQzN0w0MS44NDAzIDMzLjU0NTNDNDIuNjcxNyAzNC41NzA1IDQzLjA5MTUgMzUuODU1OSA0My4wOTE1IDM3LjM4NzdDNDMuMDkxNSAzOC45NzYxIDQyLjY3MjkgNDAuMzAyOCA0MS44MTgzIDQxLjMzMDRMNDEuODE3MSA0MS4zMzE4QzQwLjg3MzEgNDIuNDQ2MSAzOS41ODMyIDQzIDM3Ljk3MjEgNDNDMzYuNzQ3NyA0MyAzNS43NDg4IDQyLjY1OTkgMzQuOTk1OCA0MS45NjkzVjQyLjcyNDdIMzIuNDAyOVYyOFpNMzcuNTQyOCAzNC4wOTI0QzM2Ljg1NDkgMzQuMDkyNCAzNi4zMDE0IDM0LjM1NjEgMzUuODQ4NyAzNC45MDA0TDM1Ljg0NTIgMzQuOTA0NkMzNS4zMzU4IDM1LjQ4NTMgMzUuMDc3NiAzNi4yOTc2IDM1LjA3NzYgMzcuMzQ4NFYzNy41MDU3QzM1LjA3NzYgMzguNDY0IDM1LjI3NzIgMzkuMjQ0MyAzNS42OTQzIDM5LjgyNzlDMzYuMTQ0MSA0MC40NTg3IDM2Ljc3MjYgNDAuNzgxMyAzNy42MjQ1IDQwLjc4MTNDMzguNTg3NCA0MC43ODEzIDM5LjI3MDcgNDAuNDUyNyAzOS43MTUyIDM5LjgxMjdDNDAuMDcyOCAzOS4yNjg0IDQwLjI3MzcgMzguNDY3MyA0MC4yNzM3IDM3LjM4NzdDNDAuMjczNyAzNi4zMTA1IDQwLjA1MzMgMzUuNTMxMyAzOS42NzgzIDM1LjAwNzdDMzkuMjM3MSAzNC40MDcxIDM4LjUzNDIgMzQuMDkyNCAzNy41NDI4IDM0LjA5MjRaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZD0iTTQ5Ljg2MTQgMzEuODczN0M0OC4xNTM1IDMxLjg3MzcgNDYuODAxNiAzMi40MjM5IDQ1LjgzNDggMzMuNTM5MkM0NC45MzcgMzQuNTQ3MiA0NC40OTY2IDM1Ljg1NiA0NC40OTY2IDM3LjQyN0M0NC40OTY2IDM5LjAzNjggNDQuOTM2NyA0MC4zNjU5IDQ1Ljg1NTkgNDEuMzk0M0M0Ni44MDMxIDQyLjQ3MDYgNDguMTM0OCA0MyA0OS44MjA1IDQzQzUxLjIyNiA0MyA1Mi4zODI2IDQyLjY1NjMgNTMuMjQ3OSA0MS45Njk3QzU0LjEzNTkgNDEuMjYxNCA1NC43MDYxIDQwLjE4ODcgNTQuOTU3MyAzOC43NzkxTDU1IDM4LjUzOTdINTIuMjQ4NEw1Mi4yMjU5IDM4LjcyMDFDNTIuMTM3OSAzOS40MjUxIDUxLjg5MjUgMzkuOTI3OCA1MS41MTA5IDQwLjI1NThDNTEuMTI5NSA0MC41ODM1IDUwLjU4MzEgNDAuNzYxNiA0OS44NDA5IDQwLjc2MTZDNDkuMDAwMSA0MC43NjE2IDQ4LjM5NDkgNDAuNDcxNSA0Ny45OTA3IDM5LjkyMzdMNDcuOTg3NCAzOS45MTk0QzQ3LjUzNTYgMzkuMzQwMSA0Ny4zMTQ0IDM4LjUwNjIgNDcuMzE0NCAzNy40MDc0QzQ3LjMxNDQgMzYuMzMyMiA0Ny41NTQ0IDM1LjUxNzcgNDguMDA1OCAzNC45NTY4TDQ4LjAwNzggMzQuOTU0M0M0OC40NTM3IDM0LjM4MjUgNDkuMDYxOCAzNC4xMTIxIDQ5Ljg2MTQgMzQuMTEyMUM1MC41MjMgMzQuMTEyMSA1MS4wNDUxIDM0LjI2MTUgNTEuNDI3MiAzNC41NDA3QzUxLjc4ODQgMzQuODE5NCA1Mi4wNTMgMzUuMjQ0NyA1Mi4xODgxIDM1Ljg1NzFMNTIuMjIzOSAzNi4wMTk0SDU0Ljk1NDhMNTQuOTE3IDM1Ljc4MzVDNTQuNzA2MyAzNC40NjYgNTQuMTUzNiAzMy40NzAxIDUzLjI2MzQgMzIuODAxOUw1My4yNjAyIDMyLjc5OTVDNTIuMzk1MSAzMi4xNzU1IDUxLjI2MjEgMzEuODczNyA0OS44NjE0IDMxLjg3MzdaIiBmaWxsPSIjMjIyNTI5Ii8+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNS43NTYxIDI4LjI3NTNIMjIuNzQ0TDE3IDQyLjcyNDdIMjAuMDE0MUwyMS4zNDI5IDM5LjIwNDlIMjcuMTU3MkwyOC40ODYgNDIuNzI0N0gzMS41MDAxTDI1Ljc1NjEgMjguMjc1M1pNMjIuMjEyNSAzNi45MDc2TDI0LjI1OTYgMzEuNDUzOUwyNi4yODg1IDM2LjkwNzZIMjIuMjEyNVoiIGZpbGw9IiMyMjI1MjkiLz48L3N2Zz4="
|
|
817
|
+
};
|
|
818
|
+
function parseCharsPerRepeat(size, fontSize, total) {
|
|
819
|
+
if (size === "cover") {
|
|
820
|
+
return 0;
|
|
821
|
+
} else if (typeof size === "string") {
|
|
822
|
+
if (size.endsWith("%")) {
|
|
823
|
+
const rate = Number(size.substring(0, size.length - 1)) / 100;
|
|
824
|
+
return Math.ceil(rate * total / fontSize);
|
|
825
|
+
} else if (size.endsWith("rem")) {
|
|
826
|
+
return Number(size.substring(0, size.length - 3));
|
|
827
|
+
} else {
|
|
828
|
+
return Math.ceil(Number(size) / fontSize);
|
|
829
|
+
}
|
|
830
|
+
} else {
|
|
831
|
+
return Math.ceil(size / fontSize);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
function parseStrokeWidthScale(strokeWidth, fontSize, total) {
|
|
835
|
+
if (typeof strokeWidth === "string") {
|
|
836
|
+
if (strokeWidth.endsWith("%")) {
|
|
837
|
+
return Number(strokeWidth.substring(0, strokeWidth.length - 1)) / 100;
|
|
838
|
+
} else if (strokeWidth.endsWith("rem")) {
|
|
839
|
+
const value = Number(strokeWidth.substring(0, strokeWidth.length - 3));
|
|
840
|
+
return value * fontSize / total;
|
|
841
|
+
} else {
|
|
842
|
+
return Number(strokeWidth) / total;
|
|
843
|
+
}
|
|
844
|
+
} else {
|
|
845
|
+
return strokeWidth / total;
|
|
943
846
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
847
|
+
}
|
|
848
|
+
function highlight(options = {}) {
|
|
849
|
+
const config = { ...defaultOptions, ...options };
|
|
850
|
+
const referPaths = parseSvg(config.referImage);
|
|
851
|
+
const paths = [];
|
|
852
|
+
const clipRects = [];
|
|
853
|
+
return definePlugin({
|
|
854
|
+
name: "highlight",
|
|
855
|
+
paths,
|
|
856
|
+
update: (text) => {
|
|
857
|
+
paths.length = 0;
|
|
858
|
+
const { characters } = text;
|
|
859
|
+
let group;
|
|
860
|
+
const groups = [];
|
|
861
|
+
let prevStyle;
|
|
862
|
+
characters.forEach((character) => {
|
|
863
|
+
const style = character.computedStyle;
|
|
864
|
+
if (!isNone(style.highlightImage)) {
|
|
865
|
+
if (style.highlightSize !== "1rem" && prevStyle?.highlightImage === style.highlightImage && prevStyle?.highlightSize === style.highlightSize && prevStyle?.highlightStrokeWidth === style.highlightStrokeWidth && prevStyle?.highlightOverflow === style.highlightOverflow && group.length && group[0].boundingBox.top === character.boundingBox.top && group[0].fontSize === character.fontSize) {
|
|
866
|
+
group.push(character);
|
|
867
|
+
} else {
|
|
868
|
+
group = [];
|
|
869
|
+
group.push(character);
|
|
870
|
+
groups.push(group);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
prevStyle = style;
|
|
874
|
+
});
|
|
875
|
+
groups.filter((characters2) => characters2.length).map((characters2) => {
|
|
876
|
+
return {
|
|
877
|
+
style: characters2[0].computedStyle,
|
|
878
|
+
box: BoundingBox.from(...characters2.map((c) => c.glyphBox))
|
|
879
|
+
};
|
|
880
|
+
}).forEach((group2) => {
|
|
881
|
+
const { style, box: groupBox } = group2;
|
|
882
|
+
const { fontSize } = style;
|
|
883
|
+
const strokeWidthScale = parseStrokeWidthScale(style.highlightStrokeWidth, fontSize, groupBox.width);
|
|
884
|
+
const charsPerRepeat = parseCharsPerRepeat(style.highlightSize, fontSize, groupBox.width);
|
|
885
|
+
const highlightOverflow = isNone(style.highlightOverflow) ? charsPerRepeat ? "hidden" : "visible" : style.highlightOverflow;
|
|
886
|
+
const svg = parseSvgToDom(style.highlightImage);
|
|
887
|
+
const svgPaths = parseSvg(svg);
|
|
888
|
+
const box = getPathsBoundingBox(svgPaths);
|
|
889
|
+
const referBoundingBox = getPathsBoundingBox(referPaths);
|
|
890
|
+
const scale = {
|
|
891
|
+
x: charsPerRepeat ? fontSize * charsPerRepeat * (box.width / referBoundingBox.width) / box.width : groupBox.width * (box.width / referBoundingBox.width) / box.width,
|
|
892
|
+
y: groupBox.height * (box.height / referBoundingBox.height) / box.height
|
|
893
|
+
};
|
|
894
|
+
const styleScale = fontSize / box.width * 2;
|
|
895
|
+
const unitWidth = box.width * scale.x;
|
|
896
|
+
const total = Math.ceil(groupBox.width / unitWidth);
|
|
897
|
+
const offset = {
|
|
898
|
+
x: (box.left - referBoundingBox.left) * scale.x,
|
|
899
|
+
y: (box.top - referBoundingBox.top) * scale.y
|
|
900
|
+
};
|
|
901
|
+
const transform = new Matrix3().translate(-box.left, -box.top).scale(scale.x, scale.y).translate(groupBox.left, groupBox.top).translate(offset.x, offset.y);
|
|
902
|
+
for (let i = 0; i < total; i++) {
|
|
903
|
+
const _transform = transform.clone().translate(i * unitWidth, 0);
|
|
904
|
+
svgPaths.forEach((original) => {
|
|
905
|
+
const path = original.clone().matrix(_transform);
|
|
906
|
+
if (path.style.strokeWidth) {
|
|
907
|
+
path.style.strokeWidth *= styleScale * strokeWidthScale;
|
|
908
|
+
}
|
|
909
|
+
if (path.style.strokeMiterlimit) {
|
|
910
|
+
path.style.strokeMiterlimit *= styleScale;
|
|
911
|
+
}
|
|
912
|
+
if (path.style.strokeDashoffset) {
|
|
913
|
+
path.style.strokeDashoffset *= styleScale;
|
|
914
|
+
}
|
|
915
|
+
if (path.style.strokeDasharray) {
|
|
916
|
+
path.style.strokeDasharray = path.style.strokeDasharray.map((v) => v * styleScale);
|
|
917
|
+
}
|
|
918
|
+
paths.push(path);
|
|
919
|
+
clipRects[paths.length - 1] = highlightOverflow === "hidden" ? new BoundingBox(
|
|
920
|
+
groupBox.left,
|
|
921
|
+
groupBox.top - groupBox.height,
|
|
922
|
+
groupBox.width,
|
|
923
|
+
groupBox.height * 3
|
|
924
|
+
) : void 0;
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
},
|
|
929
|
+
renderOrder: -1,
|
|
930
|
+
render: (ctx, text) => {
|
|
931
|
+
paths.forEach((path, index) => {
|
|
932
|
+
drawPath({
|
|
933
|
+
ctx,
|
|
934
|
+
path,
|
|
935
|
+
clipRect: clipRects[index],
|
|
936
|
+
fontSize: text.computedStyle.fontSize
|
|
937
|
+
});
|
|
938
|
+
});
|
|
950
939
|
}
|
|
951
|
-
|
|
952
|
-
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
function parseScale(size, fontSize, total) {
|
|
944
|
+
if (size === "cover") {
|
|
945
|
+
return 1;
|
|
946
|
+
} else if (typeof size === "string") {
|
|
947
|
+
if (size.endsWith("%")) {
|
|
948
|
+
return Number(size.substring(0, size.length - 1)) / 100;
|
|
949
|
+
} else if (size.endsWith("rem")) {
|
|
950
|
+
const value = Number(size.substring(0, size.length - 3));
|
|
951
|
+
return value * fontSize / total;
|
|
952
|
+
} else {
|
|
953
|
+
return Number(size) / total;
|
|
953
954
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
955
|
+
} else {
|
|
956
|
+
return size / total;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
function listStyle() {
|
|
960
|
+
const paths = [];
|
|
961
|
+
return definePlugin({
|
|
962
|
+
name: "listStyle",
|
|
963
|
+
paths,
|
|
964
|
+
update: (text) => {
|
|
965
|
+
paths.length = 0;
|
|
966
|
+
const { paragraphs, computedStyle: style, fontSize } = text;
|
|
967
|
+
let listStyleSize = style.listStyleSize;
|
|
968
|
+
let image;
|
|
969
|
+
if (!isNone(style.listStyleImage)) {
|
|
970
|
+
image = style.listStyleImage;
|
|
971
|
+
} else if (!isNone(style.listStyleType)) {
|
|
972
|
+
const r = fontSize * 0.38 / 2;
|
|
973
|
+
listStyleSize = listStyleSize === "cover" ? r * 2 : listStyleSize;
|
|
974
|
+
switch (style.listStyleType) {
|
|
975
|
+
case "disc":
|
|
976
|
+
image = `<svg width="${r * 2}" height="${r * 2}" xmlns="http://www.w3.org/2000/svg">
|
|
977
|
+
<circle cx="${r}" cy="${r}" r="${r}" fill="${style.color}" />
|
|
978
|
+
</svg>`;
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
957
981
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
982
|
+
if (!image) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
const paddingLeft = fontSize * 0.45;
|
|
986
|
+
const imagePaths = parseSvg(image);
|
|
987
|
+
const imageBox = getPathsBoundingBox(imagePaths);
|
|
988
|
+
paragraphs.forEach((paragraph) => {
|
|
989
|
+
const box = paragraph.fragments[0]?.characters[0]?.getGlyphBoundingBox();
|
|
990
|
+
if (box) {
|
|
991
|
+
const scale = parseScale(listStyleSize, style.fontSize, box.height);
|
|
992
|
+
const reScale = box.height / imageBox.height * scale;
|
|
993
|
+
const m = new Matrix3();
|
|
994
|
+
m.translate(-imageBox.left - imageBox.width, -imageBox.top);
|
|
995
|
+
m.scale(reScale, reScale);
|
|
996
|
+
m.translate(0, box.height / 2 - imageBox.height * reScale / 2);
|
|
997
|
+
m.translate(box.left - paddingLeft, box.top);
|
|
998
|
+
paths.push(...imagePaths.map((p) => p.clone().matrix(m)));
|
|
961
999
|
}
|
|
962
1000
|
});
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
}
|
|
966
|
-
draw(options) {
|
|
967
|
-
const { ctx } = options;
|
|
968
|
-
const { characters } = this._text;
|
|
969
|
-
characters.forEach((character) => {
|
|
970
|
-
character.drawTo(ctx);
|
|
971
|
-
});
|
|
972
|
-
return this;
|
|
973
|
-
}
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
974
1003
|
}
|
|
975
1004
|
|
|
976
1005
|
var __defProp = Object.defineProperty;
|
|
@@ -980,35 +1009,50 @@ var __publicField = (obj, key, value) => {
|
|
|
980
1009
|
return value;
|
|
981
1010
|
};
|
|
982
1011
|
const defaultTextStyles = {
|
|
983
|
-
|
|
984
|
-
|
|
1012
|
+
writingMode: "horizontal-tb",
|
|
1013
|
+
verticalAlign: "baseline",
|
|
1014
|
+
lineHeight: 1,
|
|
1015
|
+
letterSpacing: 0,
|
|
1016
|
+
// font
|
|
985
1017
|
fontSize: 14,
|
|
986
1018
|
fontWeight: "normal",
|
|
987
1019
|
fontFamily: "_fallback",
|
|
988
1020
|
fontStyle: "normal",
|
|
989
1021
|
fontKerning: "normal",
|
|
1022
|
+
// text
|
|
990
1023
|
textWrap: "wrap",
|
|
991
1024
|
textAlign: "start",
|
|
992
|
-
verticalAlign: "baseline",
|
|
993
1025
|
textTransform: "none",
|
|
1026
|
+
textOrientation: "mixed",
|
|
1027
|
+
// color
|
|
1028
|
+
color: "#000",
|
|
1029
|
+
backgroundColor: "rgba(0, 0, 0, 0)",
|
|
1030
|
+
// text
|
|
994
1031
|
textDecoration: "none",
|
|
1032
|
+
// textStroke
|
|
995
1033
|
textStrokeWidth: 0,
|
|
996
1034
|
textStrokeColor: "#000",
|
|
997
|
-
|
|
998
|
-
letterSpacing: 0,
|
|
1035
|
+
// shadow
|
|
999
1036
|
shadowColor: "rgba(0, 0, 0, 0)",
|
|
1000
1037
|
shadowOffsetX: 0,
|
|
1001
1038
|
shadowOffsetY: 0,
|
|
1002
1039
|
shadowBlur: 0,
|
|
1003
|
-
|
|
1004
|
-
|
|
1040
|
+
// listStyle
|
|
1041
|
+
listStyleType: "none",
|
|
1042
|
+
listStyleImage: "none",
|
|
1043
|
+
listStyleSize: "cover",
|
|
1044
|
+
listStylePosition: "outside",
|
|
1045
|
+
// highlight
|
|
1046
|
+
highlightImage: "none",
|
|
1047
|
+
highlightSize: "cover",
|
|
1048
|
+
highlightStrokeWidth: "100%",
|
|
1049
|
+
highlightOverflow: "none"
|
|
1005
1050
|
};
|
|
1006
1051
|
class Text {
|
|
1007
1052
|
constructor(options = {}) {
|
|
1008
1053
|
__publicField(this, "content");
|
|
1009
1054
|
__publicField(this, "style");
|
|
1010
1055
|
__publicField(this, "effects");
|
|
1011
|
-
__publicField(this, "deformation");
|
|
1012
1056
|
__publicField(this, "measureDom");
|
|
1013
1057
|
__publicField(this, "needsUpdate", true);
|
|
1014
1058
|
__publicField(this, "computedStyle", { ...defaultTextStyles });
|
|
@@ -1017,16 +1061,13 @@ class Text {
|
|
|
1017
1061
|
__publicField(this, "renderBoundingBox", new BoundingBox());
|
|
1018
1062
|
__publicField(this, "parser", new Parser(this));
|
|
1019
1063
|
__publicField(this, "measurer", new Measurer(this));
|
|
1020
|
-
__publicField(this, "
|
|
1021
|
-
|
|
1022
|
-
__publicField(this, "highlighter", new Highlighter(this));
|
|
1023
|
-
__publicField(this, "renderer2D", new Renderer2D(this));
|
|
1024
|
-
const { content = "", style = {}, effects, deformation, measureDom } = options;
|
|
1064
|
+
__publicField(this, "plugins", /* @__PURE__ */ new Map());
|
|
1065
|
+
const { content = "", style = {}, measureDom, effects } = options;
|
|
1025
1066
|
this.content = content;
|
|
1026
1067
|
this.style = style;
|
|
1027
|
-
this.effects = effects;
|
|
1028
|
-
this.deformation = deformation;
|
|
1029
1068
|
this.measureDom = measureDom;
|
|
1069
|
+
this.effects = effects;
|
|
1070
|
+
this.use(effect()).use(highlight(options.highlight)).use(listStyle());
|
|
1030
1071
|
}
|
|
1031
1072
|
get fontSize() {
|
|
1032
1073
|
return this.computedStyle.fontSize;
|
|
@@ -1034,6 +1075,10 @@ class Text {
|
|
|
1034
1075
|
get characters() {
|
|
1035
1076
|
return this.paragraphs.flatMap((p) => p.fragments.flatMap((f) => f.characters));
|
|
1036
1077
|
}
|
|
1078
|
+
use(plugin) {
|
|
1079
|
+
this.plugins.set(plugin.name, plugin);
|
|
1080
|
+
return this;
|
|
1081
|
+
}
|
|
1037
1082
|
measure(dom = this.measureDom) {
|
|
1038
1083
|
this.computedStyle = { ...defaultTextStyles, ...this.style };
|
|
1039
1084
|
const old = this.paragraphs;
|
|
@@ -1052,15 +1097,23 @@ class Text {
|
|
|
1052
1097
|
this.boundingBox = boundingBox;
|
|
1053
1098
|
const characters = this.characters;
|
|
1054
1099
|
characters.forEach((c) => c.update());
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1100
|
+
const plugins = [...this.plugins.values()];
|
|
1101
|
+
plugins.sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
|
|
1102
|
+
plugin.update?.(this);
|
|
1103
|
+
});
|
|
1060
1104
|
const min = Vector2.MAX;
|
|
1061
1105
|
const max = Vector2.MIN;
|
|
1062
1106
|
characters.forEach((c) => c.getGlyphMinMax(min, max));
|
|
1063
1107
|
this.renderBoundingBox = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
1108
|
+
this.renderBoundingBox = BoundingBox.from(
|
|
1109
|
+
this.renderBoundingBox,
|
|
1110
|
+
...plugins.map((plugin) => {
|
|
1111
|
+
if (plugin.getBoundingBox) {
|
|
1112
|
+
return plugin.getBoundingBox(this);
|
|
1113
|
+
}
|
|
1114
|
+
return getPathsBoundingBox(plugin.paths ?? []);
|
|
1115
|
+
}).filter(Boolean)
|
|
1116
|
+
);
|
|
1064
1117
|
return this;
|
|
1065
1118
|
}
|
|
1066
1119
|
render(options) {
|
|
@@ -1072,28 +1125,24 @@ class Text {
|
|
|
1072
1125
|
if (this.needsUpdate) {
|
|
1073
1126
|
this.update();
|
|
1074
1127
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
this.effector.draw({ ctx });
|
|
1092
|
-
} else {
|
|
1093
|
-
this.renderer2D.draw({ ctx });
|
|
1094
|
-
}
|
|
1128
|
+
setupView(ctx, pixelRatio, this.renderBoundingBox);
|
|
1129
|
+
uploadColors(ctx, this);
|
|
1130
|
+
const plugins = [...this.plugins.values()];
|
|
1131
|
+
plugins.sort((a, b) => (a.renderOrder ?? 0) - (b.renderOrder ?? 0)).forEach((plugin) => {
|
|
1132
|
+
if (plugin.render) {
|
|
1133
|
+
plugin.render?.(ctx, this);
|
|
1134
|
+
} else if (plugin.paths) {
|
|
1135
|
+
plugin.paths.forEach((path) => {
|
|
1136
|
+
drawPath({
|
|
1137
|
+
ctx,
|
|
1138
|
+
path,
|
|
1139
|
+
fontSize: this.computedStyle.fontSize
|
|
1140
|
+
});
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1095
1144
|
return this;
|
|
1096
1145
|
}
|
|
1097
1146
|
}
|
|
1098
1147
|
|
|
1099
|
-
export { Character,
|
|
1148
|
+
export { Character, Fragment, Measurer, Paragraph, Parser, Text, defaultTextStyles, definePlugin, drawPath, effect, fillBackground, filterEmpty, getTransform2D, highlight, isNone, listStyle, parseColor, setupView, uploadColor, uploadColors };
|