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 CHANGED
@@ -76,10 +76,58 @@ function drawPath(options) {
76
76
  ctx.restore();
77
77
  }
78
78
 
79
- var __defProp$4 = Object.defineProperty;
80
- var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
81
- var __publicField$4 = (obj, key, value) => {
82
- __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
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$4(this, "boundingBox", new modernPath2d.BoundingBox());
118
- __publicField$4(this, "textWidth", 0);
119
- __publicField$4(this, "textHeight", 0);
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$4(this, "glyphHeight", 0);
122
- __publicField$4(this, "glyphWidth", 0);
123
- __publicField$4(this, "underlinePosition", 0);
124
- __publicField$4(this, "underlineThickness", 0);
125
- __publicField$4(this, "yStrikeoutPosition", 0);
126
- __publicField$4(this, "yStrikeoutSize", 0);
127
- __publicField$4(this, "baseline", 0);
128
- __publicField$4(this, "centerDiviation", 0);
129
- __publicField$4(this, "path", new modernPath2d.Path2D());
130
- __publicField$4(this, "glyphBox", new modernPath2d.BoundingBox());
131
- __publicField$4(this, "center", new modernPath2d.Vector2());
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$3 = Object.defineProperty;
358
- var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
359
- var __publicField$3 = (obj, key, value) => {
360
- __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
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$3(this, "boundingBox", new modernPath2d.BoundingBox());
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$2 = Object.defineProperty;
395
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
396
- var __publicField$2 = (obj, key, value) => {
397
- __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
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$2(this, "boundingBox", new modernPath2d.BoundingBox());
405
- __publicField$2(this, "fragments", []);
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 Feature {
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
- listStyle: "none",
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 extends Feature {
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, highlight, ...fStyle } = f;
704
+ const { content: content2, ...fStyle } = f;
877
705
  if (content2 !== void 0) {
878
- const fragment = paragraph.addFragment(content2, fStyle);
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, highlight, ...fStyle } = f;
715
+ const { content: content2, ...fStyle } = f;
889
716
  if (content2 !== void 0) {
890
- const fragment = paragraph.addFragment(content2, fStyle);
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, highlight, ...pStyle } = p;
722
+ const { content: pData, ...pStyle } = p;
897
723
  if (pData !== void 0) {
898
724
  const paragraph = new Paragraph(pStyle, style);
899
- const fragment = paragraph.addFragment(pData);
900
- fragment.highlight = highlight;
725
+ paragraph.addFragment(pData);
901
726
  paragraphs.push(paragraph);
902
727
  }
903
728
  }
@@ -907,71 +732,274 @@ class Parser extends Feature {
907
732
  }
908
733
  }
909
734
 
910
- class Reflector extends Feature {
911
- // TODO
735
+ function definePlugin(options) {
736
+ return options;
912
737
  }
913
738
 
914
- class Renderer2D extends Feature {
915
- setupView(options) {
916
- const { ctx, pixelRatio } = options;
917
- const { renderBoundingBox } = this._text;
918
- const { left, top, width, height } = renderBoundingBox;
919
- const view = ctx.canvas;
920
- view.dataset.viewbox = String(`${left} ${top} ${width} ${height}`);
921
- view.dataset.pixelRatio = String(pixelRatio);
922
- view.width = Math.max(1, Math.ceil(width * pixelRatio));
923
- view.height = Math.max(1, Math.ceil(height * pixelRatio));
924
- view.style.marginTop = `${top}px`;
925
- view.style.marginLeft = `${left}px`;
926
- view.style.width = `${width}px`;
927
- view.style.height = `${height}px`;
928
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
929
- ctx.scale(pixelRatio, pixelRatio);
930
- ctx.translate(-left, -top);
931
- return this;
932
- }
933
- uploadColors(options) {
934
- const { ctx } = options;
935
- const { paragraphs, computedStyle: style, renderBoundingBox } = this._text;
936
- uploadColor(style, renderBoundingBox, ctx);
937
- paragraphs.forEach((paragraph) => {
938
- uploadColor(paragraph.computedStyle, paragraph.boundingBox, ctx);
939
- paragraph.fragments.forEach((fragment) => {
940
- uploadColor(fragment.computedStyle, fragment.boundingBox, ctx);
739
+ const tempV1 = new modernPath2d.Vector2();
740
+ const tempM1 = new modernPath2d.Matrix3();
741
+ const tempM2 = new modernPath2d.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
+ });
941
769
  });
942
- });
943
- return this;
770
+ return boxes.length ? modernPath2d.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;
944
846
  }
945
- fillBackground(options) {
946
- const { ctx } = options;
947
- const { computedStyle: style, paragraphs } = this._text;
948
- function fillBackground(color, x, y, width, height) {
949
- ctx.fillStyle = color;
950
- ctx.fillRect(x, y, width, height);
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 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: 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
+ });
951
939
  }
952
- if (style?.backgroundColor) {
953
- fillBackground(style.backgroundColor, 0, 0, ctx.canvas.width, ctx.canvas.height);
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
954
  }
955
- paragraphs.forEach((paragraph) => {
956
- if (paragraph.style?.backgroundColor) {
957
- fillBackground(paragraph.computedStyle.backgroundColor, ...paragraph.boundingBox.toArray());
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
+ }
958
981
  }
959
- paragraph.fragments.forEach((fragment) => {
960
- if (fragment.style?.backgroundColor) {
961
- fillBackground(fragment.computedStyle.backgroundColor, ...fragment.boundingBox.toArray());
982
+ if (!image) {
983
+ return;
984
+ }
985
+ const paddingLeft = fontSize * 0.45;
986
+ const imagePaths = modernPath2d.parseSvg(image);
987
+ const imageBox = modernPath2d.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 modernPath2d.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)));
962
999
  }
963
1000
  });
