html2canvas-pro 2.0.3 → 2.0.4

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * html2canvas-pro 2.0.3 <https://yorickshan.github.io/html2canvas-pro/>
2
+ * html2canvas-pro 2.0.4 <https://yorickshan.github.io/html2canvas-pro/>
3
3
  * Copyright (c) 2024-present yorickshan and html2canvas-pro contributors
4
4
  * Released under MIT License
5
5
  */
@@ -4239,6 +4239,30 @@ const wordBreak = {
4239
4239
  }
4240
4240
  };
4241
4241
 
4242
+ const isVerticalWritingMode = (writingMode) => writingMode !== 0 /* WRITING_MODE.HORIZONTAL_TB */;
4243
+ const isSidewaysWritingMode = (writingMode) => writingMode === 3 /* WRITING_MODE.SIDEWAYS_RL */ || writingMode === 4 /* WRITING_MODE.SIDEWAYS_LR */;
4244
+ const writingMode = {
4245
+ name: 'writing-mode',
4246
+ initialValue: 'horizontal-tb',
4247
+ prefix: false,
4248
+ type: 2 /* PropertyDescriptorParsingType.IDENT_VALUE */,
4249
+ parse: (_context, writingMode) => {
4250
+ switch (writingMode) {
4251
+ case 'vertical-rl':
4252
+ return 1 /* WRITING_MODE.VERTICAL_RL */;
4253
+ case 'vertical-lr':
4254
+ return 2 /* WRITING_MODE.VERTICAL_LR */;
4255
+ case 'sideways-rl':
4256
+ return 3 /* WRITING_MODE.SIDEWAYS_RL */;
4257
+ case 'sideways-lr':
4258
+ return 4 /* WRITING_MODE.SIDEWAYS_LR */;
4259
+ case 'horizontal-tb':
4260
+ default:
4261
+ return 0 /* WRITING_MODE.HORIZONTAL_TB */;
4262
+ }
4263
+ }
4264
+ };
4265
+
4242
4266
  const zIndex = {
4243
4267
  name: 'z-index',
4244
4268
  initialValue: 'auto',
@@ -4855,6 +4879,7 @@ class CSSParsedDeclaration {
4855
4879
  this.webkitTextStrokeWidth = parse(context, webkitTextStrokeWidth, declaration.webkitTextStrokeWidth);
4856
4880
  this.webkitLineClamp = parse(context, webkitLineClamp, declaration.webkitLineClamp);
4857
4881
  this.wordBreak = parse(context, wordBreak, declaration.wordBreak);
4882
+ this.writingMode = parse(context, writingMode, declaration.writingMode);
4858
4883
  this.zIndex = parse(context, zIndex, declaration.zIndex);
4859
4884
  this.objectFit = parse(context, objectFit, declaration.objectFit);
4860
4885
  this.imageRendering = parse(context, imageRendering, declaration.imageRendering);
@@ -5790,6 +5815,9 @@ const segmentWords = (value, styles) => {
5790
5815
  return breakWords(value, styles);
5791
5816
  };
5792
5817
  const breakText = (value, styles) => {
5818
+ if (isVerticalWritingMode(styles.writingMode)) {
5819
+ return segmentGraphemes(value);
5820
+ }
5793
5821
  return styles.letterSpacing !== 0 ? segmentGraphemes(value) : segmentWords(value, styles);
5794
5822
  };
5795
5823
  // https://drafts.csswg.org/css-text/#word-separator
@@ -5829,6 +5857,11 @@ const breakWords = (str, styles) => {
5829
5857
  class TextContainer {
5830
5858
  constructor(context, node, styles) {
5831
5859
  this.text = transform(node.data, styles.textTransform);
5860
+ // Range offsets below are based on transformed text; keep the node in sync
5861
+ // when casing changes string length, for example "ß".toUpperCase() === "SS".
5862
+ if (this.text.length !== node.data.length) {
5863
+ node.data = this.text;
5864
+ }
5832
5865
  this.textBounds = parseTextBounds(context, this.text, styles, node);
5833
5866
  }
5834
5867
  }
@@ -8717,7 +8750,11 @@ class TextRenderer {
8717
8750
  * call (fillText or strokeText), allowing fill and stroke paths to share one
8718
8751
  * implementation.
8719
8752
  */
8720
- iterateLettersWithLetterSpacing(text, letterSpacing, baseline, renderFn) {
8753
+ iterateLettersWithLetterSpacing(text, letterSpacing, baseline, writingMode, renderFn) {
8754
+ if (isVerticalWritingMode(writingMode)) {
8755
+ this.iterateVerticalGlyphs(text, letterSpacing, baseline, writingMode, renderFn);
8756
+ return;
8757
+ }
8721
8758
  const letters = segmentGraphemes(text.text);
8722
8759
  const y = text.bounds.top + baseline;
8723
8760
  let left = text.bounds.left;
@@ -8734,17 +8771,39 @@ class TextRenderer {
8734
8771
  left += this.ctx.measureText(letter).width + letterSpacing;
8735
8772
  }
8736
8773
  }
8774
+ iterateVerticalGlyphs(text, letterSpacing, baseline, writingMode, renderFn) {
8775
+ const letters = segmentGraphemes(text.text);
8776
+ let top = text.bounds.top;
8777
+ for (const letter of letters) {
8778
+ if (isSidewaysWritingMode(writingMode) || (!hasCJKCharacters(letter) && letter.trim().length > 0)) {
8779
+ this.ctx.save();
8780
+ this.ctx.translate(text.bounds.left + baseline, top);
8781
+ this.ctx.rotate(writingMode === 4 /* WRITING_MODE.SIDEWAYS_LR */ ? -Math.PI / 2 : Math.PI / 2);
8782
+ renderFn(letter, 0, 0);
8783
+ this.ctx.restore();
8784
+ }
8785
+ else {
8786
+ const savedBaseline = this.ctx.textBaseline;
8787
+ if (hasCJKCharacters(letter)) {
8788
+ this.ctx.textBaseline = 'ideographic';
8789
+ }
8790
+ renderFn(letter, text.bounds.left, top + baseline);
8791
+ this.ctx.textBaseline = savedBaseline;
8792
+ }
8793
+ top += this.ctx.measureText(letter).width + letterSpacing;
8794
+ }
8795
+ }
8737
8796
  /**
8738
8797
  * Render text with letter-spacing applied (fill pass).
8739
8798
  * When letterSpacing is 0 the whole string is drawn in one call; otherwise each
8740
8799
  * grapheme is drawn individually so spacing and CJK baseline are applied correctly.
8741
8800
  */
8742
- renderTextWithLetterSpacing(text, letterSpacing, baseline) {
8743
- if (letterSpacing === 0) {
8801
+ renderTextWithLetterSpacing(text, letterSpacing, baseline, writingMode = 0 /* WRITING_MODE.HORIZONTAL_TB */) {
8802
+ if (letterSpacing === 0 && !isVerticalWritingMode(writingMode)) {
8744
8803
  this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
8745
8804
  }
8746
8805
  else {
8747
- this.iterateLettersWithLetterSpacing(text, letterSpacing, baseline, (letter, x, y) => {
8806
+ this.iterateLettersWithLetterSpacing(text, letterSpacing, baseline, writingMode, (letter, x, y) => {
8748
8807
  this.ctx.fillText(letter, x, y);
8749
8808
  });
8750
8809
  }
@@ -8758,7 +8817,7 @@ class TextRenderer {
8758
8817
  switch (paintOrderLayer) {
8759
8818
  case 0 /* PAINT_ORDER_LAYER.FILL */:
8760
8819
  this.ctx.fillStyle = asString(styles.color);
8761
- this.renderTextWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number);
8820
+ this.renderTextWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
8762
8821
  break;
8763
8822
  case 1 /* PAINT_ORDER_LAYER.STROKE */:
8764
8823
  if (styles.webkitTextStrokeWidth && textBound.text.trim().length) {
@@ -8766,11 +8825,11 @@ class TextRenderer {
8766
8825
  this.ctx.lineWidth = styles.webkitTextStrokeWidth;
8767
8826
  this.ctx.lineJoin =
8768
8827
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
8769
- if (styles.letterSpacing === 0) {
8828
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
8770
8829
  this.ctx.strokeText(textBound.text, textBound.bounds.left, textBound.bounds.top + styles.fontSize.number);
8771
8830
  }
8772
8831
  else {
8773
- this.iterateLettersWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.strokeText(letter, x, y));
8832
+ this.iterateLettersWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
8774
8833
  }
8775
8834
  this.ctx.strokeStyle = '';
8776
8835
  this.ctx.lineWidth = 0;
@@ -9006,11 +9065,11 @@ class TextRenderer {
9006
9065
  switch (paintOrderLayer) {
9007
9066
  case 0 /* PAINT_ORDER_LAYER.FILL */:
9008
9067
  this.ctx.fillStyle = asString(styles.color);
9009
- if (styles.letterSpacing === 0) {
9068
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9010
9069
  this.ctx.fillText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9011
9070
  }
9012
9071
  else {
9013
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.fillText(letter, x, y));
9072
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.fillText(letter, x, y));
9014
9073
  }
9015
9074
  break;
9016
9075
  case 1 /* PAINT_ORDER_LAYER.STROKE */:
@@ -9019,11 +9078,11 @@ class TextRenderer {
9019
9078
  this.ctx.lineWidth = styles.webkitTextStrokeWidth;
9020
9079
  this.ctx.lineJoin =
9021
9080
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
9022
- if (styles.letterSpacing === 0) {
9081
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9023
9082
  this.ctx.strokeText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9024
9083
  }
9025
9084
  else {
9026
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9085
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9027
9086
  }
9028
9087
  this.ctx.strokeStyle = '';
9029
9088
  this.ctx.lineWidth = 0;
@@ -9078,11 +9137,11 @@ class TextRenderer {
9078
9137
  switch (paintOrderLayer) {
9079
9138
  case 0 /* PAINT_ORDER_LAYER.FILL */: {
9080
9139
  this.ctx.fillStyle = asString(styles.color);
9081
- if (styles.letterSpacing === 0) {
9140
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9082
9141
  this.ctx.fillText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9083
9142
  }
9084
9143
  else {
9085
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.fillText(letter, x, y));
9144
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.fillText(letter, x, y));
9086
9145
  }
9087
9146
  const textShadows = styles.textShadow;
9088
9147
  if (textShadows.length && truncatedText.trim().length) {
@@ -9094,11 +9153,11 @@ class TextRenderer {
9094
9153
  this.ctx.shadowOffsetX = textShadow.offsetX.number * this.options.scale;
9095
9154
  this.ctx.shadowOffsetY = textShadow.offsetY.number * this.options.scale;
9096
9155
  this.ctx.shadowBlur = textShadow.blur.number;
9097
- if (styles.letterSpacing === 0) {
9156
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9098
9157
  this.ctx.fillText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9099
9158
  }
9100
9159
  else {
9101
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.fillText(letter, x, y));
9160
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.fillText(letter, x, y));
9102
9161
  }
9103
9162
  });
9104
9163
  this.ctx.shadowColor = '';
@@ -9114,11 +9173,11 @@ class TextRenderer {
9114
9173
  this.ctx.lineWidth = styles.webkitTextStrokeWidth;
9115
9174
  this.ctx.lineJoin =
9116
9175
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
9117
- if (styles.letterSpacing === 0) {
9176
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9118
9177
  this.ctx.strokeText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9119
9178
  }
9120
9179
  else {
9121
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9180
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9122
9181
  }
9123
9182
  this.ctx.strokeStyle = '';
9124
9183
  this.ctx.lineWidth = 0;
@@ -9135,7 +9194,7 @@ class TextRenderer {
9135
9194
  switch (paintOrderLayer) {
9136
9195
  case 0 /* PAINT_ORDER_LAYER.FILL */: {
9137
9196
  this.ctx.fillStyle = asString(styles.color);
9138
- this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number);
9197
+ this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9139
9198
  const textShadows = styles.textShadow;
9140
9199
  if (textShadows.length && text.text.trim().length) {
9141
9200
  textShadows
@@ -9146,7 +9205,7 @@ class TextRenderer {
9146
9205
  this.ctx.shadowOffsetX = textShadow.offsetX.number * this.options.scale;
9147
9206
  this.ctx.shadowOffsetY = textShadow.offsetY.number * this.options.scale;
9148
9207
  this.ctx.shadowBlur = textShadow.blur.number;
9149
- this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number);
9208
+ this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9150
9209
  });
9151
9210
  this.ctx.shadowColor = '';
9152
9211
  this.ctx.shadowOffsetX = 0;
@@ -9165,11 +9224,11 @@ class TextRenderer {
9165
9224
  this.ctx.lineJoin =
9166
9225
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
9167
9226
  const baseline = styles.fontSize.number;
9168
- if (styles.letterSpacing === 0) {
9227
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9169
9228
  this.ctx.strokeText(text.text, text.bounds.left, text.bounds.top + baseline);
9170
9229
  }
9171
9230
  else {
9172
- this.iterateLettersWithLetterSpacing(text, styles.letterSpacing, baseline, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9231
+ this.iterateLettersWithLetterSpacing(text, styles.letterSpacing, baseline, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9173
9232
  }
9174
9233
  this.ctx.strokeStyle = '';
9175
9234
  this.ctx.lineWidth = 0;
@@ -9464,7 +9523,7 @@ class CanvasRenderer extends Renderer {
9464
9523
  new Vector(bounds.left, bounds.top + bounds.height)
9465
9524
  ]);
9466
9525
  this.ctx.clip();
9467
- this.textRenderer.renderTextWithLetterSpacing(new TextBounds(container.value, textBounds), styles.letterSpacing, baseline);
9526
+ this.textRenderer.renderTextWithLetterSpacing(new TextBounds(container.value, textBounds), styles.letterSpacing, baseline, styles.writingMode);
9468
9527
  this.ctx.restore();
9469
9528
  this.ctx.textBaseline = 'alphabetic';
9470
9529
  this.ctx.textAlign = 'left';
@@ -9491,7 +9550,7 @@ class CanvasRenderer extends Renderer {
9491
9550
  this.ctx.textBaseline = 'middle';
9492
9551
  this.ctx.textAlign = 'right';
9493
9552
  const bounds = new Bounds(container.bounds.left, container.bounds.top + getAbsoluteValue(container.styles.paddingTop, container.bounds.width), container.bounds.width, computeLineHeight(styles.lineHeight, styles.fontSize.number) / 2 + 1);
9494
- this.textRenderer.renderTextWithLetterSpacing(new TextBounds(paint.listValue, bounds), styles.letterSpacing, computeLineHeight(styles.lineHeight, styles.fontSize.number) / 2 + 2);
9553
+ this.textRenderer.renderTextWithLetterSpacing(new TextBounds(paint.listValue, bounds), styles.letterSpacing, computeLineHeight(styles.lineHeight, styles.fontSize.number) / 2 + 2, styles.writingMode);
9495
9554
  this.ctx.textBaseline = 'bottom';
9496
9555
  this.ctx.textAlign = 'left';
9497
9556
  }