html2canvas-pro 2.2.2 → 2.2.4
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.esm.js +120 -13
- package/dist/html2canvas-pro.esm.js.map +1 -1
- package/dist/html2canvas-pro.js +120 -13
- package/dist/html2canvas-pro.js.map +1 -1
- package/dist/html2canvas-pro.min.js +3 -3
- package/dist/lib/css/index.js +9 -4
- package/dist/lib/css/property-descriptors/background-image.js +1 -0
- package/dist/lib/css/property-descriptors/border-image-source.js +1 -0
- package/dist/lib/css/property-descriptors/list-style-image.js +1 -0
- package/dist/lib/render/effects.js +134 -13
- package/dist/types/css/property-descriptor.d.ts +4 -0
- package/dist/types/render/effects.d.ts +35 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* html2canvas-pro 2.2.
|
|
2
|
+
* html2canvas-pro 2.2.4 <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
|
*/
|
|
@@ -3896,6 +3896,7 @@ const backgroundImage = {
|
|
|
3896
3896
|
initialValue: "none",
|
|
3897
3897
|
type: 1,
|
|
3898
3898
|
prefix: false,
|
|
3899
|
+
skipCache: true,
|
|
3899
3900
|
parse: (context, tokens) => {
|
|
3900
3901
|
if (tokens.length === 0) return [];
|
|
3901
3902
|
const first = tokens[0];
|
|
@@ -4338,6 +4339,7 @@ const listStyleImage = {
|
|
|
4338
4339
|
initialValue: "none",
|
|
4339
4340
|
type: 0,
|
|
4340
4341
|
prefix: false,
|
|
4342
|
+
skipCache: true,
|
|
4341
4343
|
parse: (context, token) => {
|
|
4342
4344
|
if (token.type === 20 && token.value === "none") return null;
|
|
4343
4345
|
return image.parse(context, token);
|
|
@@ -5358,6 +5360,7 @@ const borderImageSource = {
|
|
|
5358
5360
|
initialValue: "none",
|
|
5359
5361
|
prefix: false,
|
|
5360
5362
|
type: 1,
|
|
5363
|
+
skipCache: true,
|
|
5361
5364
|
parse: (context, tokens) => {
|
|
5362
5365
|
if (tokens.length === 0) return null;
|
|
5363
5366
|
const filtered = tokens.filter((t) => nonFunctionArgSeparator(t) && isSupportedImage(t));
|
|
@@ -6294,7 +6297,7 @@ const parse = (context, descriptor, style) => {
|
|
|
6294
6297
|
valueCache = /* @__PURE__ */ new Map();
|
|
6295
6298
|
parseCache.set(descriptor, valueCache);
|
|
6296
6299
|
}
|
|
6297
|
-
if (!(descriptor.type === 3 && descriptor.format === "image")) {
|
|
6300
|
+
if (!(descriptor.skipCache || descriptor.type === 3 && descriptor.format === "image")) {
|
|
6298
6301
|
if (valueCache.size >= 200) {
|
|
6299
6302
|
const oldestKey = valueCache.keys().next().value;
|
|
6300
6303
|
valueCache.delete(oldestKey);
|
|
@@ -8440,20 +8443,124 @@ var BlendEffect = class {
|
|
|
8440
8443
|
this.compositeOperation = MIX_BLEND_MODE_TO_COMPOSITE[mixBlendMode];
|
|
8441
8444
|
}
|
|
8442
8445
|
};
|
|
8443
|
-
var FilterEffect = class {
|
|
8446
|
+
var FilterEffect = class FilterEffect {
|
|
8444
8447
|
constructor(filterString) {
|
|
8445
8448
|
this.type = 5;
|
|
8446
8449
|
this.target = 6;
|
|
8447
|
-
const
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8450
|
+
const parsed = FilterEffect.parseDropShadow(filterString);
|
|
8451
|
+
this.shadow = parsed.shadow;
|
|
8452
|
+
this.safeFilterString = parsed.safeFilterString;
|
|
8453
|
+
}
|
|
8454
|
+
/**
|
|
8455
|
+
* Parse a CSS filter string, extracting drop-shadow() parameters for
|
|
8456
|
+
* rendering via ctx.shadow* (which avoids canvas taint), and producing
|
|
8457
|
+
* a safe filter string with all drop-shadow() calls removed.
|
|
8458
|
+
*
|
|
8459
|
+
* Uses paren-depth tracking to correctly handle nested function
|
|
8460
|
+
* arguments — e.g. rgba() / hsla() inside drop-shadow() — which
|
|
8461
|
+
* the previous regex-based approach ([^)]+) could not handle.
|
|
8462
|
+
*
|
|
8463
|
+
* Per CSS spec the shadow value is: <color>? && <length>{2,3}
|
|
8464
|
+
* (components can appear in any order).
|
|
8465
|
+
*/
|
|
8466
|
+
static parseDropShadow(filterString) {
|
|
8467
|
+
if (!filterString) return { safeFilterString: "" };
|
|
8468
|
+
const matches = FilterEffect.findDropShadows(filterString);
|
|
8469
|
+
if (matches.length === 0) return { safeFilterString: filterString };
|
|
8470
|
+
const shadow = FilterEffect.parseDropShadowBody(matches[0].body);
|
|
8471
|
+
let result = "";
|
|
8472
|
+
let lastEnd = 0;
|
|
8473
|
+
for (const m of matches) {
|
|
8474
|
+
result += filterString.slice(lastEnd, m.start);
|
|
8475
|
+
lastEnd = m.end;
|
|
8476
|
+
}
|
|
8477
|
+
result += filterString.slice(lastEnd);
|
|
8478
|
+
return {
|
|
8479
|
+
shadow,
|
|
8480
|
+
safeFilterString: result.replace(/\s+/g, " ").trim()
|
|
8481
|
+
};
|
|
8482
|
+
}
|
|
8483
|
+
/**
|
|
8484
|
+
* Find all drop-shadow() function calls in a CSS filter string.
|
|
8485
|
+
* Uses paren-depth tracking to correctly skip over nested
|
|
8486
|
+
* parentheses (e.g. rgba(0, 0, 0, 0.15)).
|
|
8487
|
+
*/
|
|
8488
|
+
static findDropShadows(str) {
|
|
8489
|
+
const results = [];
|
|
8490
|
+
const re = /drop-shadow\(/gi;
|
|
8491
|
+
let m;
|
|
8492
|
+
while ((m = re.exec(str)) !== null) {
|
|
8493
|
+
const openParen = m.index + 12 - 1;
|
|
8494
|
+
const start = m.index;
|
|
8495
|
+
let depth = 1;
|
|
8496
|
+
let pos = openParen + 1;
|
|
8497
|
+
while (pos < str.length && depth > 0) {
|
|
8498
|
+
const ch = str[pos];
|
|
8499
|
+
if (ch === "(") depth++;
|
|
8500
|
+
else if (ch === ")") depth--;
|
|
8501
|
+
pos++;
|
|
8502
|
+
}
|
|
8503
|
+
if (depth !== 0) break;
|
|
8504
|
+
const body = str.slice(openParen + 1, pos - 1);
|
|
8505
|
+
results.push({
|
|
8506
|
+
body,
|
|
8507
|
+
start,
|
|
8508
|
+
end: pos
|
|
8509
|
+
});
|
|
8510
|
+
}
|
|
8511
|
+
return results;
|
|
8512
|
+
}
|
|
8513
|
+
/**
|
|
8514
|
+
* Parse the body of a single drop-shadow() function.
|
|
8515
|
+
* Body format (order-independent): <offsetX> <offsetY> [<blur>] [<color>?]
|
|
8516
|
+
*/
|
|
8517
|
+
static parseDropShadowBody(body) {
|
|
8518
|
+
const tokens = FilterEffect.tokenizeFilterArgs(body.trim());
|
|
8519
|
+
const lengths = [];
|
|
8520
|
+
let color;
|
|
8521
|
+
for (const token of tokens) if (FilterEffect.isCSSLength(token)) lengths.push(parseFloat(token));
|
|
8522
|
+
else if (token) color = token;
|
|
8523
|
+
if (lengths.length < 2 || lengths.length > 3) return;
|
|
8524
|
+
return {
|
|
8525
|
+
offsetX: lengths[0],
|
|
8526
|
+
offsetY: lengths[1],
|
|
8527
|
+
blur: lengths[2] ?? 0,
|
|
8528
|
+
color: color ?? "rgba(0,0,0,1)"
|
|
8529
|
+
};
|
|
8530
|
+
}
|
|
8531
|
+
/**
|
|
8532
|
+
* Split whitespace-separated tokens while keeping parenthesised
|
|
8533
|
+
* expressions intact.
|
|
8534
|
+
*
|
|
8535
|
+
* e.g. "rgba(0, 0, 0, 0.15) 0px 1px 2px"
|
|
8536
|
+
* → ["rgba(0, 0, 0, 0.15)", "0px", "1px", "2px"]
|
|
8537
|
+
*/
|
|
8538
|
+
static tokenizeFilterArgs(str) {
|
|
8539
|
+
const tokens = [];
|
|
8540
|
+
let current = "";
|
|
8541
|
+
let depth = 0;
|
|
8542
|
+
for (let i = 0; i < str.length; i++) {
|
|
8543
|
+
const ch = str[i];
|
|
8544
|
+
if (ch === "(") {
|
|
8545
|
+
depth++;
|
|
8546
|
+
current += ch;
|
|
8547
|
+
} else if (ch === ")") {
|
|
8548
|
+
depth--;
|
|
8549
|
+
current += ch;
|
|
8550
|
+
} else if (/\s/.test(ch)) {
|
|
8551
|
+
if (depth > 0) current += ch;
|
|
8552
|
+
else if (current) {
|
|
8553
|
+
tokens.push(current);
|
|
8554
|
+
current = "";
|
|
8555
|
+
}
|
|
8556
|
+
} else current += ch;
|
|
8557
|
+
}
|
|
8558
|
+
if (current) tokens.push(current);
|
|
8559
|
+
return tokens;
|
|
8560
|
+
}
|
|
8561
|
+
/** True when token looks like a CSS length: number with optional unit. */
|
|
8562
|
+
static isCSSLength(token) {
|
|
8563
|
+
return /^-?[\d.]+(px|em|rem|pt|cm|mm|in|pc|ex|ch|vw|vh|vmin|vmax|%)?$/i.test(token);
|
|
8457
8564
|
}
|
|
8458
8565
|
};
|
|
8459
8566
|
const isTransformEffect = (effect) => effect.type === 0;
|