964
- });
965
- return this;
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
- }
1001
+ }
1002
+ });
975
1003
  }
976
1004
 
977
1005
  var __defProp = Object.defineProperty;
@@ -981,35 +1009,50 @@ var __publicField = (obj, key, value) => {
981
1009
  return value;
982
1010
  };
983
1011
  const defaultTextStyles = {
984
- color: "#000",
985
- backgroundColor: "rgba(0, 0, 0, 0)",
1012
+ writingMode: "horizontal-tb",
1013
+ verticalAlign: "baseline",
1014
+ lineHeight: 1,
1015
+ letterSpacing: 0,
1016
+ // font
986
1017
  fontSize: 14,
987
1018
  fontWeight: "normal",
988
1019
  fontFamily: "_fallback",
989
1020
  fontStyle: "normal",
990
1021
  fontKerning: "normal",
1022
+ // text
991
1023
  textWrap: "wrap",
992
1024
  textAlign: "start",
993
- verticalAlign: "baseline",
994
1025
  textTransform: "none",
1026
+ textOrientation: "mixed",
1027
+ // color
1028
+ color: "#000",
1029
+ backgroundColor: "rgba(0, 0, 0, 0)",
1030
+ // text
995
1031
  textDecoration: "none",
1032
+ // textStroke
996
1033
  textStrokeWidth: 0,
997
1034
  textStrokeColor: "#000",
998
- lineHeight: 1,
999
- letterSpacing: 0,
1035
+ // shadow
1000
1036
  shadowColor: "rgba(0, 0, 0, 0)",
1001
1037
  shadowOffsetX: 0,
1002
1038
  shadowOffsetY: 0,
1003
1039
  shadowBlur: 0,
1004
- writingMode: "horizontal-tb",
1005
- textOrientation: "mixed"
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"
1006
1050
  };
