html2canvas-pro 2.3.9 → 2.4.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/dist/html2canvas-pro.cjs +39 -25
- package/dist/html2canvas-pro.cjs.map +1 -1
- package/dist/html2canvas-pro.esm.js +39 -25
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +39 -25
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +4 -4
- package/dist/lib/dom/document-cloner.js +10 -8
- package/dist/lib/render/canvas/content-renderer.js +22 -9
- package/dist/lib/render/canvas/font-utils.js +24 -7
- package/dist/types/render/canvas/content-renderer.d.ts +9 -0
- package/dist/types/render/canvas/font-utils.d.ts +10 -2
- package/package.json +1 -1
package/dist/html2canvas-pro.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* html2canvas-pro 2.
|
|
2
|
+
* html2canvas-pro 2.4.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
|
*/
|
|
@@ -6990,19 +6990,18 @@
|
|
|
6990
6990
|
const documentClone = cloneWindow.document;
|
|
6991
6991
|
const iframeLoad = iframeLoader(iframe).then(async () => {
|
|
6992
6992
|
this.scrolledElements.forEach(restoreNodeScroll);
|
|
6993
|
-
if (cloneWindow)
|
|
6994
|
-
cloneWindow.scrollTo(windowSize.left, windowSize.top);
|
|
6995
|
-
if (/AppleWebKit/g.test(navigator.userAgent) && (cloneWindow.scrollY !== windowSize.top || cloneWindow.scrollX !== windowSize.left)) {
|
|
6996
|
-
this.context.logger.warn("Unable to restore scroll position for cloned document");
|
|
6997
|
-
this.context.windowBounds = this.context.windowBounds.add(cloneWindow.scrollX - windowSize.left, cloneWindow.scrollY - windowSize.top, 0, 0);
|
|
6998
|
-
}
|
|
6999
|
-
}
|
|
6993
|
+
if (cloneWindow) cloneWindow.scrollTo(windowSize.left, windowSize.top);
|
|
7000
6994
|
const onclone = this.options.onclone;
|
|
7001
6995
|
const referenceElement = this.clonedReferenceElement;
|
|
7002
6996
|
if (typeof referenceElement === "undefined") throw new Error(`Error finding the ${this.referenceElement.nodeName} in the cloned document`);
|
|
7003
6997
|
if (documentClone.fonts && documentClone.fonts.ready) await documentClone.fonts.ready;
|
|
7004
6998
|
if (/(AppleWebKit)/g.test(navigator.userAgent)) await imagesReady(documentClone);
|
|
7005
|
-
if (typeof onclone === "function")
|
|
6999
|
+
if (typeof onclone === "function") await Promise.resolve().then(() => onclone(documentClone, referenceElement));
|
|
7000
|
+
cloneWindow.scrollTo(windowSize.left, windowSize.top);
|
|
7001
|
+
if (/AppleWebKit/g.test(navigator.userAgent) && (cloneWindow.scrollY !== windowSize.top || cloneWindow.scrollX !== windowSize.left)) {
|
|
7002
|
+
this.context.logger.warn("Unable to restore scroll position for cloned document");
|
|
7003
|
+
this.context.windowBounds = this.context.windowBounds.add(cloneWindow.scrollX - windowSize.left, cloneWindow.scrollY - windowSize.top, 0, 0);
|
|
7004
|
+
}
|
|
7006
7005
|
return iframe;
|
|
7007
7006
|
});
|
|
7008
7007
|
/**
|
|
@@ -9092,8 +9091,8 @@
|
|
|
9092
9091
|
/**
|
|
9093
9092
|
* Font measurement utility
|
|
9094
9093
|
*
|
|
9095
|
-
* Provides Canvas API-based
|
|
9096
|
-
*
|
|
9094
|
+
* Provides Canvas API-based font measurement that works correctly with webfonts
|
|
9095
|
+
* (which may not be loaded in the original document) and system fonts.
|
|
9097
9096
|
*
|
|
9098
9097
|
* Fallback chain:
|
|
9099
9098
|
* 1. fontBoundingBoxAscent — font-level metric (Chrome 99+, FF 116+, Safari 17.4+)
|
|
@@ -9101,6 +9100,18 @@
|
|
|
9101
9100
|
* 3. fallback value — coarse CSS fallback
|
|
9102
9101
|
*/
|
|
9103
9102
|
const SAMPLE_TEXT = "Mg";
|
|
9103
|
+
const isValidMetric = (value) => value !== void 0 && !Number.isNaN(value);
|
|
9104
|
+
/**
|
|
9105
|
+
* Measure the baseline ascent and full font box for the currently set font.
|
|
9106
|
+
*/
|
|
9107
|
+
const measureFontMetrics = (ctx, fallback) => {
|
|
9108
|
+
const tm = ctx.measureText(SAMPLE_TEXT);
|
|
9109
|
+
const fontMetrics = tm;
|
|
9110
|
+
return {
|
|
9111
|
+
baseline: isValidMetric(fontMetrics.fontBoundingBoxAscent) ? fontMetrics.fontBoundingBoxAscent : isValidMetric(tm.actualBoundingBoxAscent) ? tm.actualBoundingBoxAscent : fallback,
|
|
9112
|
+
height: isValidMetric(fontMetrics.fontBoundingBoxAscent) && isValidMetric(fontMetrics.fontBoundingBoxDescent) ? fontMetrics.fontBoundingBoxAscent + fontMetrics.fontBoundingBoxDescent : isValidMetric(tm.actualBoundingBoxAscent) && isValidMetric(tm.actualBoundingBoxDescent) ? tm.actualBoundingBoxAscent + tm.actualBoundingBoxDescent : fallback
|
|
9113
|
+
};
|
|
9114
|
+
};
|
|
9104
9115
|
/**
|
|
9105
9116
|
* Measure the baseline ascent for the currently set font.
|
|
9106
9117
|
*
|
|
@@ -9109,10 +9120,7 @@
|
|
|
9109
9120
|
* @returns The distance from the text baseline to the top of the bounding box
|
|
9110
9121
|
*/
|
|
9111
9122
|
const measureBaseline = (ctx, fallback) => {
|
|
9112
|
-
|
|
9113
|
-
const fmAscent = tm.fontBoundingBoxAscent;
|
|
9114
|
-
const ascent = fmAscent !== void 0 && !Number.isNaN(fmAscent) ? fmAscent : tm.actualBoundingBoxAscent;
|
|
9115
|
-
return ascent !== void 0 && !Number.isNaN(ascent) ? ascent : fallback;
|
|
9123
|
+
return measureFontMetrics(ctx, fallback).baseline;
|
|
9116
9124
|
};
|
|
9117
9125
|
//#endregion
|
|
9118
9126
|
//#region src/render/canvas/text-renderer.ts
|
|
@@ -10359,11 +10367,13 @@
|
|
|
10359
10367
|
if (isTextInputElement(container) && container.value.length) {
|
|
10360
10368
|
const [font] = textRenderer.createFontStyle(styles);
|
|
10361
10369
|
ctx.font = font;
|
|
10362
|
-
const
|
|
10370
|
+
const fontSizeValue = getAbsoluteValue(styles.fontSize, 0);
|
|
10371
|
+
const { baseline, height: fontHeight } = measureFontMetrics(ctx, fontSizeValue);
|
|
10363
10372
|
ctx.fillStyle = container instanceof InputElementContainer && container.isPlaceholder ? asString(PLACEHOLDER_COLOR) : asString(styles.color);
|
|
10364
10373
|
ctx.textBaseline = "alphabetic";
|
|
10365
10374
|
ctx.textAlign = canvasTextAlign(container.styles.textAlign);
|
|
10366
10375
|
const bounds = contentBox(container);
|
|
10376
|
+
const clipBounds = container instanceof InputElementContainer ? inputTextClipBounds(bounds, paddingBox(container), fontHeight) : bounds;
|
|
10367
10377
|
let x = 0;
|
|
10368
10378
|
switch (container.styles.textAlign) {
|
|
10369
10379
|
case 1:
|
|
@@ -10374,21 +10384,17 @@
|
|
|
10374
10384
|
break;
|
|
10375
10385
|
}
|
|
10376
10386
|
let verticalOffset = 0;
|
|
10377
|
-
if (container instanceof InputElementContainer)
|
|
10378
|
-
const fontSizeValue = getAbsoluteValue(styles.fontSize, 0);
|
|
10379
|
-
verticalOffset = (bounds.height - fontSizeValue) / 2;
|
|
10380
|
-
}
|
|
10387
|
+
if (container instanceof InputElementContainer) verticalOffset = (bounds.height - fontHeight) / 2;
|
|
10381
10388
|
const textBounds = bounds.add(x, verticalOffset, 0, 0);
|
|
10382
10389
|
ctx.save();
|
|
10383
10390
|
pathFn([
|
|
10384
|
-
new Vector(
|
|
10385
|
-
new Vector(
|
|
10386
|
-
new Vector(
|
|
10387
|
-
new Vector(
|
|
10391
|
+
new Vector(clipBounds.left, clipBounds.top),
|
|
10392
|
+
new Vector(clipBounds.left + clipBounds.width, clipBounds.top),
|
|
10393
|
+
new Vector(clipBounds.left + clipBounds.width, clipBounds.top + clipBounds.height),
|
|
10394
|
+
new Vector(clipBounds.left, clipBounds.top + clipBounds.height)
|
|
10388
10395
|
]);
|
|
10389
10396
|
ctx.clip();
|
|
10390
10397
|
const lines = wrapTextToWidth(ctx, container.value, bounds.width, styles.whiteSpace === "pre" || styles.whiteSpace === "nowrap");
|
|
10391
|
-
const fontSizeValue = getAbsoluteValue(styles.fontSize, 0);
|
|
10392
10398
|
const lineHeight = computeLineHeight(styles.lineHeight, fontSizeValue);
|
|
10393
10399
|
lines.forEach((line, index) => {
|
|
10394
10400
|
let lineX = textBounds.left;
|
|
@@ -10410,6 +10416,14 @@
|
|
|
10410
10416
|
}
|
|
10411
10417
|
}
|
|
10412
10418
|
/**
|
|
10419
|
+
* Native single-line inputs may paint value text into their padding area when
|
|
10420
|
+
* fixed height and vertical padding leave a content box shorter than the font.
|
|
10421
|
+
* Preserve the content box horizontally, but avoid clipping those glyphs to the
|
|
10422
|
+
* collapsed vertical content box.
|
|
10423
|
+
* @internal exported for testing
|
|
10424
|
+
*/
|
|
10425
|
+
const inputTextClipBounds = (contentBounds, inputPaddingBox, fontHeight) => contentBounds.height < fontHeight ? new Bounds(contentBounds.left, inputPaddingBox.top, contentBounds.width, inputPaddingBox.height) : contentBounds;
|
|
10426
|
+
/**
|
|
10413
10427
|
* Split text into lines that fit the given pixel width, preserving hard line
|
|
10414
10428
|
* breaks and (when wrapping is enabled) breaking at word boundaries — mirroring
|
|
10415
10429
|
* how a <textarea> lays out its value.
|