html2canvas-pro 2.3.2 → 2.3.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.
- package/README.md +6 -2
- package/dist/html2canvas-pro.cjs +104 -14
- package/dist/html2canvas-pro.cjs.map +1 -1
- package/dist/html2canvas-pro.esm.js +104 -14
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +104 -14
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +3 -3
- package/dist/lib/css/index.js +8 -1
- package/dist/lib/css/property-descriptors/background-clip.js +2 -0
- package/dist/lib/css/syntax/parser.js +5 -4
- package/dist/lib/render/canvas/canvas-renderer.js +123 -9
- package/dist/types/css/property-descriptors/background-clip.d.ts +2 -1
- package/dist/types/render/canvas/canvas-renderer.d.ts +18 -0
- package/package.json +1 -1
package/dist/html2canvas-pro.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* html2canvas-pro 2.3.
|
|
2
|
+
* html2canvas-pro 2.3.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
|
*/
|
|
@@ -1734,9 +1734,10 @@
|
|
|
1734
1734
|
let arg = [];
|
|
1735
1735
|
tokens.forEach((token) => {
|
|
1736
1736
|
if (token.type === 4) {
|
|
1737
|
-
if (arg.length
|
|
1738
|
-
|
|
1739
|
-
|
|
1737
|
+
if (arg.length) {
|
|
1738
|
+
args.push(arg);
|
|
1739
|
+
arg = [];
|
|
1740
|
+
}
|
|
1740
1741
|
return;
|
|
1741
1742
|
}
|
|
1742
1743
|
if (token.type !== 31) arg.push(token);
|
|
@@ -3962,6 +3963,7 @@
|
|
|
3962
3963
|
if (isIdentToken(token)) switch (token.value) {
|
|
3963
3964
|
case "padding-box": return 1;
|
|
3964
3965
|
case "content-box": return 2;
|
|
3966
|
+
case "text": return 3;
|
|
3965
3967
|
}
|
|
3966
3968
|
return 0;
|
|
3967
3969
|
});
|
|
@@ -6734,7 +6736,8 @@
|
|
|
6734
6736
|
};
|
|
6735
6737
|
const parseCache = /* @__PURE__ */ new Map();
|
|
6736
6738
|
const parse = (context, descriptor, style) => {
|
|
6737
|
-
|
|
6739
|
+
let rawValue = style !== null && typeof style !== "undefined" ? style.toString() : descriptor.initialValue;
|
|
6740
|
+
if (rawValue.trim() === "") rawValue = descriptor.initialValue;
|
|
6738
6741
|
let valueCache = parseCache.get(descriptor);
|
|
6739
6742
|
if (valueCache) {
|
|
6740
6743
|
const cached = valueCache.get(rawValue);
|
|
@@ -10528,6 +10531,7 @@
|
|
|
10528
10531
|
this.effectsRenderer.applyEffects(paint.getEffects(2));
|
|
10529
10532
|
const styles = paint.container.styles;
|
|
10530
10533
|
const hasBackground = !isTransparent(styles.backgroundColor) || styles.backgroundImage.length;
|
|
10534
|
+
const hasTextClip = hasTextBackgroundClip(styles);
|
|
10531
10535
|
const borders = [
|
|
10532
10536
|
{
|
|
10533
10537
|
style: styles.borderTopStyle,
|
|
@@ -10550,17 +10554,21 @@
|
|
|
10550
10554
|
width: styles.borderLeftWidth
|
|
10551
10555
|
}
|
|
10552
10556
|
];
|
|
10553
|
-
const
|
|
10557
|
+
const backgroundClipValue = getBackgroundValueForIndex(styles.backgroundClip, 0);
|
|
10558
|
+
const backgroundPaintingArea = calculateBackgroundCurvedPaintingArea(backgroundClipValue, paint.curves);
|
|
10554
10559
|
if (hasBackground || styles.boxShadow.length) {
|
|
10555
|
-
this.
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
this.ctx.
|
|
10560
|
-
|
|
10560
|
+
if (hasTextClip && paint.container.textNodes.length > 0) await this.renderTextClippedBackground(paint);
|
|
10561
|
+
else {
|
|
10562
|
+
this.ctx.save();
|
|
10563
|
+
this.path(backgroundPaintingArea);
|
|
10564
|
+
this.ctx.clip();
|
|
10565
|
+
if (!isTransparent(styles.backgroundColor)) {
|
|
10566
|
+
this.ctx.fillStyle = asString(styles.backgroundColor);
|
|
10567
|
+
this.ctx.fill();
|
|
10568
|
+
}
|
|
10569
|
+
await this.backgroundRenderer.renderBackgroundImage(paint.container);
|
|
10570
|
+
this.ctx.restore();
|
|
10561
10571
|
}
|
|
10562
|
-
await this.backgroundRenderer.renderBackgroundImage(paint.container);
|
|
10563
|
-
this.ctx.restore();
|
|
10564
10572
|
styles.boxShadow.slice(0).reverse().forEach((shadow) => {
|
|
10565
10573
|
this.ctx.save();
|
|
10566
10574
|
const borderBoxArea = calculateBorderBoxPath(paint.curves);
|
|
@@ -10619,6 +10627,82 @@
|
|
|
10619
10627
|
this.effectsRenderer.applyEffects([]);
|
|
10620
10628
|
return this.canvas;
|
|
10621
10629
|
}
|
|
10630
|
+
/**
|
|
10631
|
+
* Render background clipped to text shape using offscreen canvas with destination-in compositing.
|
|
10632
|
+
* This implements background-clip: text support.
|
|
10633
|
+
*
|
|
10634
|
+
* Algorithm:
|
|
10635
|
+
* 1. Draw background (color + images) onto an offscreen canvas
|
|
10636
|
+
* 2. Use destination-in compositing to clip the background to the text glyph shapes
|
|
10637
|
+
* 3. Composite the result back onto the main canvas
|
|
10638
|
+
*
|
|
10639
|
+
* The offscreen canvas uses CSS-pixel dimensions (not device-pixel) because the
|
|
10640
|
+
* background renderer handles scaling internally via BackgroundRenderer options.
|
|
10641
|
+
*/
|
|
10642
|
+
async renderTextClippedBackground(paint) {
|
|
10643
|
+
const container = paint.container;
|
|
10644
|
+
const styles = container.styles;
|
|
10645
|
+
const bounds = container.bounds;
|
|
10646
|
+
if (bounds.width <= 0 || bounds.height <= 0) return;
|
|
10647
|
+
const offscreen = (this.canvas.ownerDocument ?? document).createElement("canvas");
|
|
10648
|
+
const width = Math.ceil(bounds.width);
|
|
10649
|
+
const height = Math.ceil(bounds.height);
|
|
10650
|
+
offscreen.width = width;
|
|
10651
|
+
offscreen.height = height;
|
|
10652
|
+
const offCtx = offscreen.getContext("2d");
|
|
10653
|
+
if (!offCtx) return;
|
|
10654
|
+
const [fontString] = this.textRenderer.createFontStyle(styles);
|
|
10655
|
+
offCtx.font = fontString;
|
|
10656
|
+
offCtx.textBaseline = "alphabetic";
|
|
10657
|
+
offCtx.textAlign = "left";
|
|
10658
|
+
offCtx.direction = styles.direction === 1 ? "rtl" : "ltr";
|
|
10659
|
+
const baseline = measureBaseline(offCtx, styles.fontSize.number);
|
|
10660
|
+
const bgRenderer = new BackgroundRenderer({
|
|
10661
|
+
ctx: offCtx,
|
|
10662
|
+
context: this.context,
|
|
10663
|
+
canvas: offscreen,
|
|
10664
|
+
options: {
|
|
10665
|
+
width,
|
|
10666
|
+
height,
|
|
10667
|
+
scale: 1
|
|
10668
|
+
}
|
|
10669
|
+
});
|
|
10670
|
+
if (!isTransparent(styles.backgroundColor)) {
|
|
10671
|
+
offCtx.fillStyle = asString(styles.backgroundColor);
|
|
10672
|
+
offCtx.fillRect(0, 0, width, height);
|
|
10673
|
+
}
|
|
10674
|
+
offCtx.save();
|
|
10675
|
+
offCtx.translate(-bounds.left, -bounds.top);
|
|
10676
|
+
await bgRenderer.renderBackgroundImage(container);
|
|
10677
|
+
offCtx.restore();
|
|
10678
|
+
offCtx.globalCompositeOperation = "destination-in";
|
|
10679
|
+
offCtx.fillStyle = "#000";
|
|
10680
|
+
const writingMode = styles.writingMode;
|
|
10681
|
+
const letterSpacing = styles.letterSpacing;
|
|
10682
|
+
for (const textNode of container.textNodes) for (const textBound of textNode.textBounds) {
|
|
10683
|
+
const localLeft = textBound.bounds.left - bounds.left;
|
|
10684
|
+
const localTop = textBound.bounds.top - bounds.top + baseline;
|
|
10685
|
+
if (letterSpacing > 0) this.renderTextMaskWithLetterSpacing(offCtx, textBound, letterSpacing, localLeft, localTop, writingMode);
|
|
10686
|
+
else offCtx.fillText(textBound.text, localLeft, localTop);
|
|
10687
|
+
}
|
|
10688
|
+
this.ctx.drawImage(offscreen, bounds.left, bounds.top);
|
|
10689
|
+
}
|
|
10690
|
+
/**
|
|
10691
|
+
* Render text glyphs one-by-one as a mask, applying letter-spacing between characters.
|
|
10692
|
+
* This ensures the mask matches how the text renderer lays out the characters.
|
|
10693
|
+
*/
|
|
10694
|
+
renderTextMaskWithLetterSpacing(ctx, text, letterSpacing, x, y, writingMode) {
|
|
10695
|
+
const letters = segmentGraphemes(text.text);
|
|
10696
|
+
let offset = x;
|
|
10697
|
+
for (const letter of letters) {
|
|
10698
|
+
if (isVerticalWritingMode(writingMode)) {
|
|
10699
|
+
ctx.fillText(text.text, x, y);
|
|
10700
|
+
return;
|
|
10701
|
+
}
|
|
10702
|
+
ctx.fillText(letter, offset, y);
|
|
10703
|
+
offset += ctx.measureText(letter).width + letterSpacing;
|
|
10704
|
+
}
|
|
10705
|
+
}
|
|
10622
10706
|
};
|
|
10623
10707
|
const calculateBackgroundCurvedPaintingArea = (clip, curves) => {
|
|
10624
10708
|
switch (clip) {
|
|
@@ -10627,6 +10711,12 @@
|
|
|
10627
10711
|
default: return calculatePaddingBoxPath(curves);
|
|
10628
10712
|
}
|
|
10629
10713
|
};
|
|
10714
|
+
/**
|
|
10715
|
+
* Check if any background layer uses background-clip: text
|
|
10716
|
+
*/
|
|
10717
|
+
const hasTextBackgroundClip = (styles) => {
|
|
10718
|
+
return styles.backgroundClip.some((clip) => clip === 3);
|
|
10719
|
+
};
|
|
10630
10720
|
//#endregion
|
|
10631
10721
|
//#region src/render/canvas/foreignobject-renderer.ts
|
|
10632
10722
|
var ForeignObjectRenderer = class {
|