html2canvas-pro 2.0.2 → 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.2 <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
  */
@@ -4245,6 +4245,30 @@
4245
4245
  }
4246
4246
  };
4247
4247
 
4248
+ const isVerticalWritingMode = (writingMode) => writingMode !== 0 /* WRITING_MODE.HORIZONTAL_TB */;
4249
+ const isSidewaysWritingMode = (writingMode) => writingMode === 3 /* WRITING_MODE.SIDEWAYS_RL */ || writingMode === 4 /* WRITING_MODE.SIDEWAYS_LR */;
4250
+ const writingMode = {
4251
+ name: 'writing-mode',
4252
+ initialValue: 'horizontal-tb',
4253
+ prefix: false,
4254
+ type: 2 /* PropertyDescriptorParsingType.IDENT_VALUE */,
4255
+ parse: (_context, writingMode) => {
4256
+ switch (writingMode) {
4257
+ case 'vertical-rl':
4258
+ return 1 /* WRITING_MODE.VERTICAL_RL */;
4259
+ case 'vertical-lr':
4260
+ return 2 /* WRITING_MODE.VERTICAL_LR */;
4261
+ case 'sideways-rl':
4262
+ return 3 /* WRITING_MODE.SIDEWAYS_RL */;
4263
+ case 'sideways-lr':
4264
+ return 4 /* WRITING_MODE.SIDEWAYS_LR */;
4265
+ case 'horizontal-tb':
4266
+ default:
4267
+ return 0 /* WRITING_MODE.HORIZONTAL_TB */;
4268
+ }
4269
+ }
4270
+ };
4271
+
4248
4272
  const zIndex = {
4249
4273
  name: 'z-index',
4250
4274
  initialValue: 'auto',
@@ -4861,6 +4885,7 @@
4861
4885
  this.webkitTextStrokeWidth = parse(context, webkitTextStrokeWidth, declaration.webkitTextStrokeWidth);
4862
4886
  this.webkitLineClamp = parse(context, webkitLineClamp, declaration.webkitLineClamp);
4863
4887
  this.wordBreak = parse(context, wordBreak, declaration.wordBreak);
4888
+ this.writingMode = parse(context, writingMode, declaration.writingMode);
4864
4889
  this.zIndex = parse(context, zIndex, declaration.zIndex);
4865
4890
  this.objectFit = parse(context, objectFit, declaration.objectFit);
4866
4891
  this.imageRendering = parse(context, imageRendering, declaration.imageRendering);
@@ -5796,6 +5821,9 @@
5796
5821
  return breakWords(value, styles);
5797
5822
  };
5798
5823
  const breakText = (value, styles) => {
5824
+ if (isVerticalWritingMode(styles.writingMode)) {
5825
+ return segmentGraphemes(value);
5826
+ }
5799
5827
  return styles.letterSpacing !== 0 ? segmentGraphemes(value) : segmentWords(value, styles);
5800
5828
  };
5801
5829
  // https://drafts.csswg.org/css-text/#word-separator
@@ -5835,6 +5863,11 @@
5835
5863
  class TextContainer {
5836
5864
  constructor(context, node, styles) {
5837
5865
  this.text = transform(node.data, styles.textTransform);
5866
+ // Range offsets below are based on transformed text; keep the node in sync
5867
+ // when casing changes string length, for example "ß".toUpperCase() === "SS".
5868
+ if (this.text.length !== node.data.length) {
5869
+ node.data = this.text;
5870
+ }
5838
5871
  this.textBounds = parseTextBounds(context, this.text, styles, node);
5839
5872
  }
5840
5873
  }
@@ -6592,10 +6625,11 @@
6592
6625
  return iframe;
6593
6626
  });
