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