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.
- package/README.md +20 -0
- package/dist/html2canvas-pro.esm.js +153 -19
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +153 -19
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +2 -2
- package/dist/lib/css/layout/bounds.js +13 -1
- package/dist/lib/css/layout/bounds.js.map +1 -1
- package/dist/lib/css/property-descriptors/background-position.js +14 -1
- package/dist/lib/css/property-descriptors/background-position.js.map +1 -1
- package/dist/lib/css/types/length-percentage.js +91 -1
- package/dist/lib/css/types/length-percentage.js.map +1 -1
- package/dist/lib/dom/document-cloner.js +2 -1
- package/dist/lib/dom/document-cloner.js.map +1 -1
- package/dist/lib/dom/node-parser.js +14 -1
- package/dist/lib/dom/node-parser.js.map +1 -1
- package/dist/lib/render/canvas/canvas-renderer.js +16 -10
- package/dist/lib/render/canvas/canvas-renderer.js.map +1 -1
- package/dist/lib/render/stacking-context.js +7 -4
- package/dist/lib/render/stacking-context.js.map +1 -1
- package/dist/types/css/types/length-percentage.d.ts +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,12 +47,32 @@ import html2canvas from 'html2canvas-pro';
|
|
|
47
47
|
|
|
48
48
|
To render an `element` with html2canvas-pro with some (optional) [options](/docs/configuration.md), simply call `html2canvas(element, options);`
|
|
49
49
|
|
|
50
|
+
### Basic Example
|
|
51
|
+
|
|
50
52
|
```javascript
|
|
51
53
|
html2canvas(document.body).then(function(canvas) {
|
|
52
54
|
document.body.appendChild(canvas);
|
|
53
55
|
});
|
|
54
56
|
```
|
|
55
57
|
|
|
58
|
+
### Controlling Output Dimensions
|
|
59
|
+
|
|
60
|
+
⚠️ **Important**: By default, the output canvas dimensions are affected by `devicePixelRatio`.
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
// If you need exact pixel dimensions (e.g., for a specific file size):
|
|
64
|
+
html2canvas(element, {
|
|
65
|
+
width: 1920,
|
|
66
|
+
height: 1080,
|
|
67
|
+
scale: 1 // Set scale to 1 for exact dimensions
|
|
68
|
+
}).then(canvas => {
|
|
69
|
+
// Canvas will be exactly 1920×1080 pixels
|
|
70
|
+
const dataURL = canvas.toDataURL('image/png');
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
See the [Configuration Guide](/docs/configuration.md#canvas-dimensions) for more details.
|
|
75
|
+
|
|
56
76
|
## Contribution
|
|
57
77
|
|
|
58
78
|
If you want to add some features, feel free to submit PR.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* html2canvas-pro 1.
|
|
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
|
*/
|
|
@@ -112,7 +112,19 @@ var Bounds = /** @class */ (function () {
|
|
|
112
112
|
return new Bounds(clientRect.left + context.windowBounds.left, clientRect.top + context.windowBounds.top, clientRect.width, clientRect.height);
|
|
113
113
|
};
|
|
114
114
|
Bounds.fromDOMRectList = function (context, domRectList) {
|
|
115
|
-
var
|
|
115
|
+
var rects = Array.from(domRectList);
|
|
116
|
+
// First try to find a rect with non-zero width
|
|
117
|
+
var domRect = rects.find(function (rect) { return rect.width !== 0; });
|
|
118
|
+
// If not found, try to find a rect with non-zero height
|
|
119
|
+
// This handles cases like inline-flex with single child where width might be 0
|
|
120
|
+
if (!domRect) {
|
|
121
|
+
domRect = rects.find(function (rect) { return rect.height !== 0; });
|
|
122
|
+
}
|
|
123
|
+
// If still not found but rects exist, use the first rect
|
|
124
|
+
// Position info (left, top) might still be valid even if dimensions are 0
|
|
125
|
+
if (!domRect && rects.length > 0) {
|
|
126
|
+
domRect = rects[0];
|
|
127
|
+
}
|
|
116
128
|
return domRect
|
|
117
129
|
? new Bounds(domRect.left + context.windowBounds.left, domRect.top + context.windowBounds.top, domRect.width, domRect.height)
|
|
118
130
|
: Bounds.EMPTY;
|
|
@@ -1604,6 +1616,94 @@ var isLength = function (token) {
|
|
|
1604
1616
|
var isLengthPercentage = function (token) {
|
|
1605
1617
|
return token.type === 16 /* TokenType.PERCENTAGE_TOKEN */ || isLength(token);
|
|
1606
1618
|
};
|
|
1619
|
+
/**
|
|
1620
|
+
* Check if a token is a calc() function
|
|
1621
|
+
*/
|
|
1622
|
+
var isCalcFunction = function (token) {
|
|
1623
|
+
return token.type === 18 /* TokenType.FUNCTION */ && token.name === 'calc';
|
|
1624
|
+
};
|
|
1625
|
+
/**
|
|
1626
|
+
* Evaluate a calc() expression and convert to LengthPercentage token
|
|
1627
|
+
* Supports basic arithmetic: +, -, *, /
|
|
1628
|
+
* Note: Percentages in calc() are converted based on a context value
|
|
1629
|
+
*/
|
|
1630
|
+
var evaluateCalcToLengthPercentage = function (calcToken, contextValue) {
|
|
1631
|
+
if (contextValue === void 0) { contextValue = 0; }
|
|
1632
|
+
// Build expression string from tokens
|
|
1633
|
+
var buildExpression = function (values) {
|
|
1634
|
+
var expression = '';
|
|
1635
|
+
for (var _i = 0, values_1 = values; _i < values_1.length; _i++) {
|
|
1636
|
+
var value = values_1[_i];
|
|
1637
|
+
if (value.type === 31 /* TokenType.WHITESPACE_TOKEN */) {
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
if (value.type === 18 /* TokenType.FUNCTION */) {
|
|
1641
|
+
if (value.name === 'calc') {
|
|
1642
|
+
var nested = buildExpression(value.values);
|
|
1643
|
+
if (nested === null)
|
|
1644
|
+
return null;
|
|
1645
|
+
expression += "(".concat(nested, ")");
|
|
1646
|
+
}
|
|
1647
|
+
else {
|
|
1648
|
+
return null;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
else if (value.type === 17 /* TokenType.NUMBER_TOKEN */) {
|
|
1652
|
+
expression += value.number.toString();
|
|
1653
|
+
}
|
|
1654
|
+
else if (value.type === 15 /* TokenType.DIMENSION_TOKEN */) {
|
|
1655
|
+
// Convert units to px
|
|
1656
|
+
if (value.unit === 'px') {
|
|
1657
|
+
expression += value.number.toString();
|
|
1658
|
+
}
|
|
1659
|
+
else if (value.unit === 'rem' || value.unit === 'em') {
|
|
1660
|
+
expression += (value.number * 16).toString();
|
|
1661
|
+
}
|
|
1662
|
+
else {
|
|
1663
|
+
expression += value.number.toString();
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
else if (value.type === 16 /* TokenType.PERCENTAGE_TOKEN */) {
|
|
1667
|
+
// Convert percentage to absolute value based on context
|
|
1668
|
+
expression += ((value.number / 100) * contextValue).toString();
|
|
1669
|
+
}
|
|
1670
|
+
else if (value.type === 6 /* TokenType.DELIM_TOKEN */) {
|
|
1671
|
+
var op = value.value;
|
|
1672
|
+
if (op === '+' || op === '-' || op === '*' || op === '/') {
|
|
1673
|
+
expression += " ".concat(op, " ");
|
|
1674
|
+
}
|
|
1675
|
+
else if (op === '(') {
|
|
1676
|
+
expression += '(';
|
|
1677
|
+
}
|
|
1678
|
+
else if (op === ')') {
|
|
1679
|
+
expression += ')';
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
return expression;
|
|
1684
|
+
};
|
|
1685
|
+
try {
|
|
1686
|
+
var expression = buildExpression(calcToken.values);
|
|
1687
|
+
if (expression === null || expression.trim() === '') {
|
|
1688
|
+
return null;
|
|
1689
|
+
}
|
|
1690
|
+
// Evaluate the expression
|
|
1691
|
+
// Note: Using Function constructor (similar to color.ts line 185)
|
|
1692
|
+
var result = new Function('return ' + expression)();
|
|
1693
|
+
if (typeof result === 'number' && !isNaN(result)) {
|
|
1694
|
+
// Return as a number token in px
|
|
1695
|
+
return {
|
|
1696
|
+
type: 17 /* TokenType.NUMBER_TOKEN */,
|
|
1697
|
+
number: result,
|
|
1698
|
+
flags: FLAG_INTEGER
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
catch (e) {
|
|
1703
|
+
return null;
|
|
1704
|
+
}
|
|
1705
|
+
return null;
|
|
1706
|
+
};
|
|
1607
1707
|
var parseLengthPercentageTuple = function (tokens) {
|
|
1608
1708
|
return tokens.length > 1 ? [tokens[0], tokens[1]] : [tokens[0]];
|
|
1609
1709
|
};
|
|
@@ -3344,7 +3444,20 @@ var backgroundPosition = {
|
|
|
3344
3444
|
prefix: false,
|
|
3345
3445
|
parse: function (_context, tokens) {
|
|
3346
3446
|
return parseFunctionArgs(tokens)
|
|
3347
|
-
.map(function (values) {
|
|
3447
|
+
.map(function (values) {
|
|
3448
|
+
// Convert calc() to length-percentage tokens, keep other length-percentage as is
|
|
3449
|
+
return values
|
|
3450
|
+
.map(function (value) {
|
|
3451
|
+
if (isCalcFunction(value)) {
|
|
3452
|
+
// For calc() at parse time, we can't know the container size
|
|
3453
|
+
// So we evaluate with 0 context which will work for px-only calc()
|
|
3454
|
+
// Percentage-based calc() will need special handling
|
|
3455
|
+
return evaluateCalcToLengthPercentage(value, 0);
|
|
3456
|
+
}
|
|
3457
|
+
return isLengthPercentage(value) ? value : null;
|
|
3458
|
+
})
|
|
3459
|
+
.filter(function (v) { return v !== null; });
|
|
3460
|
+
})
|
|
3348
3461
|
.map(parseLengthPercentageTuple);
|
|
3349
3462
|
}
|
|
3350
3463
|
};
|
|
@@ -5700,7 +5813,19 @@ var createsRealStackingContext = function (node, container, root) {
|
|
|
5700
5813
|
container.styles.isTransformed() ||
|
|
5701
5814
|
(isBodyElement(node) && root.styles.isTransparent()));
|
|
5702
5815
|
};
|
|
5703
|
-
var createsStackingContext = function (styles) {
|
|
5816
|
+
var createsStackingContext = function (styles) {
|
|
5817
|
+
// Positioned and floating elements create stacking contexts
|
|
5818
|
+
if (styles.isPositioned() || styles.isFloating()) {
|
|
5819
|
+
return true;
|
|
5820
|
+
}
|
|
5821
|
+
// Fix for Issue #137: Inline-level containers (inline-flex, inline-block, etc.)
|
|
5822
|
+
// should create stacking contexts to prevent their children from being added
|
|
5823
|
+
// to the parent's stacking context, which causes rendering order issues
|
|
5824
|
+
return (contains(styles.display, 268435456 /* DISPLAY.INLINE_FLEX */) ||
|
|
5825
|
+
contains(styles.display, 33554432 /* DISPLAY.INLINE_BLOCK */) ||
|
|
5826
|
+
contains(styles.display, 536870912 /* DISPLAY.INLINE_GRID */) ||
|
|
5827
|
+
contains(styles.display, 134217728 /* DISPLAY.INLINE_TABLE */));
|
|
5828
|
+
};
|
|
5704
5829
|
var isTextNode = function (node) { return node.nodeType === Node.TEXT_NODE; };
|
|
5705
5830
|
var isElementNode = function (node) { return node.nodeType === Node.ELEMENT_NODE; };
|
|
5706
5831
|
var isHTMLElementNode = function (node) {
|
|
@@ -6536,7 +6661,8 @@ var copyCSSStyles = function (style, target) {
|
|
|
6536
6661
|
// Edge does not provide value for cssText
|
|
6537
6662
|
for (var i = style.length - 1; i >= 0; i--) {
|
|
6538
6663
|
var property = style.item(i);
|
|
6539
|
-
|
|
6664
|
+
// fix: Chrome_138 ignore custom properties
|
|
6665
|
+
if (ignoredStyleProperties.indexOf(property) === -1 && !property.startsWith('--')) {
|
|
6540
6666
|
target.style.setProperty(property, style.getPropertyValue(property));
|
|
6541
6667
|
}
|
|
6542
6668
|
}
|
|
@@ -7062,8 +7188,9 @@ var ElementPaint = /** @class */ (function () {
|
|
|
7062
7188
|
this.effects.push(new OpacityEffect(this.container.styles.opacity));
|
|
7063
7189
|
}
|
|
7064
7190
|
if (this.container.styles.rotate !== null) {
|
|
7065
|
-
var
|
|
7066
|
-
var
|
|
7191
|
+
var origin_1 = this.container.styles.transformOrigin;
|
|
7192
|
+
var offsetX = this.container.bounds.left + getAbsoluteValue(origin_1[0], this.container.bounds.width);
|
|
7193
|
+
var offsetY = this.container.bounds.top + getAbsoluteValue(origin_1[1], this.container.bounds.height);
|
|
7067
7194
|
// Apply rotate property if present
|
|
7068
7195
|
var angle = this.container.styles.rotate;
|
|
7069
7196
|
var rad = (angle * Math.PI) / 180;
|
|
@@ -7073,8 +7200,9 @@ var ElementPaint = /** @class */ (function () {
|
|
|
7073
7200
|
this.effects.push(new TransformEffect(offsetX, offsetY, rotateMatrix));
|
|
7074
7201
|
}
|
|
7075
7202
|
if (this.container.styles.transform !== null) {
|
|
7076
|
-
var
|
|
7077
|
-
var
|
|
7203
|
+
var origin_2 = this.container.styles.transformOrigin;
|
|
7204
|
+
var offsetX = this.container.bounds.left + getAbsoluteValue(origin_2[0], this.container.bounds.width);
|
|
7205
|
+
var offsetY = this.container.bounds.top + getAbsoluteValue(origin_2[1], this.container.bounds.height);
|
|
7078
7206
|
var matrix = this.container.styles.transform;
|
|
7079
7207
|
this.effects.push(new TransformEffect(offsetX, offsetY, matrix));
|
|
7080
7208
|
}
|
|
@@ -7645,15 +7773,9 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
7645
7773
|
CanvasRenderer.prototype.renderTextWithLetterSpacing = function (text, letterSpacing, baseline) {
|
|
7646
7774
|
var _this = this;
|
|
7647
7775
|
if (letterSpacing === 0) {
|
|
7648
|
-
//
|
|
7649
|
-
//
|
|
7650
|
-
|
|
7651
|
-
this.ctx.textBaseline = 'ideographic';
|
|
7652
|
-
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + text.bounds.height);
|
|
7653
|
-
}
|
|
7654
|
-
else {
|
|
7655
|
-
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
|
|
7656
|
-
}
|
|
7776
|
+
// Use alphabetic baseline for consistent text positioning across browsers
|
|
7777
|
+
// Issue #129: text.bounds.top + text.bounds.height causes text to render too low
|
|
7778
|
+
this.ctx.fillText(text.text, text.bounds.left, text.bounds.top + baseline);
|
|
7657
7779
|
}
|
|
7658
7780
|
else {
|
|
7659
7781
|
var letters = segmentGraphemes(text.text);
|
|
@@ -7736,7 +7858,19 @@ var CanvasRenderer = /** @class */ (function (_super) {
|
|
|
7736
7858
|
_this.ctx.lineWidth = styles.webkitTextStrokeWidth;
|
|
7737
7859
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
7738
7860
|
_this.ctx.lineJoin = !!window.chrome ? 'miter' : 'round';
|
|
7739
|
-
|
|
7861
|
+
// Issue #110: Use baseline (fontSize) for consistent positioning with fill
|
|
7862
|
+
// Previously used text.bounds.height which caused stroke to render too low
|
|
7863
|
+
var baseline_1 = styles.fontSize.number;
|
|
7864
|
+
if (styles.letterSpacing === 0) {
|
|
7865
|
+
_this.ctx.strokeText(text.text, text.bounds.left, text.bounds.top + baseline_1);
|
|
7866
|
+
}
|
|
7867
|
+
else {
|
|
7868
|
+
var letters = segmentGraphemes(text.text);
|
|
7869
|
+
letters.reduce(function (left, letter) {
|
|
7870
|
+
_this.ctx.strokeText(letter, left, text.bounds.top + baseline_1);
|
|
7871
|
+
return left + _this.ctx.measureText(letter).width;
|
|
7872
|
+
}, text.bounds.left);
|
|
7873
|
+
}
|
|
7740
7874
|
}
|
|
7741
7875
|
_this.ctx.strokeStyle = '';
|
|
7742
7876
|
_this.ctx.lineWidth = 0;
|