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
  */
@@ -246,6 +246,30 @@ var Validator = class {
246
246
  };
247
247
  }
248
248
  /**
249
+ * Validate the backgroundColor option
250
+ *
251
+ * @param backgroundColor - CSS color string or null (transparent)
252
+ * @returns Validation result
253
+ */
254
+ validateBackgroundColor(backgroundColor) {
255
+ if (backgroundColor === null) return {
256
+ valid: true,
257
+ sanitized: null
258
+ };
259
+ if (typeof backgroundColor !== "string" || backgroundColor.length === 0) return {
260
+ valid: false,
261
+ error: "backgroundColor must be a CSS color string or null"
262
+ };
263
+ if (/[^a-zA-Z0-9%(),.#\s/-]/.test(backgroundColor)) return {
264
+ valid: false,
265
+ error: `backgroundColor contains characters that cannot appear in a CSS color: ${backgroundColor}`
266
+ };
267
+ return {
268
+ valid: true,
269
+ sanitized: backgroundColor
270
+ };
271
+ }
272
+ /**
249
273
  * Validate image timeout
250
274
  *
251
275
  * @param timeout - Timeout in milliseconds
@@ -390,6 +414,10 @@ var Validator = class {
390
414
  const nonceResult = this.validateCspNonce(options.cspNonce);
391
415
  if (!nonceResult.valid) errors.push(`CSP nonce: ${nonceResult.error}`);
392
416
  }
417
+ if (options.backgroundColor !== void 0) {
418
+ const colorResult = this.validateBackgroundColor(options.backgroundColor);
419
+ if (!colorResult.valid) errors.push(`backgroundColor: ${colorResult.error}`);
420
+ }
393
421
  if (this.config.customValidator) {
394
422
  const customResult = this.config.customValidator(options, "options");
395
423
  if (!customResult.valid) errors.push(`Custom validation: ${customResult.error}`);
@@ -8066,9 +8094,9 @@ const isSheetFullyApplied = (sheet, depth = 0) => {
8066
8094
  if (!rules) return true;
8067
8095
  for (let i = 0; i < rules.length; i++) {
8068
8096
  const rule = rules[i];
8069
- if (rule && typeof CSSImportRule !== "undefined" && rule instanceof CSSImportRule) {
8070
- if (!rule.styleSheet || !isSheetFullyApplied(rule.styleSheet, depth + 1)) return false;
8071
- }
8097
+ if (!rule || !("styleSheet" in rule)) continue;
8098
+ const inner = rule.styleSheet;
8099
+ if (!inner || !isSheetFullyApplied(inner, depth + 1)) return false;
8072
8100
  }
8073
8101
  return true;
8074
8102
  };
@@ -8110,7 +8138,7 @@ const collectLinks = (root, out) => {
8110
8138
  * remain as the settle path for plain <link> fetches.
8111
8139
  * @internal – exported for testing only.
8112
8140
  */
8113
- const stylesheetsReady = (documentClone, logger, signal) => {
8141
+ const stylesheetsReady = (documentClone, logger, signal, timeoutMs = STYLESHEET_READY_TIMEOUT_MS) => {
8114
8142
  if (signal?.aborted) return Promise.resolve();
8115
8143
  const pending = [];
8116
8144
  const links = [];
@@ -8140,8 +8168,8 @@ const stylesheetsReady = (documentClone, logger, signal) => {
8140
8168
  const timer = setInterval(() => {
8141
8169
  elapsed += 50;
8142
8170
  if (link.sheet) settle();
8143
- else if (elapsed >= 1e4) {
8144
- logger?.warn(`Stylesheet ${href} did not finish loading within ${STYLESHEET_READY_TIMEOUT_MS / 1e3}s; continuing with a possibly unstyled clone`);
8171
+ else if (elapsed >= timeoutMs) {
8172
+ logger?.warn(`Stylesheet ${href} did not finish loading within ${timeoutMs / 1e3}s; continuing with a possibly unstyled clone`);
8145
8173
  settle();
8146
8174
  }
8147
8175
  }, 50);
@@ -8158,8 +8186,8 @@ const stylesheetsReady = (documentClone, logger, signal) => {
8158
8186
  const timer = setInterval(() => {
8159
8187
  elapsed += 50;
8160
8188
  const currentSheet = style.sheet;
8161
- if (currentSheet && isSheetFullyApplied(currentSheet) || elapsed >= 1e4) {
8162
- 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`);
8189
+ if (currentSheet && isSheetFullyApplied(currentSheet) || elapsed >= timeoutMs) {
8190
+ 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`);
8163
8191
  clearInterval(timer);
8164
8192
  resolve();
8165
8193
  }
@@ -9847,7 +9875,7 @@ const parseStackTree = (parent, stackingContext, realStackingContext, listItems)
9847
9875
  if (treatAsRealStackingContext || createsStackingContext) {
9848
9876
  const parentStack = treatAsRealStackingContext || child.styles.isPositioned() ? realStackingContext : stackingContext;
9849
9877
  const stack = new StackingContext(paintContainer);
9850
- if (child.styles.isPositioned() || child.styles.opacity < 1 || child.styles.isTransformed()) {
9878
+ if (treatAsRealStackingContext || child.styles.isPositioned() || child.styles.opacity < 1 || child.styles.isTransformed()) {
9851
9879
  const order = child.styles.zIndex.order;
9852
9880
  if (order < 0) {
9853
9881
  let index = 0;
@@ -12145,6 +12173,7 @@ const paintRadialLayer = (ctx, layer, area) => {
12145
12173
  ctx.fillStyle = rasterized.lastColor;
12146
12174
  ctx.fillRect(area.left, area.top, area.width, area.height);
12147
12175
  ctx.drawImage(rasterized.canvas, area.left + cx - rx, area.top + cy - ry, rx * 2, ry * 2);
12176
+ releaseSurface(rasterized.canvas);
12148
12177
  };
12149
12178
  /** Draw one gradient layer filling the whole mask area. */
12150
12179
  const paintGradientLayer = (ctx, layer, area) => {
@@ -13058,7 +13087,10 @@ var CanvasRenderer = class CanvasRenderer {
13058
13087
  maskCanvas.width = surface.width;
13059
13088
  maskCanvas.height = surface.height;
13060
13089
  const maskCtx = maskCanvas.getContext("2d");
13061
- if (!maskCtx) return;
13090
+ if (!maskCtx) {
13091
+ releaseSurface(maskCanvas);
13092
+ return;
13093
+ }
13062
13094
  maskCtx.scale(this.options.scale, this.options.scale);
13063
13095
  maskCtx.translate(-bounds.left, -bounds.top);
13064
13096
  try {
@@ -13070,6 +13102,7 @@ var CanvasRenderer = class CanvasRenderer {
13070
13102
  }, async (url) => await this.context.cache.match(url));
13071
13103
  } catch (error) {
13072
13104
  this.context.logger.error(`Error rendering mask-image: ${error instanceof Error ? error.message : error}`);
13105
+ releaseSurface(maskCanvas);
13073
13106
  return;
13074
13107
  }
13075
13108
  ctx.save();
@@ -13079,6 +13112,7 @@ var CanvasRenderer = class CanvasRenderer {
13079
13112
  ctx.drawImage(maskCanvas, 0, 0);
13080
13113
  } finally {
13081
13114
  ctx.restore();
13115
+ releaseSurface(maskCanvas);
13082
13116
  }
13083
13117
  }
13084
13118
  /**
@@ -13483,6 +13517,7 @@ var CanvasRenderer = class CanvasRenderer {
13483
13517
  ctx.fillStyle = rasterized.lastColor;
13484
13518
  ctx.fillRect(0, 0, width, height);
13485
13519
  ctx.drawImage(rasterized.canvas, cx - rx, cy - ry, rx * 2, ry * 2);
13520
+ releaseSurface(rasterized.canvas);
13486
13521
  return canvas;
13487
13522
  }
13488
13523
  if (source.type === 5 || source.type === 6) {