html2canvas-pro 1.5.12 → 1.6.0

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 1.5.12 <https://yorickshan.github.io/html2canvas-pro/>
2
+ * html2canvas-pro 1.6.0 <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
  */
@@ -118,7 +118,19 @@
118
118
  return new Bounds(clientRect.left + context.windowBounds.left, clientRect.top + context.windowBounds.top, clientRect.width, clientRect.height);
119
119
  };
120
120
  Bounds.fromDOMRectList = function (context, domRectList) {
121
- var domRect = Array.from(domRectList).find(function (rect) { return rect.width !== 0; });
121
+ var rects = Array.from(domRectList);
122
+ // First try to find a rect with non-zero width
123
+ var domRect = rects.find(function (rect) { return rect.width !== 0; });
124
+ // If not found, try to find a rect with non-zero height
125
+ // This handles cases like inline-flex with single child where width might be 0
126
+ if (!domRect) {
127
+ domRect = rects.find(function (rect) { return rect.height !== 0; });
128
+ }
129
+ // If still not found but rects exist, use the first rect
130
+ // Position info (left, top) might still be valid even if dimensions are 0
131
+ if (!domRect && rects.length > 0) {
132
+ domRect = rects[0];
133
+ }
122
134
  return domRect
123
135
  ? new Bounds(domRect.left + context.windowBounds.left, domRect.top + context.windowBounds.top, domRect.width, domRect.height)
124
136
  : Bounds.EMPTY;
@@ -1610,6 +1622,94 @@
1610
1622
  var isLengthPercentage = function (token) {
1611
1623
  return token.type === 16 /* TokenType.PERCENTAGE_TOKEN */ || isLength(token);
1612
1624
  };
1625
+ /**
1626
+ * Check if a token is a calc() function
1627
+ */
1628
+ var isCalcFunction = function (token) {
1629
+ return token.type === 18 /* TokenType.FUNCTION */ && token.name === 'calc';
1630
+ };
1631
+ /**
1632
+ * Evaluate a calc() expression and convert to LengthPercentage token
1633
+ * Supports basic arithmetic: +, -, *, /
1634
+ * Note: Percentages in calc() are converted based on a context value
1635
+ */
1636
+ var evaluateCalcToLengthPercentage = function (calcToken, contextValue) {
1637
+ if (contextValue === void 0) { contextValue = 0; }
1638
+ // Build expression string from tokens
1639
+ var buildExpression = function (values) {
1640
+ var expression = '';
1641
+ for (var _i = 0, values_1 = values; _i < values_1.length; _i++) {
1642
+ var value = values_1[_i];
1643
+ if (value.type === 31 /* TokenType.WHITESPACE_TOKEN */) {
1644
+ continue;
1645
+ }
1646
+ if (value.type === 18 /* TokenType.FUNCTION */) {
1647
+ if (value.name === 'calc') {
1648
+ var nested = buildExpression(value.values);
1649
+ if (nested === null)
1650
+ return null;
1651
+ expression += "(".concat(nested, ")");
1652
+ }
1653
+ else {
1654
+ return null;
1655
+ }
1656
+ }
1657
+ else if (value.type === 17 /* TokenType.NUMBER_TOKEN */) {
1658
+ expression += value.number.toString();
1659
+ }
1660
+ else if (value.type === 15 /* TokenType.DIMENSION_TOKEN */) {
1661
+ // Convert units to px
1662
+ if (value.unit === 'px') {
1663
+ expression += value.number.toString();
1664
+ }
1665
+ else if (value.unit === 'rem' || value.unit === 'em') {
1666
+ expression += (value.number * 16).toString();
1667
+ }
1668
+ else {
1669
+ expression += value.number.toString();
1670
+ }
1671
+ }
1672
+ else if (value.type === 16 /* TokenType.PERCENTAGE_TOKEN */) {
1673
+ // Convert percentage to absolute value based on context
1674
+ expression += ((value.number / 100) * contextValue).toString();
1675
+ }
1676
+ else if (value.type === 6 /* TokenType.DELIM_TOKEN */) {
1677
+ var op = value.value;
1678
+ if (op === '+' || op === '-' || op === '*' || op === '/') {
1679
+ expression += " ".concat(op, " ");
1680
+ }
1681
+ else if (op === '(') {
1682
+ expression += '(';
1683
+ }
1684
+ else if (op === ')') {
1685
+ expression += ')';
1686
+ }
1687
+ }
1688
+ }
1689
+ return expression;
1690
+ };
1691
+ try {
1692
+ var expression = buildExpression(calcToken.values);
1693
+ if (expression === null || expression.trim() === '') {
1694
+ return null;
1695
+ }
1696
+ // Evaluate the expression
1697
+ // Note: Using Function constructor (similar to color.ts line 185)
1698
+ var result = new Function('return ' + expression)();
1699
+ if (typeof result === 'number' && !isNaN(result)) {
1700
+ // Return as a number token in px
1701
+ return {
1702
+ type: 17 /* TokenType.NUMBER_TOKEN */,
1703
+ number: result,
1704
+ flags: FLAG_INTEGER
1705
+ };
1706
+ }
1707
+ }
1708
+ catch (e) {
1709
+ return null;
1710
+ }
1711
+ return null;
1712
+ };
1613
1713
  var parseLengthPercentageTuple = function (tokens) {
1614
1714
  return tokens.length > 1 ? [tokens[0], tokens[1]] : [tokens[0]];
1615
1715
  };
@@ -3350,7 +3450,20 @@
3350
3450
  prefix: false,
3351
3451
  parse: function (_context, tokens) {
3352
3452
  return parseFunctionArgs(tokens)
3353
- .map(function (values) { return values.filter(isLengthPercentage); })
3453
+ .map(function (values) {
3454
+ // Convert calc() to length-percentage tokens, keep other length-percentage as is
3455
+ return values
3456
+ .map(function (value) {
3457
+ if (isCalcFunction(value)) {
3458
+ // For calc() at parse time, we can't know the container size
3459
+ // So we evaluate with 0 context which will work for px-only calc()
3460
+ // Percentage-based calc() will need special handling
3461
+ return evaluateCalcToLengthPercentage(value, 0);
3462
+ }
3463
+ return isLengthPercentage(value) ? value : null;
3464
+ })
3465
+ .filter(function (v) { return v !== null; });
3466
+ })
3354
3467
  .map(parseLengthPercentageTuple);
3355
3468
  }
3356
3469
  };
