html2canvas-pro 2.5.4 → 2.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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * html2canvas-pro 2.5.4 <https://yorickshan.github.io/html2canvas-pro/>
2
+ * html2canvas-pro 2.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
  */
@@ -242,6 +242,30 @@ var Validator = class {
242
242
  };
243
243
  }
244
244
  /**
245
+ * Validate the backgroundColor option
246
+ *
247
+ * @param backgroundColor - CSS color string or null (transparent)
248
+ * @returns Validation result
249
+ */
250
+ validateBackgroundColor(backgroundColor) {
251
+ if (backgroundColor === null) return {
252
+ valid: true,
253
+ sanitized: null
254
+ };
255
+ if (typeof backgroundColor !== "string" || backgroundColor.length === 0) return {
256
+ valid: false,
257
+ error: "backgroundColor must be a CSS color string or null"
258
+ };
259
+ if (/[^a-zA-Z0-9%(),.#\s/-]/.test(backgroundColor)) return {
260
+ valid: false,
261
+ error: `backgroundColor contains characters that cannot appear in a CSS color: ${backgroundColor}`
262
+ };
263
+ return {
264
+ valid: true,
265
+ sanitized: backgroundColor
266
+ };
267
+ }
268
+ /**
245
269
  * Validate image timeout
246
270
  *
247
271
  * @param timeout - Timeout in milliseconds
@@ -386,6 +410,10 @@ var Validator = class {
386
410
  const nonceResult = this.validateCspNonce(options.cspNonce);
387
411
  if (!nonceResult.valid) errors.push(`CSP nonce: ${nonceResult.error}`);
388
412
  }
413
+ if (options.backgroundColor !== void 0) {
414
+ const colorResult = this.validateBackgroundColor(options.backgroundColor);
415
+ if (!colorResult.valid) errors.push(`backgroundColor: ${colorResult.error}`);
416
+ }
389
417
  if (this.config.customValidator) {
390
418
  const customResult = this.config.customValidator(options, "options");
391
419
  if (!customResult.valid) errors.push(`Custom validation: ${customResult.error}`);
@@ -8062,9 +8090,9 @@ const isSheetFullyApplied = (sheet, depth = 0) => {
8062
8090
  if (!rules) return true;
8063
8091
  for (let i = 0; i < rules.length; i++) {
8064
8092
  const rule = rules[i];
8065
- if (rule && typeof CSSImportRule !== "undefined" && rule instanceof CSSImportRule) {
8066
- if (!rule.styleSheet || !isSheetFullyApplied(rule.styleSheet, depth + 1)) return false;
8067
- }
8093
+ if (!rule || !("styleSheet" in rule)) continue;
8094
+ const inner = rule.styleSheet;
8095
+ if (!inner || !isSheetFullyApplied(inner, depth + 1)) return false;
8068
8096
  }
8069
8097
  return true;
8070
8098
  };
@@ -8106,7 +8134,7 @@ const collectLinks = (root, out) => {
8106
8134
  * remain as the settle path for plain <link> fetches.
8107
8135
  * @internal – exported for testing only.
8108
8136
  */
8109
- const stylesheetsReady = (documentClone, logger, signal) => {
8137
+ const stylesheetsReady = (documentClone, logger, signal, timeoutMs = STYLESHEET_READY_TIMEOUT_MS) => {
8110
8138
  if (signal?.aborted) return Promise.resolve();
8111
8139
  const pending = [];
8112
8140
  const links = [];
@@ -8136,8 +8164,8 @@ const stylesheetsReady = (documentClone, logger, signal) => {
8136
8164
  const timer = setInterval(() => {
8137
8165
  elapsed += 50;
8138
8166
  if (link.sheet) settle();
8139
- else if (elapsed >= 1e4) {
8140
- logger?.warn(`Stylesheet ${href} did not finish loading within ${STYLESHEET_READY_TIMEOUT_MS / 1e3}s; continuing with a possibly unstyled clone`);
8167
+ else if (elapsed >= timeoutMs) {
8168
+ logger?.warn(`Stylesheet ${href} did not finish loading within ${timeoutMs / 1e3}s; continuing with a possibly unstyled clone`);
8141
8169
  settle();
8142
8170
  }
8143
8171
  }, 50);
@@ -8154,8 +8182,8 @@ const stylesheetsReady = (documentClone, logger, signal) => {
8154
8182
  const timer = setInterval(() => {
8155
8183
  elapsed += 50;
8156
8184
  const currentSheet = style.sheet;
8157
- if (currentSheet && isSheetFullyApplied(currentSheet) || elapsed >= 1e4) {
8158
- if (elapsed >= 1e4 && !isSheetFullyApplied(currentSheet ?? null)) logger?.warn(`@import chain in an inline <style> did not finish loading within ${STYLESHEET_READY_TIMEOUT_MS / 1e3}s; continuing with a possibly unstyled clone`);
8185
+ if (currentSheet && isSheetFullyApplied(currentSheet) || elapsed >= timeoutMs) {
8186
+ if (elapsed >= timeoutMs && !isSheetFullyApplied(currentSheet ?? null)) logger?.warn(`@import chain in an inline <style> did not finish loading within ${timeoutMs / 1e3}s; continuing with a possibly unstyled clone`);
8159
8187
  clearInterval(timer);
8160
8188
  resolve();
8161
8189
  }
@@ -9843,7 +9871,7 @@ const parseStackTree = (parent, stackingContext, realStackingContext, listItems)
9843
9871
  if (treatAsRealStackingContext || createsStackingContext) {
9844
9872
  const parentStack = treatAsRealStackingContext || child.styles.isPositioned() ? realStackingContext : stackingContext;
9845
9873
  const stack = new StackingContext(paintContainer);
9846
- if (child.styles.isPositioned() || child.styles.opacity < 1 || child.styles.isTransformed()) {
9874
+ if (treatAsRealStackingContext || child.styles.isPositioned() || child.styles.opacity < 1 || child.styles.isTransformed()) {
9847
9875
  const order = child.styles.zIndex.order;
9848
9876
  if (order < 0) {
9849
9877
  let index = 0;
@@ -12141,6 +12169,7 @@ const paintRadialLayer = (ctx, layer, area) => {
12141
12169
  ctx.fillStyle = rasterized.lastColor;
12142
12170
  ctx.fillRect(area.left, area.top, area.width, area.height);
12143
12171
  ctx.drawImage(rasterized.canvas, area.left + cx - rx, area.top + cy - ry, rx * 2, ry * 2);
12172
+ releaseSurface(rasterized.canvas);
12144
12173
  };
12145
12174
  /** Draw one gradient layer filling the whole mask area. */
12146
12175
  const paintGradientLayer = (ctx, layer, area) => {
@@ -13054,7 +13083,10 @@ var CanvasRenderer = class CanvasRenderer {
13054
13083
  maskCanvas.width = surface.width;
13055
13084
  maskCanvas.height = surface.height;
13056
13085
  const maskCtx = maskCanvas.getContext("2d");
13057
- if (!maskCtx) return;
13086
+ if (!maskCtx) {
13087
+ releaseSurface(maskCanvas);
13088
+ return;
13089
+ }
13058
13090
  maskCtx.scale(this.options.scale, this.options.scale);
13059
13091
  maskCtx.translate(-bounds.left, -bounds.top);
13060
13092
  try {
@@ -13066,6 +13098,7 @@ var CanvasRenderer = class CanvasRenderer {
13066
13098
  }, async (url) => await this.context.cache.match(url));
13067
13099
  } catch (error) {
13068
13100
  this.context.logger.error(`Error rendering mask-image: ${error instanceof Error ? error.message : error}`);
13101
+ releaseSurface(maskCanvas);
13069
13102
  return;
13070
13103
  }
13071
13104
  ctx.save();
@@ -13075,6 +13108,7 @@ var CanvasRenderer = class CanvasRenderer {
13075
13108
  ctx.drawImage(maskCanvas, 0, 0);
13076
13109
  } finally {
13077
13110
  ctx.restore();
13111
+ releaseSurface(maskCanvas);
13078
13112
  }
13079
13113
  }
13080
13114
  /**
@@ -13479,6 +13513,7 @@ var CanvasRenderer = class CanvasRenderer {
13479
13513
  ctx.fillStyle = rasterized.lastColor;
13480
13514
  ctx.fillRect(0, 0, width, height);
13481
13515
  ctx.drawImage(rasterized.canvas, cx - rx, cy - ry, rx * 2, ry * 2);
13516
+ releaseSurface(rasterized.canvas);
13482
13517
  return canvas;
13483
13518
  }
13484
13519
  if (source.type === 5 || source.type === 6) {