1007
1051
  class Text {
1008
1052
  constructor(options = {}) {
1009
1053
  __publicField(this, "content");
1010
1054
  __publicField(this, "style");
1011
1055
  __publicField(this, "effects");
1012
- __publicField(this, "deformation");
1013
1056
  __publicField(this, "measureDom");
1014
1057
  __publicField(this, "needsUpdate", true);
1015
1058
  __publicField(this, "computedStyle", { ...defaultTextStyles });
@@ -1018,16 +1061,13 @@ class Text {
1018
1061
  __publicField(this, "renderBoundingBox", new modernPath2d.BoundingBox());
1019
1062
  __publicField(this, "parser", new Parser(this));
1020
1063
  __publicField(this, "measurer", new Measurer(this));
1021
- __publicField(this, "deformer", new Deformer(this));
1022
- __publicField(this, "effector", new Effector(this));
1023
- __publicField(this, "highlighter", new Highlighter(this));
1024
- __publicField(this, "renderer2D", new Renderer2D(this));
1025
- const { content = "", style = {}, effects, deformation, measureDom } = options;
1064
+ __publicField(this, "plugins", /* @__PURE__ */ new Map());
1065
+ const { content = "", style = {}, measureDom, effects } = options;
1026
1066
  this.content = content;
1027
1067
  this.style = style;
1028
- this.effects = effects;
1029
- this.deformation = deformation;
1030
1068
  this.measureDom = measureDom;
1069
+ this.effects = effects;
1070
+ this.use(effect()).use(highlight(options.highlight)).use(listStyle());
1031
1071
  }
1032
1072
  get fontSize() {
1033
1073
  return this.computedStyle.fontSize;
@@ -1035,6 +1075,10 @@ class Text {
1035
1075
  get characters() {
1036
1076
  return this.paragraphs.flatMap((p) => p.fragments.flatMap((f) => f.characters));
1037
1077
  }
1078
+ use(plugin) {
1079
+ this.plugins.set(plugin.name, plugin);
1080
+ return this;
1081
+ }
1038
1082
  measure(dom = this.measureDom) {
1039
1083
  this.computedStyle = { ...defaultTextStyles, ...this.style };
1040
1084
  const old = this.paragraphs;
@@ -1053,15 +1097,23 @@ class Text {
1053
1097
  this.boundingBox = boundingBox;
1054
1098
  const characters = this.characters;
1055
1099
  characters.forEach((c) => c.update());
1056
- if (this.deformation) {
1057
- this.deformer.deform();
1058
- } else {
1059
- this.highlighter.highlight();
1060
- }
1100
+ const plugins = [...this.plugins.values()];
1101
+ plugins.sort((a, b) => (a.updateOrder ?? 0) - (b.updateOrder ?? 0)).forEach((plugin) => {
1102
+ plugin.update?.(this);
1103
+ });
1061
1104
  const min = modernPath2d.Vector2.MAX;
1062
1105
  const max = modernPath2d.Vector2.MIN;
1063
1106
  characters.forEach((c) => c.getGlyphMinMax(min, max));
1064
1107
  this.renderBoundingBox = new modernPath2d.BoundingBox(min.x, min.y, max.x - min.x, max.y - min.y);
1108
+ this.renderBoundingBox = modernPath2d.BoundingBox.from(
1109
+ this.renderBoundingBox,
1110
+ ...plugins.map((plugin) => {
1111
+ if (plugin.getBoundingBox) {
1112
+ return plugin.getBoundingBox(this);
1113
+ }
1114
+ return modernPath2d.getPathsBoundingBox(plugin.paths ?? []);
1115
+ }).filter(Boolean)
1116
+ );
1065
1117
  return this;
1066
1118
  }
1067
1119
  render(options) {
@@ -1073,47 +1125,49 @@ class Text {
1073
1125
  if (this.needsUpdate) {
1074
1126
  this.update();
1075
1127
  }
1076
- if (this.effects?.length) {
1077
- this.renderBoundingBox = modernPath2d.BoundingBox.from(...[
1078
- this.renderBoundingBox,
1079
- this.effector.getBoundingBox(),
1080
- this.highlighter.getBoundingBox()
1081
- ].filter(Boolean));
1082
- } else {
1083
- this.renderBoundingBox = modernPath2d.BoundingBox.from(...[
1084
- this.renderBoundingBox,
1085
- this.highlighter.getBoundingBox()
1086
- ].filter(Boolean));
1087
- }
1088
- this.renderer2D.setupView({ pixelRatio, ctx });
1089
- this.renderer2D.uploadColors({ ctx });
1090
- this.highlighter.draw({ ctx });
1091
- if (this.effects?.length) {
1092
- this.effector.draw({ ctx });
1093
- } else {
1094
- this.renderer2D.draw({ ctx });
1095
- }
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
+ });
1096
1144
  return this;
1097
1145
  }
1098
1146
  }
1099
1147
 
1100
1148
  exports.Character = Character;
1101
- exports.Deformer = Deformer;
1102
- exports.Effector = Effector;
1103
1149
  exports.Fragment = Fragment;
1104
- exports.Highlighter = Highlighter;
1105
1150
  exports.Measurer = Measurer;
1106
1151
  exports.Paragraph = Paragraph;
1107
1152
  exports.Parser = Parser;
1108
- exports.Reflector = Reflector;
1109
- exports.Renderer2D = Renderer2D;
1110
1153
  exports.Text = Text;
1111
- exports.defaultHighlightRefer = defaultHighlightRefer;
1112
1154
  exports.defaultTextStyles = defaultTextStyles;
1155
+ exports.definePlugin = definePlugin;
1113
1156
  exports.drawPath = drawPath;
1157
+ exports.effect = effect;
1158
+ exports.fillBackground = fillBackground;
1114
1159
  exports.filterEmpty = filterEmpty;
1160
+ exports.getTransform2D = getTransform2D;
1161
+ exports.highlight = highlight;
1162
+ exports.isNone = isNone;
1163
+ exports.listStyle = listStyle;
1115
1164
  exports.parseColor = parseColor;
1165
+ exports.setupView = setupView;
1116
1166
  exports.uploadColor = uploadColor;
1167
+ exports.uploadColors = uploadColors;
1168
+ Object.keys(modernPath2d).forEach(function (k) {
1169
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = modernPath2d[k];
1170
+ });
1117
1171
  Object.keys(modernFont).forEach(function (k) {
1118
1172
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = modernFont[k];
1119
1173
  });