modern-text 0.2.37 → 0.2.39

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