@@ -5706,7 +5819,19 @@
5706
5819
  container.styles.isTransformed() ||
5707
5820
  (isBodyElement(node) && root.styles.isTransparent()));
5708
5821
  };
5709
- var createsStackingContext = function (styles) { return styles.isPositioned() || styles.isFloating(); };
5822
+ var createsStackingContext = function (styles) {
5823
+ // Positioned and floating elements create stacking contexts
5824
+ if (styles.isPositioned() || styles.isFloating()) {
5825
+ return true;
5826
+ }
5827
+ // Fix for Issue #137: Inline-level containers (inline-flex, inline-block, etc.)
5828
+ // should create stacking contexts to prevent their children from being added
5829
+ // to the parent's stacking context, which causes rendering order issues
5830
+ return (contains(styles.display, 268435456 /* DISPLAY.INLINE_FLEX */) ||
5831
+ contains(styles.display, 33554432 /* DISPLAY.INLINE_BLOCK */) ||
5832
+ contains(styles.display, 536870912 /* DISPLAY.INLINE_GRID */) ||
5833
+ contains(styles.display, 134217728 /* DISPLAY.INLINE_TABLE */));
5834
+ };
5710
5835
  var isTextNode = function (node) { return node.nodeType === Node.TEXT_NODE; };
5711
5836
  var isElementNode = function (node) { return node.nodeType === Node.ELEMENT_NODE; };
