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
package/dist/html2canvas-pro.js
CHANGED
|
@@ -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
|
*/
|
|
@@ -3903,6 +3903,7 @@
|
|
|
3903
3903
|
initialValue: "none",
|
|
3904
3904
|
type: 1,
|
|
3905
3905
|
prefix: false,
|
|
3906
|
+
skipCache: true,
|
|
3906
3907
|
parse: (context, tokens) => {
|
|
3907
3908
|
if (tokens.length === 0) return [];
|
|
3908
3909
|
const first = tokens[0];
|
|
@@ -4345,6 +4346,7 @@
|
|
|
4345
4346
|
initialValue: "none",
|
|
4346
4347
|
type: 0,
|
|
4347
4348
|
prefix: false,
|
|
4349
|
+
skipCache: true,
|
|
4348
4350
|
parse: (context, token) => {
|
|
4349
4351
|
if (token.type === 20 && token.value === "none") return null;
|
|
4350
4352
|
return image.parse(context, token);
|
|
@@ -5365,6 +5367,7 @@
|
|
|
5365
5367
|
initialValue: "none",
|
|
5366
5368
|
prefix: false,
|
|
5367
5369
|
type: 1,
|
|
5370
|
+
skipCache: true,
|
|
5368
5371
|
parse: (context, tokens) => {
|
|
5369
5372
|
if (tokens.length === 0) return null;
|
|
5370
5373
|
const filtered = tokens.filter((t) => nonFunctionArgSeparator(t) && isSupportedImage(t));
|
|
@@ -6301,7 +6304,7 @@
|
|
|
6301
6304
|
valueCache = /* @__PURE__ */ new Map();
|
|
6302
6305
|
parseCache.set(descriptor, valueCache);
|
|
6303
6306
|
}
|
|
6304
|
-
if (!(descriptor.type === 3 && descriptor.format === "image")) {
|
|
6307
|
+
if (!(descriptor.skipCache || descriptor.type === 3 && descriptor.format === "image")) {
|
|
6305
6308
|
if (valueCache.size >= 200) {
|
|
6306
6309
|
const oldestKey = valueCache.keys().next().value;
|
|
6307
6310
|
valueCache.delete(oldestKey);
|
|
@@ -8447,20 +8450,124 @@
|
|
|
8447
8450
|
this.compositeOperation = MIX_BLEND_MODE_TO_COMPOSITE[mixBlendMode];
|
|
8448
8451
|
}
|
|
8449
8452
|
};
|
|
8450
|
-
var FilterEffect = class {
|
|
8453
|
+
var FilterEffect = class FilterEffect {
|
|
8451
8454
|
constructor(filterString) {
|
|
8452
8455
|
this.type = 5;
|
|
8453
8456
|
this.target = 6;
|
|
8454
|
-
const
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
|
|
8459
|
-
|
|
8460
|
-
|
|
8461
|
-
|
|
8462
|
-
|
|
8463
|
-
|
|
8457
|
+
const parsed = FilterEffect.parseDropShadow(filterString);
|
|
8458
|
+
this.shadow = parsed.shadow;
|
|
8459
|
+
this.safeFilterString = parsed.safeFilterString;
|
|
8460
|
+
}
|
|
8461
|
+
/**
|
|
8462
|
+
* Parse a CSS filter string, extracting drop-shadow() parameters for
|
|
8463
|
+
* rendering via ctx.shadow* (which avoids canvas taint), and producing
|
|
8464
|
+
* a safe filter string with all drop-shadow() calls removed.
|
|
8465
|
+
*
|
|
8466
|
+
* Uses paren-depth tracking to correctly handle nested function
|
|
8467
|
+
* arguments — e.g. rgba() / hsla() inside drop-shadow() — which
|
|
8468
|
+
* the previous regex-based approach ([^)]+) could not handle.
|
|
8469
|
+
*
|
|
8470
|
+
* Per CSS spec the shadow value is: <color>? && <length>{2,3}
|
|
8471
|
+
* (components can appear in any order).
|
|
8472
|
+
*/
|
|
8473
|
+
static parseDropShadow(filterString) {
|
|
8474
|
+
if (!filterString) return { safeFilterString: "" };
|
|
8475
|
+
const matches = FilterEffect.findDropShadows(filterString);
|
|
8476
|
+
if (matches.length === 0) return { safeFilterString: filterString };
|
|
8477
|
+
const shadow = FilterEffect.parseDropShadowBody(matches[0].body);
|
|
8478
|
+
let result = "";
|
|
8479
|
+
let lastEnd = 0;
|
|
8480
|
+
for (const m of matches) {
|
|
8481
|
+
result += filterString.slice(lastEnd, m.start);
|
|
8482
|
+
lastEnd = m.end;
|
|
8483
|
+
}
|
|
8484
|
+
result += filterString.slice(lastEnd);
|
|
8485
|
+
return {
|
|
8486
|
+
shadow,
|
|
8487
|
+
safeFilterString: result.replace(/\s+/g, " ").trim()
|
|
8488
|
+
};
|
|
8489
|
+
}
|
|
8490
|
+
/**
|
|
8491
|
+
* Find all drop-shadow() function calls in a CSS filter string.
|
|
8492
|
+
* Uses paren-depth tracking to correctly skip over nested
|
|
8493
|
+
* parentheses (e.g. rgba(0, 0, 0, 0.15)).
|
|
8494
|
+
*/
|
|
8495
|
+
static findDropShadows(str) {
|
|
8496
|
+
const results = [];
|
|
8497
|
+
const re = /drop-shadow\(/gi;
|
|
8498
|
+
let m;
|
|
8499
|
+
while ((m = re.exec(str)) !== null) {
|
|
8500
|
+
const openParen = m.index + 12 - 1;
|
|
8501
|
+
const start = m.index;
|
|
8502
|
+
let depth = 1;
|
|
8503
|
+
let pos = openParen + 1;
|
|
8504
|
+
while (pos < str.length && depth > 0) {
|
|
8505
|
+
const ch = str[pos];
|
|
8506
|
+
if (ch === "(") depth++;
|
|
8507
|
+
else if (ch === ")") depth--;
|
|
8508
|
+
pos++;
|
|
8509
|
+
}
|
|
8510
|
+
if (depth !== 0) break;
|
|
8511
|
+
const body = str.slice(openParen + 1, pos - 1);
|
|
8512
|
+
results.push({
|
|
8513
|
+
body,
|
|
8514
|
+
start,
|
|
8515
|
+
end: pos
|
|
8516
|
+
});
|
|
8517
|
+
}
|
|
8518
|
+
return results;
|
|
8519
|
+
}
|
|
8520
|
+
/**
|
|
8521
|
+
* Parse the body of a single drop-shadow() function.
|
|
8522
|
+
* Body format (order-independent): <offsetX> <offsetY> [<blur>] [<color>?]
|
|
8523
|
+
*/
|
|
8524
|
+
static parseDropShadowBody(body) {
|
|
8525
|
+
const tokens = FilterEffect.tokenizeFilterArgs(body.trim());
|
|
8526
|
+
const lengths = [];
|
|
8527
|
+
let color;
|
|
8528
|
+
for (const token of tokens) if (FilterEffect.isCSSLength(token)) lengths.push(parseFloat(token));
|
|
8529
|
+
else if (token) color = token;
|
|
8530
|
+
if (lengths.length < 2 || lengths.length > 3) return;
|
|
8531
|
+
return {
|
|
8532
|
+
offsetX: lengths[0],
|
|
8533
|
+
offsetY: lengths[1],
|
|
8534
|
+
blur: lengths[2] ?? 0,
|
|
8535
|
+
color: color ?? "rgba(0,0,0,1)"
|
|
8536
|
+
};
|
|
8537
|
+
}
|
|
8538
|
+
/**
|
|
8539
|
+
* Split whitespace-separated tokens while keeping parenthesised
|
|
8540
|
+
* expressions intact.
|
|
8541
|
+
*
|
|
8542
|
+
* e.g. "rgba(0, 0, 0, 0.15) 0px 1px 2px"
|
|
8543
|
+
* → ["rgba(0, 0, 0, 0.15)", "0px", "1px", "2px"]
|
|
8544
|
+
*/
|
|
8545
|
+
static tokenizeFilterArgs(str) {
|
|
8546
|
+
const tokens = [];
|
|
8547
|
+
let current = "";
|
|
8548
|
+
let depth = 0;
|
|
8549
|
+
for (let i = 0; i < str.length; i++) {
|
|
8550
|
+
const ch = str[i];
|
|
8551
|
+
if (ch === "(") {
|
|
8552
|
+
depth++;
|
|
8553
|
+
current += ch;
|
|
8554
|
+
} else if (ch === ")") {
|
|
8555
|
+
depth--;
|
|
8556
|
+
current += ch;
|
|
8557
|
+
} else if (/\s/.test(ch)) {
|
|
8558
|
+
if (depth > 0) current += ch;
|
|
8559
|
+
else if (current) {
|
|
8560
|
+
tokens.push(current);
|
|
8561
|
+
current = "";
|
|
8562
|
+
}
|
|
8563
|
+
} else current += ch;
|
|
8564
|
+
}
|
|
8565
|
+
if (current) tokens.push(current);
|
|
8566
|
+
return tokens;
|
|
8567
|
+
}
|
|
8568
|
+
/** True when token looks like a CSS length: number with optional unit. */
|
|
8569
|
+
static isCSSLength(token) {
|
|
8570
|
+
return /^-?[\d.]+(px|em|rem|pt|cm|mm|in|pc|ex|ch|vw|vh|vmin|vmax|%)?$/i.test(token);
|
|
8464
8571
|
}
|
|
8465
8572
|
};
|
|
8466
8573
|
const isTransformEffect = (effect) => effect.type === 0;
|