html2canvas-pro 2.3.6 → 2.3.8
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 +76 -4
- package/dist/html2canvas-pro.cjs.map +1 -1
- package/dist/html2canvas-pro.esm.js +76 -4
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +76 -4
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +5 -4
- package/dist/lib/core/cache-storage.js +16 -1
- package/dist/lib/css/index.js +2 -0
- package/dist/lib/css/property-descriptors/white-space.js +32 -0
- package/dist/lib/dom/element-container.js +10 -1
- package/dist/lib/render/canvas/content-renderer.js +54 -1
- package/dist/types/css/index.d.ts +2 -0
- package/dist/types/css/property-descriptors/white-space.d.ts +9 -0
- package/dist/types/render/canvas/content-renderer.d.ts +7 -0
- package/package.json +1 -1
|
@@ -176,7 +176,22 @@ class Cache {
|
|
|
176
176
|
const isSameOrigin = typeof this._options.customIsSameOrigin === 'function'
|
|
177
177
|
? await this._options.customIsSameOrigin(key, defaultIsSameOrigin)
|
|
178
178
|
: defaultIsSameOrigin(key);
|
|
179
|
-
|
|
179
|
+
// With allowTaint disabled (the default), a cross-origin image is safe to
|
|
180
|
+
// draw only when the server sends CORS headers. Instead of skipping such
|
|
181
|
+
// images outright, still ATTEMPT a CORS load: it succeeds for CORS-enabled
|
|
182
|
+
// hosts (CDNs, avatar services, picsum, ...) and fails cleanly for hosts
|
|
183
|
+
// without CORS headers, where the image is skipped exactly as before
|
|
184
|
+
// (issue #229). An explicit proxy takes precedence so existing setups
|
|
185
|
+
// that rely on proxying keep working.
|
|
186
|
+
const canAttemptCors = !isSameOrigin &&
|
|
187
|
+
this._options.allowTaint === false &&
|
|
188
|
+
!isInlineImage(key) &&
|
|
189
|
+
!isBlobImage(key) &&
|
|
190
|
+
typeof this._options.proxy !== 'string';
|
|
191
|
+
const useCORS = !isInlineImage(key) &&
|
|
192
|
+
(this._options.useCORS === true || canAttemptCors) &&
|
|
193
|
+
features_1.FEATURES.SUPPORT_CORS_IMAGES &&
|
|
194
|
+
!isSameOrigin;
|
|
180
195
|
const useProxy = !isInlineImage(key) &&
|
|
181
196
|
!isSameOrigin &&
|
|
182
197
|
!isBlobImage(key) &&
|
package/dist/lib/css/index.js
CHANGED
|
@@ -36,6 +36,7 @@ const transform_origin_1 = require("./property-descriptors/transform-origin");
|
|
|
36
36
|
const rotate_1 = require("./property-descriptors/rotate");
|
|
37
37
|
const visibility_1 = require("./property-descriptors/visibility");
|
|
38
38
|
const word_break_1 = require("./property-descriptors/word-break");
|
|
39
|
+
const white_space_1 = require("./property-descriptors/white-space");
|
|
39
40
|
const writing_mode_1 = require("./property-descriptors/writing-mode");
|
|
40
41
|
const z_index_1 = require("./property-descriptors/z-index");
|
|
41
42
|
const parser_1 = require("./syntax/parser");
|
|
@@ -227,6 +228,7 @@ CSSParsedDeclaration.standardProps = [
|
|
|
227
228
|
['webkitTextStrokeWidth', webkit_text_stroke_width_1.webkitTextStrokeWidth, 'webkitTextStrokeWidth'],
|
|
228
229
|
['webkitLineClamp', webkit_line_clamp_1.webkitLineClamp, 'webkitLineClamp'],
|
|
229
230
|
['wordBreak', word_break_1.wordBreak, 'wordBreak'],
|
|
231
|
+
['whiteSpace', white_space_1.whiteSpace, 'whiteSpace'],
|
|
230
232
|
['writingMode', writing_mode_1.writingMode, 'writingMode'],
|
|
231
233
|
['zIndex', z_index_1.zIndex, 'zIndex'],
|
|
232
234
|
['objectFit', object_fit_1.objectFit, 'objectFit'],
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.whiteSpace = exports.WHITE_SPACE = void 0;
|
|
4
|
+
var WHITE_SPACE;
|
|
5
|
+
(function (WHITE_SPACE) {
|
|
6
|
+
WHITE_SPACE["NORMAL"] = "normal";
|
|
7
|
+
WHITE_SPACE["PRE"] = "pre";
|
|
8
|
+
WHITE_SPACE["NOWRAP"] = "nowrap";
|
|
9
|
+
WHITE_SPACE["PRE_WRAP"] = "pre-wrap";
|
|
10
|
+
WHITE_SPACE["PRE_LINE"] = "pre-line";
|
|
11
|
+
})(WHITE_SPACE || (exports.WHITE_SPACE = WHITE_SPACE = {}));
|
|
12
|
+
exports.whiteSpace = {
|
|
13
|
+
name: 'white-space',
|
|
14
|
+
initialValue: 'normal',
|
|
15
|
+
prefix: false,
|
|
16
|
+
type: 2 /* PropertyDescriptorParsingType.IDENT_VALUE */,
|
|
17
|
+
parse: (_context, value) => {
|
|
18
|
+
switch (value) {
|
|
19
|
+
case 'pre':
|
|
20
|
+
return WHITE_SPACE.PRE;
|
|
21
|
+
case 'nowrap':
|
|
22
|
+
return WHITE_SPACE.NOWRAP;
|
|
23
|
+
case 'pre-wrap':
|
|
24
|
+
return WHITE_SPACE.PRE_WRAP;
|
|
25
|
+
case 'pre-line':
|
|
26
|
+
return WHITE_SPACE.PRE_LINE;
|
|
27
|
+
case 'normal':
|
|
28
|
+
default:
|
|
29
|
+
return WHITE_SPACE.NORMAL;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -28,7 +28,16 @@ class ElementContainer {
|
|
|
28
28
|
if (element.tagName === 'FIELDSET') {
|
|
29
29
|
const legend = element.querySelector(':scope > legend');
|
|
30
30
|
if (legend) {
|
|
31
|
-
|
|
31
|
+
const legendBounds = (0, bounds_1.parseBounds)(this.context, legend);
|
|
32
|
+
this.legendBounds = legendBounds;
|
|
33
|
+
// A <fieldset>'s legend is centered on its top border line and
|
|
34
|
+
// overflows ABOVE it. Chrome's getBoundingClientRect() for the
|
|
35
|
+
// fieldset includes that overflow, so bounds.top is too high and
|
|
36
|
+
// the border would be drawn above the legend. Shift the bounds so
|
|
37
|
+
// the top border sits at the legend's vertical center, matching
|
|
38
|
+
// how the browser renders fieldset + legend (issue #227).
|
|
39
|
+
const borderBoxTop = legendBounds.top + legendBounds.height / 2 - this.styles.borderTopWidth / 2;
|
|
40
|
+
this.bounds = new bounds_1.Bounds(this.bounds.left, borderBoxTop, this.bounds.width, this.bounds.height - (borderBoxTop - this.bounds.top));
|
|
32
41
|
}
|
|
33
42
|
}
|
|
34
43
|
if ((0, debugger_1.isDebugging)(element, 4 /* DebuggerType.RENDER */)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapTextToWidth = void 0;
|
|
3
4
|
exports.renderReplacedElements = renderReplacedElements;
|
|
4
5
|
exports.renderFormElements = renderFormElements;
|
|
5
6
|
exports.renderListMarker = renderListMarker;
|
|
@@ -19,6 +20,7 @@ const font_utils_1 = require("./font-utils");
|
|
|
19
20
|
const image_rendering_1 = require("../../css/property-descriptors/image-rendering");
|
|
20
21
|
const length_percentage_1 = require("../../css/types/length-percentage");
|
|
21
22
|
const line_height_1 = require("../../css/property-descriptors/line-height");
|
|
23
|
+
const white_space_1 = require("../../css/property-descriptors/white-space");
|
|
22
24
|
const bitwise_1 = require("../../core/bitwise");
|
|
23
25
|
/**
|
|
24
26
|
* Render replaced elements: Image, Canvas, SVG, IFrame.
|
|
@@ -134,12 +136,63 @@ function renderFormElements(ctx, textRenderer, pathFn, container, styles) {
|
|
|
134
136
|
new vector_1.Vector(bounds.left, bounds.top + bounds.height)
|
|
135
137
|
]);
|
|
136
138
|
ctx.clip();
|
|
137
|
-
|
|
139
|
+
// A <textarea> soft-wraps its value at the element width (unless
|
|
140
|
+
// white-space is `pre`/`nowrap`, e.g. wrap="off"); text inputs are
|
|
141
|
+
// single-line. Render textareas line-by-line so long values wrap
|
|
142
|
+
// instead of being clipped on one line (issue #228).
|
|
143
|
+
const lines = (0, exports.wrapTextToWidth)(ctx, container.value, bounds.width, styles.whiteSpace === white_space_1.WHITE_SPACE.PRE || styles.whiteSpace === white_space_1.WHITE_SPACE.NOWRAP);
|
|
144
|
+
const fontSizeValue = (0, length_percentage_1.getAbsoluteValue)(styles.fontSize, 0);
|
|
145
|
+
const lineHeight = (0, line_height_1.computeLineHeight)(styles.lineHeight, fontSizeValue);
|
|
146
|
+
lines.forEach((line, index) => {
|
|
147
|
+
let lineX = textBounds.left;
|
|
148
|
+
const lineWidth = ctx.measureText(line).width;
|
|
149
|
+
switch (styles.textAlign) {
|
|
150
|
+
case 1 /* TEXT_ALIGN.CENTER */:
|
|
151
|
+
lineX += (bounds.width - lineWidth) / 2;
|
|
152
|
+
break;
|
|
153
|
+
case 2 /* TEXT_ALIGN.RIGHT */:
|
|
154
|
+
lineX += bounds.width - lineWidth;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
const lineBounds = new bounds_1.Bounds(lineX, textBounds.top + index * lineHeight, 0, 0);
|
|
158
|
+
textRenderer.renderTextWithLetterSpacing(new text_1.TextBounds(line, lineBounds), styles.letterSpacing, baseline, styles.writingMode);
|
|
159
|
+
});
|
|
138
160
|
ctx.restore();
|
|
139
161
|
ctx.textBaseline = 'alphabetic';
|
|
140
162
|
ctx.textAlign = 'left';
|
|
141
163
|
}
|
|
142
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Split text into lines that fit the given pixel width, preserving hard line
|
|
167
|
+
* breaks and (when wrapping is enabled) breaking at word boundaries — mirroring
|
|
168
|
+
* how a <textarea> lays out its value.
|
|
169
|
+
* @internal exported for testing
|
|
170
|
+
*/
|
|
171
|
+
const wrapTextToWidth = (ctx, text, maxWidth, noWrap) => {
|
|
172
|
+
const lines = [];
|
|
173
|
+
const paragraphs = text.split('\n');
|
|
174
|
+
for (const paragraph of paragraphs) {
|
|
175
|
+
if (noWrap) {
|
|
176
|
+
lines.push(paragraph);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
const words = paragraph.split(/(\s+)/);
|
|
180
|
+
let line = '';
|
|
181
|
+
for (const word of words) {
|
|
182
|
+
const candidate = line + word;
|
|
183
|
+
if (ctx.measureText(candidate).width <= maxWidth || line === '') {
|
|
184
|
+
line = candidate;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
lines.push(line);
|
|
188
|
+
line = word;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
lines.push(line);
|
|
192
|
+
}
|
|
193
|
+
return lines;
|
|
194
|
+
};
|
|
195
|
+
exports.wrapTextToWidth = wrapTextToWidth;
|
|
143
196
|
/**
|
|
144
197
|
* Render list-item marker (image or text).
|
|
145
198
|
*/
|
|
@@ -27,6 +27,7 @@ import { transformOrigin } from './property-descriptors/transform-origin';
|
|
|
27
27
|
import { rotate } from './property-descriptors/rotate';
|
|
28
28
|
import { visibility } from './property-descriptors/visibility';
|
|
29
29
|
import { wordBreak } from './property-descriptors/word-break';
|
|
30
|
+
import { whiteSpace } from './property-descriptors/white-space';
|
|
30
31
|
import { writingMode } from './property-descriptors/writing-mode';
|
|
31
32
|
import { zIndex } from './property-descriptors/z-index';
|
|
32
33
|
import { CSSValue } from './syntax/parser';
|
|
@@ -141,6 +142,7 @@ export declare class CSSParsedDeclaration {
|
|
|
141
142
|
webkitTextStrokeWidth: ReturnType<typeof webkitTextStrokeWidth.parse>;
|
|
142
143
|
webkitLineClamp: ReturnType<typeof webkitLineClamp.parse>;
|
|
143
144
|
wordBreak: ReturnType<typeof wordBreak.parse>;
|
|
145
|
+
whiteSpace: ReturnType<typeof whiteSpace.parse>;
|
|
144
146
|
writingMode: ReturnType<typeof writingMode.parse>;
|
|
145
147
|
zIndex: ReturnType<typeof zIndex.parse>;
|
|
146
148
|
objectFit: ReturnType<typeof objectFit.parse>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IPropertyIdentValueDescriptor } from '../property-descriptor';
|
|
2
|
+
export declare enum WHITE_SPACE {
|
|
3
|
+
NORMAL = "normal",
|
|
4
|
+
PRE = "pre",
|
|
5
|
+
NOWRAP = "nowrap",
|
|
6
|
+
PRE_WRAP = "pre-wrap",
|
|
7
|
+
PRE_LINE = "pre-line"
|
|
8
|
+
}
|
|
9
|
+
export declare const whiteSpace: IPropertyIdentValueDescriptor<WHITE_SPACE>;
|
|
@@ -36,6 +36,13 @@ export declare function renderReplacedElements(ctx: CanvasRenderingContext2D, co
|
|
|
36
36
|
* Render form element content: checkbox, radio, text input.
|
|
37
37
|
*/
|
|
38
38
|
export declare function renderFormElements(ctx: CanvasRenderingContext2D, textRenderer: TextRenderer, pathFn: (paths: Vector[]) => void, container: ElementContainer, styles: CSSParsedDeclaration): void;
|
|
39
|
+
/**
|
|
40
|
+
* Split text into lines that fit the given pixel width, preserving hard line
|
|
41
|
+
* breaks and (when wrapping is enabled) breaking at word boundaries — mirroring
|
|
42
|
+
* how a <textarea> lays out its value.
|
|
43
|
+
* @internal exported for testing
|
|
44
|
+
*/
|
|
45
|
+
export declare const wrapTextToWidth: (ctx: CanvasRenderingContext2D, text: string, maxWidth: number, noWrap: boolean) => string[];
|
|
39
46
|
/**
|
|
40
47
|
* Render list-item marker (image or text).
|
|
41
48
|
*/
|