5712
5837
  var isHTMLElementNode = function (node) {
@@ -6542,7 +6667,8 @@
6542
6667
  // Edge does not provide value for cssText
6543
6668
  for (var i = style.length - 1; i >= 0; i--) {
6544
6669
  var property = style.item(i);
6545
- if (ignoredStyleProperties.indexOf(property) === -1) {
6670
+ // fix: Chrome_138 ignore custom properties
6671
+ if (ignoredStyleProperties.indexOf(property) === -1 && !property.startsWith('--')) {
6546
6672
  target.style.setProperty(property, style.getPropertyValue(property));
6547
6673
  }
6548
6674
  }
@@ -7068,8 +7194,9 @@
7068
7194
  this.effects.push(new OpacityEffect(this.container.styles.opacity));
7069
7195
  }
7070
7196
  if (this.container.styles.rotate !== null) {
7071
- var offsetX = this.container.bounds.left + this.container.styles.transformOrigin[0].number;
7072
- var offsetY = this.container.bounds.top + this.container.styles.transformOrigin[1].number;
7197
+ var origin_1 = this.container.styles.transformOrigin;
7198
+ var offsetX = this.container.bounds.left + getAbsoluteValue(origin_1[0], this.container.bounds.width);
7199
+ var offsetY = this.container.bounds.top + getAbsoluteValue(origin_1[1], this.container.bounds.height);
7073
7200
  // Apply rotate property if present
7074
7201
  var angle = this.container.styles.rotate;
7075
7202
  var rad = (angle * Math.PI) / 180;
@@ -7079,8 +7206,9 @@
7079
7206
  this.effects.push(new TransformEffect(offsetX, offsetY, rotateMatrix));
7080
7207
  }
7081
7208
  if (this.container.styles.transform !== null) {
7082
- var offsetX = this.container.bounds.left + this.container.styles.transformOrigin[0].number;
7083
- var offsetY = this.container.bounds.top + this.container.styles.transformOrigin[1].number;
7209
+ var origin_2 = this.container.styles.transformOrigin;
7210
+ var offsetX = this.container.bounds.left + getAbsoluteValue(origin_2[0], this.container.bounds.width);
7211
+ var offsetY = this.container.bounds.top + getAbsoluteValue(origin_2[1], this.container.bounds.height);
7084
7212
  var matrix = this.container.styles.transform;
7085
7213
  this.effects.push(new TransformEffect(offsetX, offsetY, matrix));
7086
7214
  }
@@ -7651,15 +7779,9 @@
7651
7779
  CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing, baseline) {
7652
7780
  var _this = this;
7653
7781
  if (letterSpacing === 0) {
7654
- // Fixed an issue with characters moving up in non-Firefox.
7655
- // https://github.com/niklasvh/html2canvas/issues/2107#issuecomment-692462900
7656
- if (navigator.userAgent.indexOf('Firefox') === -1) {
7657
- this.ctx.textBaseline = 'ideographic';
7658
- this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
7659
- }
7660
- else {
7661
- this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
7662
- }
7782
+ // Use alphabetic baseline for consistent text positioning across browsers
7783
+ // Issue #129: text.bounds.top + text.bounds.height causes text to render too low
7784
+ this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
7663
7785
  }
7664
7786
  else {
7665
7787
  var letters = segmentGraphemes(text.text);
@@ -7742,7 +7864,19 @@
7742
7864
  _this.ctx.lineWidth = styles.webkitTextStrokeWidth;
7743
7865
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
7744
7866
  _this.ctx.lineJoin = !!window.chrome ? 'miter' : 'round';
7745
- _this.ctx.strokeText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
7867
+ // Issue #110: Use baseline (fontSize) for consistent positioning with fill
7868
+ // Previously used text.bounds.height which caused stroke to render too low
7869
+ var baseline_1 = styles.fontSize.number;
7870
+ if (styles.letterSpacing === 0) {
7871
+ _this.ctx.strokeText(text.text, text.bounds.left, text.bounds.top + baseline_1);
7872
+ }
7873
+ else {
7874
+ var letters = segmentGraphemes(text.text);
7875
+ letters.reduce(function (left, letter) {
7876
+ _this.ctx.strokeText(letter, left, text.bounds.top + baseline_1);
7877
+ return left + _this.ctx.measureText(letter).width;
7878
+ }, text.bounds.left);
7879
+ }
7746
7880
  }
7747
7881
  _this.ctx.strokeStyle = '';
7748
7882
  _this.ctx.lineWidth = 0;