html2canvas-pro 2.3.7 → 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.
@@ -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
- const useCORS = !isInlineImage(key) && this._options.useCORS === true && features_1.FEATURES.SUPPORT_CORS_IMAGES && !isSameOrigin;
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) &&
@@ -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
+ };
@@ -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
- textRenderer.renderTextWithLetterSpacing(new text_1.TextBounds(container.value, textBounds), styles.letterSpacing, baseline, styles.writingMode);
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
  */
package/package.json CHANGED
@@ -19,7 +19,7 @@
19
19
  "default": "./dist/html2canvas-pro.esm.js"
20
20
  }
21
21
  },
22
- "version": "2.3.7",
22
+ "version": "2.3.8",
23
23
  "author": {
24
24
  "name": "yorickshan",
25
25
  "email": "yorickshan@gmail.com",