6594
6627
  /**
6595
- * The baseURI of the document will be lost after documentClone.open().
6596
- * We save it before open() to preserve the original base URI for resource resolution.
6597
- * */
6598
- const baseUri = documentClone.baseURI;
6628
+ * The base URI used for resolving relative URLs (e.g. background-image) in the clone.
6629
+ * Must come from the source document: the iframe document is about:blank, so
6630
+ * documentClone.baseURI would break getComputedStyle() for relative background URLs.
6631
+ */
6632
+ const baseUri = ownerDocument.baseURI;
6599
6633
  documentClone.open();
6600
6634
  const rawHTML = serializeDoctype(document.doctype) + '<html></html>';
6601
6635
  try {
@@ -8722,7 +8756,11 @@
8722
8756
  * call (fillText or strokeText), allowing fill and stroke paths to share one
8723
8757
  * implementation.
8724
8758
  */
8725
- iterateLettersWithLetterSpacing(text, letterSpacing, baseline, renderFn) {
8759
+ iterateLettersWithLetterSpacing(text, letterSpacing, baseline, writingMode, renderFn) {
8760
+ if (isVerticalWritingMode(writingMode)) {
8761
+ this.iterateVerticalGlyphs(text, letterSpacing, baseline, writingMode, renderFn);
8762
+ return;
8763
+ }
8726
8764
  const letters = segmentGraphemes(text.text);
8727
8765
  const y = text.bounds.top + baseline;
8728
8766
  let left = text.bounds.left;
@@ -8739,17 +8777,39 @@
8739
8777
  left += this.ctx.measureText(letter).width + letterSpacing;
8740
8778
  }
8741
8779
  }
8780
+ iterateVerticalGlyphs(text, letterSpacing, baseline, writingMode, renderFn) {
8781
+ const letters = segmentGraphemes(text.text);
8782
+ let top = text.bounds.top;
8783
+ for (const letter of letters) {
8784
+ if (isSidewaysWritingMode(writingMode) || (!hasCJKCharacters(letter) && letter.trim().length > 0)) {
8785
+ this.ctx.save();
8786
+ this.ctx.translate(text.bounds.left + baseline, top);
8787
+ this.ctx.rotate(writingMode === 4 /* WRITING_MODE.SIDEWAYS_LR */ ? -Math.PI / 2 : Math.PI / 2);
8788
+ renderFn(letter, 0, 0);
8789
+ this.ctx.restore();
8790
+ }
8791
+ else {
8792
+ const savedBaseline = this.ctx.textBaseline;
8793
+ if (hasCJKCharacters(letter)) {
8794
+ this.ctx.textBaseline = 'ideographic';
8795
+ }
8796
+ renderFn(letter, text.bounds.left, top + baseline);
8797
+ this.ctx.textBaseline = savedBaseline;
8798
+ }
8799
+ top += this.ctx.measureText(letter).width + letterSpacing;
8800
+ }
8801
+ }
8742
8802
  /**
8743
8803
  * Render text with letter-spacing applied (fill pass).
8744
8804
  * When letterSpacing is 0 the whole string is drawn in one call; otherwise each
8745
8805
  * grapheme is drawn individually so spacing and CJK baseline are applied correctly.
8746
8806
  */
8747
- renderTextWithLetterSpacing(text, letterSpacing, baseline) {
8748
- if (letterSpacing === 0) {
8807
+ renderTextWithLetterSpacing(text, letterSpacing, baseline, writingMode = 0 /* WRITING_MODE.HORIZONTAL_TB */) {
8808
+ if (letterSpacing === 0 && !isVerticalWritingMode(writingMode)) {
8749
8809
  this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
8750
8810
  }
8751
8811
  else {
8752
- this.iterateLettersWithLetterSpacing(text, letterSpacing, baseline, (letter, x, y) => {
8812
+ this.iterateLettersWithLetterSpacing(text, letterSpacing, baseline, writingMode, (letter, x, y) => {
8753
8813
  this.ctx.fillText(letter, x, y);
8754
8814
  });
8755
8815
  }
@@ -8763,7 +8823,7 @@
8763
8823
  switch (paintOrderLayer) {
8764
8824
  case 0 /* PAINT_ORDER_LAYER.FILL */:
8765
8825
  this.ctx.fillStyle = asString(styles.color);
8766
- this.renderTextWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number);
8826
+ this.renderTextWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
8767
8827
  break;
8768
8828
  case 1 /* PAINT_ORDER_LAYER.STROKE */:
8769
8829
  if (styles.webkitTextStrokeWidth && textBound.text.trim().length) {
@@ -8771,11 +8831,11 @@
8771
8831
  this.ctx.lineWidth = styles.webkitTextStrokeWidth;
8772
8832
  this.ctx.lineJoin =
8773
8833
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
8774
- if (styles.letterSpacing === 0) {
8834
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
8775
8835
  this.ctx.strokeText(textBound.text, textBound.bounds.left, textBound.bounds.top + styles.fontSize.number);
8776
8836
  }
8777
8837
  else {
8778
- this.iterateLettersWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.strokeText(letter, x, y));
8838
+ this.iterateLettersWithLetterSpacing(textBound, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
8779
8839
  }
8780
8840
  this.ctx.strokeStyle = '';
8781
8841
  this.ctx.lineWidth = 0;
@@ -9011,11 +9071,11 @@
9011
9071
  switch (paintOrderLayer) {
9012
9072
  case 0 /* PAINT_ORDER_LAYER.FILL */:
9013
9073
  this.ctx.fillStyle = asString(styles.color);
9014
- if (styles.letterSpacing === 0) {
9074
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9015
9075
  this.ctx.fillText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9016
9076
  }
9017
9077
  else {
9018
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.fillText(letter, x, y));
9078
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.fillText(letter, x, y));
9019
9079
  }
9020
9080
  break;
9021
9081
  case 1 /* PAINT_ORDER_LAYER.STROKE */:
@@ -9024,11 +9084,11 @@
9024
9084
  this.ctx.lineWidth = styles.webkitTextStrokeWidth;
9025
9085
  this.ctx.lineJoin =
9026
9086
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
9027
- if (styles.letterSpacing === 0) {
9087
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9028
9088
  this.ctx.strokeText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9029
9089
  }
9030
9090
  else {
9031
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9091
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9032
9092
  }
9033
9093
  this.ctx.strokeStyle = '';
9034
9094
  this.ctx.lineWidth = 0;
@@ -9083,11 +9143,11 @@
9083
9143
  switch (paintOrderLayer) {
9084
9144
  case 0 /* PAINT_ORDER_LAYER.FILL */: {
9085
9145
  this.ctx.fillStyle = asString(styles.color);
9086
- if (styles.letterSpacing === 0) {
9146
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9087
9147
  this.ctx.fillText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9088
9148
  }
9089
9149
  else {
9090
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.fillText(letter, x, y));
9150
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.fillText(letter, x, y));
9091
9151
  }
9092
9152
  const textShadows = styles.textShadow;
9093
9153
  if (textShadows.length && truncatedText.trim().length) {
@@ -9099,11 +9159,11 @@
9099
9159
  this.ctx.shadowOffsetX = textShadow.offsetX.number * this.options.scale;
9100
9160
  this.ctx.shadowOffsetY = textShadow.offsetY.number * this.options.scale;
9101
9161
  this.ctx.shadowBlur = textShadow.blur.number;
9102
- if (styles.letterSpacing === 0) {
9162
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9103
9163
  this.ctx.fillText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9104
9164
  }
9105
9165
  else {
9106
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.fillText(letter, x, y));
9166
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.fillText(letter, x, y));
9107
9167
  }
9108
9168
  });
9109
9169
  this.ctx.shadowColor = '';
@@ -9119,11 +9179,11 @@
9119
9179
  this.ctx.lineWidth = styles.webkitTextStrokeWidth;
9120
9180
  this.ctx.lineJoin =
9121
9181
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
9122
- if (styles.letterSpacing === 0) {
9182
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9123
9183
  this.ctx.strokeText(truncatedText, firstBound.bounds.left, firstBound.bounds.top + styles.fontSize.number);
9124
9184
  }
9125
9185
  else {
9126
- this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9186
+ this.iterateLettersWithLetterSpacing(truncatedBounds, styles.letterSpacing, styles.fontSize.number, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9127
9187
  }
9128
9188
  this.ctx.strokeStyle = '';
9129
9189
  this.ctx.lineWidth = 0;
@@ -9140,7 +9200,7 @@
9140
9200
  switch (paintOrderLayer) {
9141
9201
  case 0 /* PAINT_ORDER_LAYER.FILL */: {
9142
9202
  this.ctx.fillStyle = asString(styles.color);
9143
- this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number);
9203
+ this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9144
9204
  const textShadows = styles.textShadow;
9145
9205
  if (textShadows.length && text.text.trim().length) {
9146
9206
  textShadows
@@ -9151,7 +9211,7 @@
9151
9211
  this.ctx.shadowOffsetX = textShadow.offsetX.number * this.options.scale;
9152
9212
  this.ctx.shadowOffsetY = textShadow.offsetY.number * this.options.scale;
9153
9213
  this.ctx.shadowBlur = textShadow.blur.number;
9154
- this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number);
9214
+ this.renderTextWithLetterSpacing(text, styles.letterSpacing, styles.fontSize.number, styles.writingMode);
9155
9215
  });
9156
9216
  this.ctx.shadowColor = '';
9157
9217
  this.ctx.shadowOffsetX = 0;
@@ -9170,11 +9230,11 @@
9170
9230
  this.ctx.lineJoin =
9171
9231
  typeof window !== 'undefined' && !!window.chrome ? 'miter' : 'round';
9172
9232
  const baseline = styles.fontSize.number;
9173
- if (styles.letterSpacing === 0) {
9233
+ if (styles.letterSpacing === 0 && !isVerticalWritingMode(styles.writingMode)) {
9174
9234
  this.ctx.strokeText(text.text, text.bounds.left, text.bounds.top + baseline);
9175
9235
  }
9176
9236
  else {
9177
- this.iterateLettersWithLetterSpacing(text, styles.letterSpacing, baseline, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9237
+ this.iterateLettersWithLetterSpacing(text, styles.letterSpacing, baseline, styles.writingMode, (letter, x, y) => this.ctx.strokeText(letter, x, y));
9178
9238
  }
9179
9239
  this.ctx.strokeStyle = '';
9180
9240
  this.ctx.lineWidth = 0;
@@ -9469,7 +9529,7 @@
9469
9529
  new Vector(bounds.left, bounds.top + bounds.height)
9470
9530
  ]);
9471
9531
  this.ctx.clip();
9472
- this.textRenderer.renderTextWithLetterSpacing(new TextBounds(container.value, textBounds), styles.letterSpacing, baseline);
9532
+ this.textRenderer.renderTextWithLetterSpacing(new TextBounds(container.value, textBounds), styles.letterSpacing, baseline, styles.writingMode);
9473
9533
  this.ctx.restore();
9474
9534
  this.ctx.textBaseline = 'alphabetic';
9475
9535
  this.ctx.textAlign = 'left';
@@ -9496,7 +9556,7 @@
9496
9556
  this.ctx.textBaseline = 'middle';
9497
9557
  this.ctx.textAlign = 'right';
9498
9558
  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);
9499
- this.textRenderer.renderTextWithLetterSpacing(new TextBounds(paint.listValue, bounds), styles.letterSpacing, computeLineHeight(styles.lineHeight, styles.fontSize.number) / 2 + 2);
9559
+ this.textRenderer.renderTextWithLetterSpacing(new TextBounds(paint.listValue, bounds), styles.letterSpacing, computeLineHeight(styles.lineHeight, styles.fontSize.number) / 2 + 2, styles.writingMode);
9500
9560
  this.ctx.textBaseline = 'bottom';
9501
9561
  this.ctx.textAlign = 'left';
9502
9562
  }