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.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,273 @@ class Parser extends Feature {
|
|
|
906
732
|
}
|
|
907
733
|
}
|
|
908
734
|
|
|
909
|
-
|
|
910
|
-
|
|
735
|
+
function plugin(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(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
|
+
});
|
|
940
769
|
});
|
|
941
|
-
|
|
942
|
-
|
|
770
|
+
return boxes.length ? 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);
|
|
943
832
|
}
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
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;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
function highlight(options = {}) {
|
|
849
|
+
const config = { ...defaultOptions, ...options };
|
|
850
|
+
const referPaths = 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: 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 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
|
+
}
|
|
957
979
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
980
|
+
if (!image) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const paddingLeft = fontSize * 0.45;
|
|
984
|
+
const imagePaths = parseSvg(image);
|
|
985
|
+
const imageBox = 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 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)));
|
|
961
997
|
}
|
|
962
998
|
});
|
|
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
|
-
}
|
|
999
|
+
},
|
|
1000
|
+
paths
|
|
1001
|
+
});
|
|
974
1002
|
}
|
|
975
1003
|
|
|
976
1004
|
var __defProp = Object.defineProperty;
|
|
@@ -980,35 +1008,49 @@ var __publicField = (obj, key, value) => {
|
|
|
980
1008
|
return value;
|
|
981
1009
|
};
|
|
982
1010
|
const defaultTextStyles = {
|
|
983
|
-
|
|
984
|
-
|
|
1011
|
+
writingMode: "horizontal-tb",
|
|
1012
|
+
verticalAlign: "baseline",
|
|
1013
|
+
lineHeight: 1,
|
|
1014
|
+
letterSpacing: 0,
|
|
1015
|
+
// font
|
|
985
1016
|
fontSize: 14,
|
|
986
1017
|
fontWeight: "normal",
|
|
987
1018
|
fontFamily: "_fallback",
|
|
988
1019
|
fontStyle: "normal",
|
|
989
1020
|
fontKerning: "normal",
|
|
1021
|
+
// text
|
|
990
1022
|
textWrap: "wrap",
|
|
991
1023
|
textAlign: "start",
|
|
992
|
-
verticalAlign: "baseline",
|
|
993
1024
|
textTransform: "none",
|
|
1025
|
+
textOrientation: "mixed",
|
|
1026
|
+
// color
|
|
1027
|
+
color: "#000",
|
|
1028
|
+
backgroundColor: "rgba(0, 0, 0, 0)",
|
|
1029
|
+
// text
|
|
994
1030
|
textDecoration: "none",
|
|
1031
|
+
// textStroke
|
|
995
1032
|
textStrokeWidth: 0,
|
|
996
1033
|
textStrokeColor: "#000",
|
|
997
|
-
|
|
998
|
-
letterSpacing: 0,
|
|
1034
|
+
// shadow
|
|
999
1035
|
shadowColor: "rgba(0, 0, 0, 0)",
|
|
1000
1036
|
shadowOffsetX: 0,
|
|
1001
1037
|
shadowOffsetY: 0,
|
|
1002
1038
|
shadowBlur: 0,
|
|
1003
|
-
|
|
1004
|
-
|
|
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"
|
|
1005
1049
|
};
|
|
1006
1050
|
class Text {
|
|
1007
1051
|
constructor(options = {}) {
|
|
1008
1052
|
__publicField(this, "content");
|
|
1009
1053
|
__publicField(this, "style");
|
|
1010
|
-
__publicField(this, "effects");
|
|
1011
|
-
__publicField(this, "deformation");
|
|
1012
1054
|
__publicField(this, "measureDom");
|
|
1013
1055
|
__publicField(this, "needsUpdate", true);
|
|
1014
1056
|
__publicField(this, "computedStyle", { ...defaultTextStyles });
|
|
@@ -1017,16 +1059,12 @@ class Text {
|
|
|
1017
1059
|
__publicField(this, "renderBoundingBox", new BoundingBox());
|
|
1018
1060
|
__publicField(this, "parser", new Parser(this));
|
|
1019
1061
|
__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;
|
|
1062
|
+
__publicField(this, "plugins", /* @__PURE__ */ new Map());
|
|
1063
|
+
const { content = "", style = {}, measureDom } = options;
|
|
1025
1064
|
this.content = content;
|
|
1026
1065
|
this.style = style;
|
|
1027
|
-
this.effects = effects;
|
|
1028
|
-
this.deformation = deformation;
|
|
1029
1066
|
this.measureDom = measureDom;
|
|
1067
|
+
this.use(effect(options.effects)).use(highlight(options.highlight)).use(listStyle());
|
|
1030
1068
|
}
|
|
1031
1069
|
get fontSize() {
|
|
1032
1070
|
return this.computedStyle.fontSize;
|
|
@@ -1034,6 +1072,10 @@ class Text {
|
|
|
1034
1072
|
get characters() {
|
|
1035
1073
|
return this.paragraphs.flatMap((p) => p.fragments.flatMap((f) => f.characters));
|
|
1036
1074
|
}
|
|
1075
|
+
use(plugin) {
|
|
1076
|
+
this.plugins.set(plugin.name, plugin);
|
|
1077
|
+
return this;
|
|
1078
|
+
}
|
|
1037
1079
|
measure(dom = this.measureDom) {
|
|
1038
1080
|
this.computedStyle = { ...defaultTextStyles, ...this.style };
|
|
1039
1081
|
const old = this.paragraphs;
|
|
@@ -1052,15 +1094,23 @@ class Text {
|
|
|
1052
1094
|
this.boundingBox = boundingBox;
|
|
1053
1095
|
const characters = this.characters;
|
|
1054
1096
|
characters.forEach((c) => c.update());
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1097
|
+
const plugins = [...this.plugins.values()];
|
|
1098
|
+
plugins.sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
|
|
1099
|
+
plugin.update?.(this);
|
|
1100
|
+
});
|
|
1060
1101
|
const min = Vector2.MAX;
|
|
1061
1102
|
const max = Vector2.MIN;
|
|
1062
1103
|
characters.forEach((c) => c.getGlyphMinMax(min, max));
|
|
1063
1104
|
this.renderBoundingBox = new BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
|
|
1105
|
+
this.renderBoundingBox = BoundingBox.from(
|
|
1106
|
+
this.renderBoundingBox,
|
|
1107
|
+
...plugins.map((plugin) => {
|
|
1108
|
+
if (plugin.getBoundingBox) {
|
|
1109
|
+
return plugin.getBoundingBox(this);
|
|
1110
|
+
}
|
|
1111
|
+
return getPathsBoundingBox(plugin.paths ?? []);
|
|
1112
|
+
}).filter(Boolean)
|
|
1113
|
+
);
|
|
1064
1114
|
return this;
|
|
1065
1115
|
}
|
|
1066
1116
|
render(options) {
|
|
@@ -1072,28 +1122,24 @@ class Text {
|
|
|
1072
1122
|
if (this.needsUpdate) {
|
|
1073
1123
|
this.update();
|
|
1074
1124
|
}
|
|
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
|
-
}
|
|
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
|
+
});
|
|
1095
1141
|
return this;
|
|
1096
1142
|
}
|
|
1097
1143
|
}
|
|
1098
1144
|
|
|
1099
|
-
export { Character,
|
|
1145
|
+
export { Character, Fragment, Measurer, Paragraph, Parser, Text, defaultTextStyles, drawPath, effect, fillBackground, filterEmpty, getTransform2D, highlight, isNone, listStyle, parseColor, plugin, setupView, uploadColor, uploadColors };
|