image-exporter 1.0.7 → 1.0.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.
- package/README.md +2 -2
- package/bun.lockb +0 -0
- package/dist/image-exporter.es.js +1456 -684
- package/dist/image-exporter.umd.js +1463 -691
- package/package.json +3 -3
- package/src/capture/capture-element.ts +10 -7
- package/src/types.d.ts +1 -1
|
@@ -1,809 +1,1578 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
function handleFileNames(imageOptions, filenames) {
|
|
3
|
+
let proposedFilename = imageOptions.label;
|
|
4
|
+
if (imageOptions.includeScaleInLabel) proposedFilename += `_@${imageOptions.scale}x`;
|
|
5
|
+
const extension = `.${imageOptions.format}`;
|
|
6
|
+
proposedFilename += extension;
|
|
7
|
+
if (!filenames.includes(proposedFilename)) {
|
|
8
|
+
filenames.push(proposedFilename);
|
|
9
|
+
return proposedFilename;
|
|
10
|
+
}
|
|
11
|
+
const numberPattern = /-(\d+)$/;
|
|
12
|
+
const match = proposedFilename.match(numberPattern);
|
|
13
|
+
if (match) {
|
|
14
|
+
const baseFilename = proposedFilename.replace(numberPattern, "");
|
|
15
|
+
let counter = parseInt(match[1], 10);
|
|
16
|
+
while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
|
|
17
|
+
counter++;
|
|
18
|
+
}
|
|
19
|
+
const newFilename = `${baseFilename}-${counter}${extension}`;
|
|
20
|
+
filenames.push(newFilename);
|
|
21
|
+
return newFilename;
|
|
22
|
+
} else {
|
|
23
|
+
const baseFilename = proposedFilename.replace(extension, "");
|
|
24
|
+
let counter = 2;
|
|
25
|
+
while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
|
|
26
|
+
counter++;
|
|
27
|
+
}
|
|
28
|
+
const newFilename = `${baseFilename}-${counter}${extension}`;
|
|
29
|
+
filenames.push(newFilename);
|
|
30
|
+
return newFilename;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function changeJpegDpi(uint8Array, dpi) {
|
|
34
|
+
uint8Array[13] = 1;
|
|
35
|
+
uint8Array[14] = dpi >> 8;
|
|
36
|
+
uint8Array[15] = dpi & 255;
|
|
37
|
+
uint8Array[16] = dpi >> 8;
|
|
38
|
+
uint8Array[17] = dpi & 255;
|
|
39
|
+
return uint8Array;
|
|
40
|
+
}
|
|
41
|
+
const _P = "p".charCodeAt(0);
|
|
42
|
+
const _H = "H".charCodeAt(0);
|
|
43
|
+
const _Y = "Y".charCodeAt(0);
|
|
44
|
+
const _S = "s".charCodeAt(0);
|
|
45
|
+
let pngDataTable;
|
|
46
|
+
function createPngDataTable() {
|
|
47
|
+
const crcTable = new Int32Array(256);
|
|
48
|
+
for (let n = 0; n < 256; n++) {
|
|
49
|
+
let c = n;
|
|
50
|
+
for (let k = 0; k < 8; k++) {
|
|
51
|
+
c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
52
|
+
}
|
|
53
|
+
crcTable[n] = c;
|
|
54
|
+
}
|
|
55
|
+
return crcTable;
|
|
56
|
+
}
|
|
57
|
+
function calcCrc(uint8Array) {
|
|
58
|
+
let c = -1;
|
|
59
|
+
if (!pngDataTable)
|
|
60
|
+
pngDataTable = createPngDataTable();
|
|
61
|
+
for (let n = 0; n < uint8Array.length; n++) {
|
|
62
|
+
c = pngDataTable[(c ^ uint8Array[n]) & 255] ^ c >>> 8;
|
|
63
|
+
}
|
|
64
|
+
return c ^ -1;
|
|
65
|
+
}
|
|
66
|
+
function searchStartOfPhys(uint8Array) {
|
|
67
|
+
const length = uint8Array.length - 1;
|
|
68
|
+
for (let i = length; i >= 4; i--) {
|
|
69
|
+
if (uint8Array[i - 4] === 9 && uint8Array[i - 3] === _P && uint8Array[i - 2] === _H && uint8Array[i - 1] === _Y && uint8Array[i] === _S) {
|
|
70
|
+
return i - 3;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
function changePngDpi(uint8Array, dpi, overwritepHYs = false) {
|
|
76
|
+
const physChunk = new Uint8Array(13);
|
|
77
|
+
dpi *= 39.3701;
|
|
78
|
+
physChunk[0] = _P;
|
|
79
|
+
physChunk[1] = _H;
|
|
80
|
+
physChunk[2] = _Y;
|
|
81
|
+
physChunk[3] = _S;
|
|
82
|
+
physChunk[4] = dpi >>> 24;
|
|
83
|
+
physChunk[5] = dpi >>> 16;
|
|
84
|
+
physChunk[6] = dpi >>> 8;
|
|
85
|
+
physChunk[7] = dpi & 255;
|
|
86
|
+
physChunk[8] = physChunk[4];
|
|
87
|
+
physChunk[9] = physChunk[5];
|
|
88
|
+
physChunk[10] = physChunk[6];
|
|
89
|
+
physChunk[11] = physChunk[7];
|
|
90
|
+
physChunk[12] = 1;
|
|
91
|
+
const crc = calcCrc(physChunk);
|
|
92
|
+
const crcChunk = new Uint8Array(4);
|
|
93
|
+
crcChunk[0] = crc >>> 24;
|
|
94
|
+
crcChunk[1] = crc >>> 16;
|
|
95
|
+
crcChunk[2] = crc >>> 8;
|
|
96
|
+
crcChunk[3] = crc & 255;
|
|
97
|
+
if (overwritepHYs) {
|
|
98
|
+
const startingIndex = searchStartOfPhys(uint8Array);
|
|
99
|
+
uint8Array.set(physChunk, startingIndex);
|
|
100
|
+
uint8Array.set(crcChunk, startingIndex + 13);
|
|
101
|
+
return uint8Array;
|
|
102
|
+
} else {
|
|
103
|
+
const chunkLength = new Uint8Array(4);
|
|
104
|
+
chunkLength[0] = 0;
|
|
105
|
+
chunkLength[1] = 0;
|
|
106
|
+
chunkLength[2] = 0;
|
|
107
|
+
chunkLength[3] = 9;
|
|
108
|
+
const finalHeader = new Uint8Array(54);
|
|
109
|
+
finalHeader.set(uint8Array, 0);
|
|
110
|
+
finalHeader.set(chunkLength, 33);
|
|
111
|
+
finalHeader.set(physChunk, 37);
|
|
112
|
+
finalHeader.set(crcChunk, 50);
|
|
113
|
+
return finalHeader;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const b64PhysSignature1 = "AAlwSFlz";
|
|
117
|
+
const b64PhysSignature2 = "AAAJcEhZ";
|
|
118
|
+
const b64PhysSignature3 = "AAAACXBI";
|
|
119
|
+
function detectPhysChunkFromDataUrl(dataUrl) {
|
|
120
|
+
let b64index = dataUrl.indexOf(b64PhysSignature1);
|
|
121
|
+
if (b64index === -1) {
|
|
122
|
+
b64index = dataUrl.indexOf(b64PhysSignature2);
|
|
123
|
+
}
|
|
124
|
+
if (b64index === -1) {
|
|
125
|
+
b64index = dataUrl.indexOf(b64PhysSignature3);
|
|
126
|
+
}
|
|
127
|
+
return b64index;
|
|
128
|
+
}
|
|
129
|
+
const PREFIX = "[modern-screenshot]";
|
|
130
|
+
const IN_BROWSER = typeof window !== "undefined";
|
|
131
|
+
const SUPPORT_WEB_WORKER = IN_BROWSER && "Worker" in window;
|
|
132
|
+
const SUPPORT_ATOB = IN_BROWSER && "atob" in window;
|
|
133
|
+
const SUPPORT_BTOA = IN_BROWSER && "btoa" in window;
|
|
134
|
+
const USER_AGENT = IN_BROWSER ? (_a = window.navigator) == null ? void 0 : _a.userAgent : "";
|
|
135
|
+
const IN_CHROME = USER_AGENT.includes("Chrome");
|
|
136
|
+
const IN_SAFARI = USER_AGENT.includes("AppleWebKit") && !IN_CHROME;
|
|
137
|
+
const IN_FIREFOX = USER_AGENT.includes("Firefox");
|
|
138
|
+
const isContext = (value) => value && "__CONTEXT__" in value;
|
|
139
|
+
const isCssFontFaceRule = (rule) => rule.constructor.name === "CSSFontFaceRule";
|
|
140
|
+
const isCSSImportRule = (rule) => rule.constructor.name === "CSSImportRule";
|
|
141
|
+
const isElementNode = (node) => node.nodeType === 1;
|
|
142
|
+
const isSVGElementNode = (node) => typeof node.className === "object";
|
|
143
|
+
const isSVGImageElementNode = (node) => node.tagName === "image";
|
|
144
|
+
const isSVGUseElementNode = (node) => node.tagName === "use";
|
|
145
|
+
const isHTMLElementNode = (node) => isElementNode(node) && typeof node.style !== "undefined" && !isSVGElementNode(node);
|
|
146
|
+
const isCommentNode = (node) => node.nodeType === 8;
|
|
147
|
+
const isTextNode = (node) => node.nodeType === 3;
|
|
148
|
+
const isImageElement = (node) => node.tagName === "IMG";
|
|
149
|
+
const isVideoElement = (node) => node.tagName === "VIDEO";
|
|
150
|
+
const isCanvasElement = (node) => node.tagName === "CANVAS";
|
|
151
|
+
const isTextareaElement = (node) => node.tagName === "TEXTAREA";
|
|
152
|
+
const isInputElement = (node) => node.tagName === "INPUT";
|
|
153
|
+
const isStyleElement = (node) => node.tagName === "STYLE";
|
|
154
|
+
const isScriptElement = (node) => node.tagName === "SCRIPT";
|
|
155
|
+
const isSelectElement = (node) => node.tagName === "SELECT";
|
|
156
|
+
const isSlotElement = (node) => node.tagName === "SLOT";
|
|
157
|
+
const isIFrameElement = (node) => node.tagName === "IFRAME";
|
|
158
|
+
const consoleWarn = (...args) => console.warn(PREFIX, ...args);
|
|
159
|
+
function supportWebp(ownerDocument) {
|
|
160
|
+
var _a2;
|
|
161
|
+
const canvas = (_a2 = ownerDocument == null ? void 0 : ownerDocument.createElement) == null ? void 0 : _a2.call(ownerDocument, "canvas");
|
|
162
|
+
if (canvas) {
|
|
163
|
+
canvas.height = canvas.width = 1;
|
|
164
|
+
}
|
|
165
|
+
return Boolean(canvas) && "toDataURL" in canvas && Boolean(canvas.toDataURL("image/webp").includes("image/webp"));
|
|
166
|
+
}
|
|
167
|
+
const isDataUrl = (url) => url.startsWith("data:");
|
|
1
168
|
function resolveUrl(url, baseUrl) {
|
|
2
|
-
if (url.match(/^[a-z]+:\/\//i))
|
|
169
|
+
if (url.match(/^[a-z]+:\/\//i))
|
|
3
170
|
return url;
|
|
4
|
-
|
|
5
|
-
if (url.match(/^\/\//)) {
|
|
171
|
+
if (IN_BROWSER && url.match(/^\/\//))
|
|
6
172
|
return window.location.protocol + url;
|
|
7
|
-
|
|
8
|
-
if (url.match(/^[a-z]+:/i)) {
|
|
173
|
+
if (url.match(/^[a-z]+:/i))
|
|
9
174
|
return url;
|
|
10
|
-
|
|
11
|
-
|
|
175
|
+
if (!IN_BROWSER)
|
|
176
|
+
return url;
|
|
177
|
+
const doc = getDocument().implementation.createHTMLDocument();
|
|
12
178
|
const base = doc.createElement("base");
|
|
13
179
|
const a = doc.createElement("a");
|
|
14
180
|
doc.head.appendChild(base);
|
|
15
181
|
doc.body.appendChild(a);
|
|
16
|
-
if (baseUrl)
|
|
182
|
+
if (baseUrl)
|
|
17
183
|
base.href = baseUrl;
|
|
18
|
-
}
|
|
19
184
|
a.href = url;
|
|
20
185
|
return a.href;
|
|
21
186
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const random = () => (
|
|
25
|
-
// eslint-disable-next-line no-bitwise
|
|
26
|
-
`0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4)
|
|
27
|
-
);
|
|
28
|
-
return () => {
|
|
29
|
-
counter += 1;
|
|
30
|
-
return `u${random()}${counter}`;
|
|
31
|
-
};
|
|
32
|
-
})();
|
|
33
|
-
function toArray(arrayLike) {
|
|
34
|
-
const arr = [];
|
|
35
|
-
for (let i = 0, l = arrayLike.length; i < l; i++) {
|
|
36
|
-
arr.push(arrayLike[i]);
|
|
37
|
-
}
|
|
38
|
-
return arr;
|
|
39
|
-
}
|
|
40
|
-
let styleProps = null;
|
|
41
|
-
function getStyleProperties(options = {}) {
|
|
42
|
-
if (styleProps) {
|
|
43
|
-
return styleProps;
|
|
44
|
-
}
|
|
45
|
-
if (options.includeStyleProperties) {
|
|
46
|
-
styleProps = options.includeStyleProperties;
|
|
47
|
-
return styleProps;
|
|
48
|
-
}
|
|
49
|
-
styleProps = toArray(window.getComputedStyle(document.documentElement));
|
|
50
|
-
return styleProps;
|
|
51
|
-
}
|
|
52
|
-
function px(node, styleProperty) {
|
|
53
|
-
const win = node.ownerDocument.defaultView || window;
|
|
54
|
-
const val = win.getComputedStyle(node).getPropertyValue(styleProperty);
|
|
55
|
-
return val ? parseFloat(val.replace("px", "")) : 0;
|
|
56
|
-
}
|
|
57
|
-
function getNodeWidth(node) {
|
|
58
|
-
const leftBorder = px(node, "border-left-width");
|
|
59
|
-
const rightBorder = px(node, "border-right-width");
|
|
60
|
-
return node.clientWidth + leftBorder + rightBorder;
|
|
61
|
-
}
|
|
62
|
-
function getNodeHeight(node) {
|
|
63
|
-
const topBorder = px(node, "border-top-width");
|
|
64
|
-
const bottomBorder = px(node, "border-bottom-width");
|
|
65
|
-
return node.clientHeight + topBorder + bottomBorder;
|
|
66
|
-
}
|
|
67
|
-
function getImageSize(targetNode, options = {}) {
|
|
68
|
-
const width = options.width || getNodeWidth(targetNode);
|
|
69
|
-
const height = options.height || getNodeHeight(targetNode);
|
|
70
|
-
return { width, height };
|
|
187
|
+
function getDocument(target) {
|
|
188
|
+
return (target && isElementNode(target) ? target == null ? void 0 : target.ownerDocument : target) ?? window.document;
|
|
71
189
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const val = FINAL_PROCESS && FINAL_PROCESS.env ? FINAL_PROCESS.env.devicePixelRatio : null;
|
|
80
|
-
if (val) {
|
|
81
|
-
ratio = parseInt(val, 10);
|
|
82
|
-
if (Number.isNaN(ratio)) {
|
|
83
|
-
ratio = 1;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return ratio || window.devicePixelRatio || 1;
|
|
190
|
+
const XMLNS = "http://www.w3.org/2000/svg";
|
|
191
|
+
function createSvg(width, height, ownerDocument) {
|
|
192
|
+
const svg = getDocument(ownerDocument).createElementNS(XMLNS, "svg");
|
|
193
|
+
svg.setAttributeNS(null, "width", width.toString());
|
|
194
|
+
svg.setAttributeNS(null, "height", height.toString());
|
|
195
|
+
svg.setAttributeNS(null, "viewBox", `0 0 ${width} ${height}`);
|
|
196
|
+
return svg;
|
|
87
197
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
if (canvas.width > canvas.height) {
|
|
93
|
-
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
94
|
-
canvas.width = canvasDimensionLimit;
|
|
95
|
-
} else {
|
|
96
|
-
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
97
|
-
canvas.height = canvasDimensionLimit;
|
|
98
|
-
}
|
|
99
|
-
} else if (canvas.width > canvasDimensionLimit) {
|
|
100
|
-
canvas.height *= canvasDimensionLimit / canvas.width;
|
|
101
|
-
canvas.width = canvasDimensionLimit;
|
|
102
|
-
} else {
|
|
103
|
-
canvas.width *= canvasDimensionLimit / canvas.height;
|
|
104
|
-
canvas.height = canvasDimensionLimit;
|
|
105
|
-
}
|
|
198
|
+
function svgToDataUrl(svg, removeControlCharacter) {
|
|
199
|
+
let xhtml = new XMLSerializer().serializeToString(svg);
|
|
200
|
+
if (removeControlCharacter) {
|
|
201
|
+
xhtml = xhtml.replace(/[\u0000-\u0008\v\f\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/gu, "");
|
|
106
202
|
}
|
|
203
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(xhtml)}`;
|
|
107
204
|
}
|
|
108
|
-
function
|
|
205
|
+
function readBlob(blob, type) {
|
|
109
206
|
return new Promise((resolve, reject) => {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
img.crossOrigin = "anonymous";
|
|
118
|
-
img.decoding = "async";
|
|
119
|
-
img.src = url;
|
|
207
|
+
const reader = new FileReader();
|
|
208
|
+
reader.onload = () => resolve(reader.result);
|
|
209
|
+
reader.onerror = () => reject(reader.error);
|
|
210
|
+
reader.onabort = () => reject(new Error(`Failed read blob to ${type}`));
|
|
211
|
+
{
|
|
212
|
+
reader.readAsDataURL(blob);
|
|
213
|
+
}
|
|
120
214
|
});
|
|
121
215
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
svg.setAttribute("width", `${width}`);
|
|
130
|
-
svg.setAttribute("height", `${height}`);
|
|
131
|
-
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
132
|
-
foreignObject.setAttribute("width", "100%");
|
|
133
|
-
foreignObject.setAttribute("height", "100%");
|
|
134
|
-
foreignObject.setAttribute("x", "0");
|
|
135
|
-
foreignObject.setAttribute("y", "0");
|
|
136
|
-
foreignObject.setAttribute("externalResourcesRequired", "true");
|
|
137
|
-
svg.appendChild(foreignObject);
|
|
138
|
-
foreignObject.appendChild(node);
|
|
139
|
-
return svgToDataURL(svg);
|
|
216
|
+
const blobToDataUrl = (blob) => readBlob(blob, "dataUrl");
|
|
217
|
+
function createImage(url, ownerDocument) {
|
|
218
|
+
const img = getDocument(ownerDocument).createElement("img");
|
|
219
|
+
img.decoding = "sync";
|
|
220
|
+
img.loading = "eager";
|
|
221
|
+
img.src = url;
|
|
222
|
+
return img;
|
|
140
223
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
224
|
+
function loadMedia(media, options) {
|
|
225
|
+
return new Promise((resolve) => {
|
|
226
|
+
const { timeout, ownerDocument, onError: userOnError, onWarn } = options ?? {};
|
|
227
|
+
const node = typeof media === "string" ? createImage(media, getDocument(ownerDocument)) : media;
|
|
228
|
+
let timer = null;
|
|
229
|
+
let removeEventListeners = null;
|
|
230
|
+
function onResolve() {
|
|
231
|
+
resolve(node);
|
|
232
|
+
timer && clearTimeout(timer);
|
|
233
|
+
removeEventListeners == null ? void 0 : removeEventListeners();
|
|
234
|
+
}
|
|
235
|
+
if (timeout) {
|
|
236
|
+
timer = setTimeout(onResolve, timeout);
|
|
237
|
+
}
|
|
238
|
+
if (isVideoElement(node)) {
|
|
239
|
+
const currentSrc = node.currentSrc || node.src;
|
|
240
|
+
if (!currentSrc) {
|
|
241
|
+
if (node.poster) {
|
|
242
|
+
return loadMedia(node.poster, options).then(resolve);
|
|
243
|
+
}
|
|
244
|
+
return onResolve();
|
|
245
|
+
}
|
|
246
|
+
if (node.readyState >= 2) {
|
|
247
|
+
return onResolve();
|
|
248
|
+
}
|
|
249
|
+
const onLoadeddata = onResolve;
|
|
250
|
+
const onError = (error) => {
|
|
251
|
+
onWarn == null ? void 0 : onWarn(
|
|
252
|
+
"Failed video load",
|
|
253
|
+
currentSrc,
|
|
254
|
+
error
|
|
255
|
+
);
|
|
256
|
+
userOnError == null ? void 0 : userOnError(error);
|
|
257
|
+
onResolve();
|
|
258
|
+
};
|
|
259
|
+
removeEventListeners = () => {
|
|
260
|
+
node.removeEventListener("loadeddata", onLoadeddata);
|
|
261
|
+
node.removeEventListener("error", onError);
|
|
262
|
+
};
|
|
263
|
+
node.addEventListener("loadeddata", onLoadeddata, { once: true });
|
|
264
|
+
node.addEventListener("error", onError, { once: true });
|
|
265
|
+
} else {
|
|
266
|
+
const currentSrc = isSVGImageElementNode(node) ? node.href.baseVal : node.currentSrc || node.src;
|
|
267
|
+
if (!currentSrc) {
|
|
268
|
+
return onResolve();
|
|
269
|
+
}
|
|
270
|
+
const onLoad = async () => {
|
|
271
|
+
if (isImageElement(node) && "decode" in node) {
|
|
272
|
+
try {
|
|
273
|
+
await node.decode();
|
|
274
|
+
} catch (error) {
|
|
275
|
+
onWarn == null ? void 0 : onWarn(
|
|
276
|
+
"Failed to decode image, trying to render anyway",
|
|
277
|
+
node.dataset.originalSrc || currentSrc,
|
|
278
|
+
error
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
onResolve();
|
|
283
|
+
};
|
|
284
|
+
const onError = (error) => {
|
|
285
|
+
onWarn == null ? void 0 : onWarn(
|
|
286
|
+
"Failed image load",
|
|
287
|
+
node.dataset.originalSrc || currentSrc,
|
|
288
|
+
error
|
|
289
|
+
);
|
|
290
|
+
onResolve();
|
|
291
|
+
};
|
|
292
|
+
if (isImageElement(node) && node.complete) {
|
|
293
|
+
return onLoad();
|
|
294
|
+
}
|
|
295
|
+
removeEventListeners = () => {
|
|
296
|
+
node.removeEventListener("load", onLoad);
|
|
297
|
+
node.removeEventListener("error", onError);
|
|
298
|
+
};
|
|
299
|
+
node.addEventListener("load", onLoad, { once: true });
|
|
300
|
+
node.addEventListener("error", onError, { once: true });
|
|
301
|
+
}
|
|
302
|
+
});
|
|
152
303
|
}
|
|
153
|
-
function
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
function clonePseudoElement(nativeNode, clonedNode, pseudo, options) {
|
|
166
|
-
const style = window.getComputedStyle(nativeNode, pseudo);
|
|
167
|
-
const content = style.getPropertyValue("content");
|
|
168
|
-
if (content === "" || content === "none") {
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const className = uuid();
|
|
172
|
-
try {
|
|
173
|
-
clonedNode.className = `${clonedNode.className} ${className}`;
|
|
174
|
-
} catch (err) {
|
|
175
|
-
return;
|
|
304
|
+
async function waitUntilLoad(node, options) {
|
|
305
|
+
if (isHTMLElementNode(node)) {
|
|
306
|
+
if (isImageElement(node) || isVideoElement(node)) {
|
|
307
|
+
await loadMedia(node, options);
|
|
308
|
+
} else {
|
|
309
|
+
await Promise.all(
|
|
310
|
+
["img", "video"].flatMap((selectors) => {
|
|
311
|
+
return Array.from(node.querySelectorAll(selectors)).map((el) => loadMedia(el, options));
|
|
312
|
+
})
|
|
313
|
+
);
|
|
314
|
+
}
|
|
176
315
|
}
|
|
177
|
-
const styleElement = document.createElement("style");
|
|
178
|
-
styleElement.appendChild(getPseudoElementStyle(className, pseudo, style, options));
|
|
179
|
-
clonedNode.appendChild(styleElement);
|
|
180
|
-
}
|
|
181
|
-
function clonePseudoElements(nativeNode, clonedNode, options) {
|
|
182
|
-
clonePseudoElement(nativeNode, clonedNode, ":before", options);
|
|
183
|
-
clonePseudoElement(nativeNode, clonedNode, ":after", options);
|
|
184
|
-
}
|
|
185
|
-
const WOFF = "application/font-woff";
|
|
186
|
-
const JPEG = "image/jpeg";
|
|
187
|
-
const mimes = {
|
|
188
|
-
woff: WOFF,
|
|
189
|
-
woff2: WOFF,
|
|
190
|
-
ttf: "application/font-truetype",
|
|
191
|
-
eot: "application/vnd.ms-fontobject",
|
|
192
|
-
png: "image/png",
|
|
193
|
-
jpg: JPEG,
|
|
194
|
-
jpeg: JPEG,
|
|
195
|
-
gif: "image/gif",
|
|
196
|
-
tiff: "image/tiff",
|
|
197
|
-
svg: "image/svg+xml",
|
|
198
|
-
webp: "image/webp"
|
|
199
|
-
};
|
|
200
|
-
function getExtension(url) {
|
|
201
|
-
const match = /\.([^./]*?)$/g.exec(url);
|
|
202
|
-
return match ? match[1] : "";
|
|
203
316
|
}
|
|
204
|
-
function
|
|
205
|
-
|
|
206
|
-
|
|
317
|
+
const uuid = /* @__PURE__ */ function uuid2() {
|
|
318
|
+
let counter = 0;
|
|
319
|
+
const random = () => `0000${(Math.random() * 36 ** 4 << 0).toString(36)}`.slice(-4);
|
|
320
|
+
return () => {
|
|
321
|
+
counter += 1;
|
|
322
|
+
return `u${random()}${counter}`;
|
|
323
|
+
};
|
|
324
|
+
}();
|
|
325
|
+
function splitFontFamily(fontFamily) {
|
|
326
|
+
return fontFamily == null ? void 0 : fontFamily.split(",").map((val) => val.trim().replace(/"|'/g, "").toLowerCase()).filter(Boolean);
|
|
207
327
|
}
|
|
208
|
-
|
|
209
|
-
|
|
328
|
+
let uid = 0;
|
|
329
|
+
function createLogger(debug) {
|
|
330
|
+
const prefix = `${PREFIX}[#${uid}]`;
|
|
331
|
+
uid++;
|
|
332
|
+
return {
|
|
333
|
+
// eslint-disable-next-line no-console
|
|
334
|
+
time: (label) => debug && console.time(`${prefix} ${label}`),
|
|
335
|
+
// eslint-disable-next-line no-console
|
|
336
|
+
timeEnd: (label) => debug && console.timeEnd(`${prefix} ${label}`),
|
|
337
|
+
warn: (...args) => debug && consoleWarn(...args)
|
|
338
|
+
};
|
|
210
339
|
}
|
|
211
|
-
function
|
|
212
|
-
return
|
|
340
|
+
function getDefaultRequestInit(bypassingCache) {
|
|
341
|
+
return {
|
|
342
|
+
cache: bypassingCache ? "no-cache" : "force-cache"
|
|
343
|
+
};
|
|
213
344
|
}
|
|
214
|
-
function
|
|
215
|
-
return
|
|
345
|
+
async function orCreateContext(node, options) {
|
|
346
|
+
return isContext(node) ? node : createContext(node, { ...options, autoDestruct: true });
|
|
216
347
|
}
|
|
217
|
-
async function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
348
|
+
async function createContext(node, options) {
|
|
349
|
+
var _a2, _b;
|
|
350
|
+
const { scale = 1, workerUrl, workerNumber = 1 } = options || {};
|
|
351
|
+
const debug = Boolean(options == null ? void 0 : options.debug);
|
|
352
|
+
const features = (options == null ? void 0 : options.features) ?? true;
|
|
353
|
+
const ownerDocument = node.ownerDocument ?? (IN_BROWSER ? window.document : void 0);
|
|
354
|
+
const ownerWindow = ((_a2 = node.ownerDocument) == null ? void 0 : _a2.defaultView) ?? (IN_BROWSER ? window : void 0);
|
|
355
|
+
const requests = /* @__PURE__ */ new Map();
|
|
356
|
+
const context = {
|
|
357
|
+
// Options
|
|
358
|
+
width: 0,
|
|
359
|
+
height: 0,
|
|
360
|
+
quality: 1,
|
|
361
|
+
type: "image/png",
|
|
362
|
+
scale,
|
|
363
|
+
backgroundColor: null,
|
|
364
|
+
style: null,
|
|
365
|
+
filter: null,
|
|
366
|
+
maximumCanvasSize: 0,
|
|
367
|
+
timeout: 3e4,
|
|
368
|
+
progress: null,
|
|
369
|
+
debug,
|
|
370
|
+
fetch: {
|
|
371
|
+
requestInit: getDefaultRequestInit((_b = options == null ? void 0 : options.fetch) == null ? void 0 : _b.bypassingCache),
|
|
372
|
+
placeholderImage: "data:image/png;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
|
|
373
|
+
bypassingCache: false,
|
|
374
|
+
...options == null ? void 0 : options.fetch
|
|
375
|
+
},
|
|
376
|
+
fetchFn: null,
|
|
377
|
+
font: {},
|
|
378
|
+
drawImageInterval: 100,
|
|
379
|
+
workerUrl: null,
|
|
380
|
+
workerNumber,
|
|
381
|
+
onCloneNode: null,
|
|
382
|
+
onEmbedNode: null,
|
|
383
|
+
onCreateForeignObjectSvg: null,
|
|
384
|
+
includeStyleProperties: null,
|
|
385
|
+
autoDestruct: false,
|
|
386
|
+
...options,
|
|
387
|
+
// InternalContext
|
|
388
|
+
__CONTEXT__: true,
|
|
389
|
+
log: createLogger(debug),
|
|
390
|
+
node,
|
|
391
|
+
ownerDocument,
|
|
392
|
+
ownerWindow,
|
|
393
|
+
dpi: scale === 1 ? null : 96 * scale,
|
|
394
|
+
svgStyleElement: createStyleElement(ownerDocument),
|
|
395
|
+
svgDefsElement: ownerDocument == null ? void 0 : ownerDocument.createElementNS(XMLNS, "defs"),
|
|
396
|
+
svgStyles: /* @__PURE__ */ new Map(),
|
|
397
|
+
defaultComputedStyles: /* @__PURE__ */ new Map(),
|
|
398
|
+
workers: [
|
|
399
|
+
...Array.from({
|
|
400
|
+
length: SUPPORT_WEB_WORKER && workerUrl && workerNumber ? workerNumber : 0
|
|
401
|
+
})
|
|
402
|
+
].map(() => {
|
|
227
403
|
try {
|
|
228
|
-
|
|
404
|
+
const worker = new Worker(workerUrl);
|
|
405
|
+
worker.onmessage = async (event) => {
|
|
406
|
+
var _a3, _b2, _c, _d;
|
|
407
|
+
const { url, result } = event.data;
|
|
408
|
+
if (result) {
|
|
409
|
+
(_b2 = (_a3 = requests.get(url)) == null ? void 0 : _a3.resolve) == null ? void 0 : _b2.call(_a3, result);
|
|
410
|
+
} else {
|
|
411
|
+
(_d = (_c = requests.get(url)) == null ? void 0 : _c.reject) == null ? void 0 : _d.call(_c, new Error(`Error receiving message from worker: ${url}`));
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
worker.onmessageerror = (event) => {
|
|
415
|
+
var _a3, _b2;
|
|
416
|
+
const { url } = event.data;
|
|
417
|
+
(_b2 = (_a3 = requests.get(url)) == null ? void 0 : _a3.reject) == null ? void 0 : _b2.call(_a3, new Error(`Error receiving message from worker: ${url}`));
|
|
418
|
+
};
|
|
419
|
+
return worker;
|
|
229
420
|
} catch (error) {
|
|
230
|
-
|
|
421
|
+
context.log.warn("Failed to new Worker", error);
|
|
422
|
+
return null;
|
|
231
423
|
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
424
|
+
}).filter(Boolean),
|
|
425
|
+
fontFamilies: /* @__PURE__ */ new Map(),
|
|
426
|
+
fontCssTexts: /* @__PURE__ */ new Map(),
|
|
427
|
+
acceptOfImage: `${[
|
|
428
|
+
supportWebp(ownerDocument) && "image/webp",
|
|
429
|
+
"image/svg+xml",
|
|
430
|
+
"image/*",
|
|
431
|
+
"*/*"
|
|
432
|
+
].filter(Boolean).join(",")};q=0.8`,
|
|
433
|
+
requests,
|
|
434
|
+
drawImageCount: 0,
|
|
435
|
+
tasks: [],
|
|
436
|
+
features,
|
|
437
|
+
isEnable: (key) => {
|
|
438
|
+
if (key === "restoreScrollPosition") {
|
|
439
|
+
return typeof features === "boolean" ? false : features[key] ?? false;
|
|
440
|
+
}
|
|
441
|
+
if (typeof features === "boolean") {
|
|
442
|
+
return features;
|
|
443
|
+
}
|
|
444
|
+
return features[key] ?? true;
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
context.log.time("wait until load");
|
|
448
|
+
await waitUntilLoad(node, { timeout: context.timeout, onWarn: context.log.warn });
|
|
449
|
+
context.log.timeEnd("wait until load");
|
|
450
|
+
const { width, height } = resolveBoundingBox(node, context);
|
|
451
|
+
context.width = width;
|
|
452
|
+
context.height = height;
|
|
453
|
+
return context;
|
|
235
454
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
455
|
+
function createStyleElement(ownerDocument) {
|
|
456
|
+
if (!ownerDocument)
|
|
457
|
+
return void 0;
|
|
458
|
+
const style = ownerDocument.createElement("style");
|
|
459
|
+
const cssText = style.ownerDocument.createTextNode(`
|
|
460
|
+
.______background-clip--text {
|
|
461
|
+
background-clip: text;
|
|
462
|
+
-webkit-background-clip: text;
|
|
463
|
+
}
|
|
464
|
+
`);
|
|
465
|
+
style.appendChild(cssText);
|
|
466
|
+
return style;
|
|
467
|
+
}
|
|
468
|
+
function resolveBoundingBox(node, context) {
|
|
469
|
+
let { width, height } = context;
|
|
470
|
+
if (isElementNode(node) && (!width || !height)) {
|
|
471
|
+
const box = node.getBoundingClientRect();
|
|
472
|
+
width = width || box.width || Number(node.getAttribute("width")) || 0;
|
|
473
|
+
height = height || box.height || Number(node.getAttribute("height")) || 0;
|
|
241
474
|
}
|
|
242
|
-
|
|
243
|
-
|
|
475
|
+
return { width, height };
|
|
476
|
+
}
|
|
477
|
+
async function imageToCanvas(image, context) {
|
|
478
|
+
const {
|
|
479
|
+
log: log2,
|
|
480
|
+
timeout,
|
|
481
|
+
drawImageCount,
|
|
482
|
+
drawImageInterval
|
|
483
|
+
} = context;
|
|
484
|
+
log2.time("image to canvas");
|
|
485
|
+
const loaded = await loadMedia(image, { timeout, onWarn: context.log.warn });
|
|
486
|
+
const { canvas, context2d } = createCanvas(image.ownerDocument, context);
|
|
487
|
+
const drawImage = () => {
|
|
488
|
+
try {
|
|
489
|
+
context2d == null ? void 0 : context2d.drawImage(loaded, 0, 0, canvas.width, canvas.height);
|
|
490
|
+
} catch (error) {
|
|
491
|
+
context.log.warn("Failed to drawImage", error);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
drawImage();
|
|
495
|
+
if (context.isEnable("fixSvgXmlDecode")) {
|
|
496
|
+
for (let i = 0; i < drawImageCount; i++) {
|
|
497
|
+
await new Promise((resolve) => {
|
|
498
|
+
setTimeout(() => {
|
|
499
|
+
drawImage();
|
|
500
|
+
resolve();
|
|
501
|
+
}, i + drawImageInterval);
|
|
502
|
+
});
|
|
503
|
+
}
|
|
244
504
|
}
|
|
245
|
-
|
|
505
|
+
context.drawImageCount = 0;
|
|
506
|
+
log2.timeEnd("image to canvas");
|
|
507
|
+
return canvas;
|
|
246
508
|
}
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
509
|
+
function createCanvas(ownerDocument, context) {
|
|
510
|
+
const { width, height, scale, backgroundColor, maximumCanvasSize: max } = context;
|
|
511
|
+
const canvas = ownerDocument.createElement("canvas");
|
|
512
|
+
canvas.width = Math.floor(width * scale);
|
|
513
|
+
canvas.height = Math.floor(height * scale);
|
|
514
|
+
canvas.style.width = `${width}px`;
|
|
515
|
+
canvas.style.height = `${height}px`;
|
|
516
|
+
if (max) {
|
|
517
|
+
if (canvas.width > max || canvas.height > max) {
|
|
518
|
+
if (canvas.width > max && canvas.height > max) {
|
|
519
|
+
if (canvas.width > canvas.height) {
|
|
520
|
+
canvas.height *= max / canvas.width;
|
|
521
|
+
canvas.width = max;
|
|
522
|
+
} else {
|
|
523
|
+
canvas.width *= max / canvas.height;
|
|
524
|
+
canvas.height = max;
|
|
525
|
+
}
|
|
526
|
+
} else if (canvas.width > max) {
|
|
527
|
+
canvas.height *= max / canvas.width;
|
|
528
|
+
canvas.width = max;
|
|
529
|
+
} else {
|
|
530
|
+
canvas.width *= max / canvas.height;
|
|
531
|
+
canvas.height = max;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
251
534
|
}
|
|
252
|
-
|
|
253
|
-
|
|
535
|
+
const context2d = canvas.getContext("2d");
|
|
536
|
+
if (context2d && backgroundColor) {
|
|
537
|
+
context2d.fillStyle = backgroundColor;
|
|
538
|
+
context2d.fillRect(0, 0, canvas.width, canvas.height);
|
|
254
539
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
540
|
+
return { canvas, context2d };
|
|
541
|
+
}
|
|
542
|
+
function cloneCanvas(canvas, context) {
|
|
543
|
+
if (canvas.ownerDocument) {
|
|
544
|
+
try {
|
|
545
|
+
const dataURL = canvas.toDataURL();
|
|
546
|
+
if (dataURL !== "data:,") {
|
|
547
|
+
return createImage(dataURL, canvas.ownerDocument);
|
|
260
548
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
549
|
+
} catch (error) {
|
|
550
|
+
context.log.warn("Failed to clone canvas", error);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
const cloned = canvas.cloneNode(false);
|
|
554
|
+
const ctx = canvas.getContext("2d");
|
|
555
|
+
const clonedCtx = cloned.getContext("2d");
|
|
556
|
+
try {
|
|
557
|
+
if (ctx && clonedCtx) {
|
|
558
|
+
clonedCtx.putImageData(
|
|
559
|
+
ctx.getImageData(0, 0, canvas.width, canvas.height),
|
|
560
|
+
0,
|
|
561
|
+
0
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
return cloned;
|
|
264
565
|
} catch (error) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
console.warn(msg);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
cache[cacheKey] = dataURL;
|
|
275
|
-
return dataURL;
|
|
276
|
-
}
|
|
277
|
-
async function cloneCanvasElement(canvas) {
|
|
278
|
-
const dataURL = canvas.toDataURL();
|
|
279
|
-
if (dataURL === "data:,") {
|
|
280
|
-
return canvas.cloneNode(false);
|
|
281
|
-
}
|
|
282
|
-
return createImage(dataURL);
|
|
283
|
-
}
|
|
284
|
-
async function cloneVideoElement(video, options) {
|
|
285
|
-
if (video.currentSrc) {
|
|
286
|
-
const canvas = document.createElement("canvas");
|
|
287
|
-
const ctx = canvas.getContext("2d");
|
|
288
|
-
canvas.width = video.clientWidth;
|
|
289
|
-
canvas.height = video.clientHeight;
|
|
290
|
-
ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
291
|
-
const dataURL2 = canvas.toDataURL();
|
|
292
|
-
return createImage(dataURL2);
|
|
293
|
-
}
|
|
294
|
-
const poster = video.poster;
|
|
295
|
-
const contentType = getMimeType(poster);
|
|
296
|
-
const dataURL = await resourceToDataURL(poster, contentType, options);
|
|
297
|
-
return createImage(dataURL);
|
|
298
|
-
}
|
|
299
|
-
async function cloneIFrameElement(iframe, options) {
|
|
300
|
-
var _a;
|
|
566
|
+
context.log.warn("Failed to clone canvas", error);
|
|
567
|
+
}
|
|
568
|
+
return cloned;
|
|
569
|
+
}
|
|
570
|
+
function cloneIframe(iframe, context) {
|
|
571
|
+
var _a2;
|
|
301
572
|
try {
|
|
302
|
-
if ((
|
|
303
|
-
return
|
|
573
|
+
if ((_a2 = iframe == null ? void 0 : iframe.contentDocument) == null ? void 0 : _a2.body) {
|
|
574
|
+
return cloneNode(iframe.contentDocument.body, context);
|
|
304
575
|
}
|
|
305
|
-
} catch (
|
|
576
|
+
} catch (error) {
|
|
577
|
+
context.log.warn("Failed to clone iframe", error);
|
|
306
578
|
}
|
|
307
579
|
return iframe.cloneNode(false);
|
|
308
580
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
581
|
+
function cloneImage(image) {
|
|
582
|
+
const cloned = image.cloneNode(false);
|
|
583
|
+
if (image.currentSrc && image.currentSrc !== image.src) {
|
|
584
|
+
cloned.src = image.currentSrc;
|
|
585
|
+
cloned.srcset = "";
|
|
586
|
+
}
|
|
587
|
+
if (cloned.loading === "lazy") {
|
|
588
|
+
cloned.loading = "eager";
|
|
589
|
+
}
|
|
590
|
+
return cloned;
|
|
591
|
+
}
|
|
592
|
+
async function cloneVideo(video, context) {
|
|
593
|
+
if (video.ownerDocument && !video.currentSrc && video.poster) {
|
|
594
|
+
return createImage(video.poster, video.ownerDocument);
|
|
312
595
|
}
|
|
313
|
-
|
|
314
|
-
|
|
596
|
+
const cloned = video.cloneNode(false);
|
|
597
|
+
cloned.crossOrigin = "anonymous";
|
|
598
|
+
if (video.currentSrc && video.currentSrc !== video.src) {
|
|
599
|
+
cloned.src = video.currentSrc;
|
|
315
600
|
}
|
|
316
|
-
|
|
317
|
-
|
|
601
|
+
const ownerDocument = cloned.ownerDocument;
|
|
602
|
+
if (ownerDocument) {
|
|
603
|
+
let canPlay = true;
|
|
604
|
+
await loadMedia(cloned, { onError: () => canPlay = false, onWarn: context.log.warn });
|
|
605
|
+
if (!canPlay) {
|
|
606
|
+
if (video.poster) {
|
|
607
|
+
return createImage(video.poster, video.ownerDocument);
|
|
608
|
+
}
|
|
609
|
+
return cloned;
|
|
610
|
+
}
|
|
611
|
+
cloned.currentTime = video.currentTime;
|
|
612
|
+
await new Promise((resolve) => {
|
|
613
|
+
cloned.addEventListener("seeked", resolve, { once: true });
|
|
614
|
+
});
|
|
615
|
+
const canvas = ownerDocument.createElement("canvas");
|
|
616
|
+
canvas.width = video.offsetWidth;
|
|
617
|
+
canvas.height = video.offsetHeight;
|
|
618
|
+
try {
|
|
619
|
+
const ctx = canvas.getContext("2d");
|
|
620
|
+
if (ctx)
|
|
621
|
+
ctx.drawImage(cloned, 0, 0, canvas.width, canvas.height);
|
|
622
|
+
} catch (error) {
|
|
623
|
+
context.log.warn("Failed to clone video", error);
|
|
624
|
+
if (video.poster) {
|
|
625
|
+
return createImage(video.poster, video.ownerDocument);
|
|
626
|
+
}
|
|
627
|
+
return cloned;
|
|
628
|
+
}
|
|
629
|
+
return cloneCanvas(canvas, context);
|
|
318
630
|
}
|
|
319
|
-
return
|
|
631
|
+
return cloned;
|
|
320
632
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
var _a, _b;
|
|
325
|
-
if (isSVGElement(clonedNode)) {
|
|
326
|
-
return clonedNode;
|
|
633
|
+
function cloneElement(node, context) {
|
|
634
|
+
if (isCanvasElement(node)) {
|
|
635
|
+
return cloneCanvas(node, context);
|
|
327
636
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
} else {
|
|
334
|
-
children = toArray(((_b = nativeNode.shadowRoot) !== null && _b !== void 0 ? _b : nativeNode).childNodes);
|
|
637
|
+
if (isIFrameElement(node)) {
|
|
638
|
+
return cloneIframe(node, context);
|
|
639
|
+
}
|
|
640
|
+
if (isImageElement(node)) {
|
|
641
|
+
return cloneImage(node);
|
|
335
642
|
}
|
|
336
|
-
if (
|
|
337
|
-
return
|
|
643
|
+
if (isVideoElement(node)) {
|
|
644
|
+
return cloneVideo(node, context);
|
|
338
645
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
646
|
+
return node.cloneNode(false);
|
|
647
|
+
}
|
|
648
|
+
function getSandBox(context) {
|
|
649
|
+
var _a2;
|
|
650
|
+
let sandbox = context.sandbox;
|
|
651
|
+
if (!sandbox) {
|
|
652
|
+
const { ownerDocument } = context;
|
|
653
|
+
try {
|
|
654
|
+
if (ownerDocument) {
|
|
655
|
+
sandbox = ownerDocument.createElement("iframe");
|
|
656
|
+
sandbox.id = `__SANDBOX__-${uuid()}`;
|
|
657
|
+
sandbox.width = "0";
|
|
658
|
+
sandbox.height = "0";
|
|
659
|
+
sandbox.style.visibility = "hidden";
|
|
660
|
+
sandbox.style.position = "fixed";
|
|
661
|
+
ownerDocument.body.appendChild(sandbox);
|
|
662
|
+
(_a2 = sandbox.contentWindow) == null ? void 0 : _a2.document.write('<!DOCTYPE html><meta charset="UTF-8"><title></title><body>');
|
|
663
|
+
context.sandbox = sandbox;
|
|
664
|
+
}
|
|
665
|
+
} catch (error) {
|
|
666
|
+
context.log.warn("Failed to getSandBox", error);
|
|
342
667
|
}
|
|
343
|
-
}
|
|
344
|
-
return
|
|
668
|
+
}
|
|
669
|
+
return sandbox;
|
|
345
670
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
671
|
+
const ignoredStyles = [
|
|
672
|
+
"width",
|
|
673
|
+
"height",
|
|
674
|
+
"-webkit-text-fill-color"
|
|
675
|
+
];
|
|
676
|
+
const includedAttributes = [
|
|
677
|
+
"stroke",
|
|
678
|
+
"fill"
|
|
679
|
+
];
|
|
680
|
+
function getDefaultStyle(node, pseudoElement, context) {
|
|
681
|
+
const { defaultComputedStyles } = context;
|
|
682
|
+
const nodeName = node.nodeName.toLowerCase();
|
|
683
|
+
const isSvgNode = isSVGElementNode(node) && nodeName !== "svg";
|
|
684
|
+
const attributes = isSvgNode ? includedAttributes.map((name) => [name, node.getAttribute(name)]).filter(([, value]) => value !== null) : [];
|
|
685
|
+
const key = [
|
|
686
|
+
isSvgNode && "svg",
|
|
687
|
+
nodeName,
|
|
688
|
+
attributes.map((name, value) => `${name}=${value}`).join(","),
|
|
689
|
+
pseudoElement
|
|
690
|
+
].filter(Boolean).join(":");
|
|
691
|
+
if (defaultComputedStyles.has(key))
|
|
692
|
+
return defaultComputedStyles.get(key);
|
|
693
|
+
const sandbox = getSandBox(context);
|
|
694
|
+
const sandboxWindow = sandbox == null ? void 0 : sandbox.contentWindow;
|
|
695
|
+
if (!sandboxWindow)
|
|
696
|
+
return /* @__PURE__ */ new Map();
|
|
697
|
+
const sandboxDocument = sandboxWindow == null ? void 0 : sandboxWindow.document;
|
|
698
|
+
let root;
|
|
699
|
+
let el;
|
|
700
|
+
if (isSvgNode) {
|
|
701
|
+
root = sandboxDocument.createElementNS(XMLNS, "svg");
|
|
702
|
+
el = root.ownerDocument.createElementNS(root.namespaceURI, nodeName);
|
|
703
|
+
attributes.forEach(([name, value]) => {
|
|
704
|
+
el.setAttributeNS(null, name, value);
|
|
705
|
+
});
|
|
706
|
+
root.appendChild(el);
|
|
707
|
+
} else {
|
|
708
|
+
root = el = sandboxDocument.createElement(nodeName);
|
|
709
|
+
}
|
|
710
|
+
el.textContent = " ";
|
|
711
|
+
sandboxDocument.body.appendChild(root);
|
|
712
|
+
const computedStyle = sandboxWindow.getComputedStyle(el, pseudoElement);
|
|
713
|
+
const styles = /* @__PURE__ */ new Map();
|
|
714
|
+
for (let len = computedStyle.length, i = 0; i < len; i++) {
|
|
715
|
+
const name = computedStyle.item(i);
|
|
716
|
+
if (ignoredStyles.includes(name))
|
|
717
|
+
continue;
|
|
718
|
+
styles.set(name, computedStyle.getPropertyValue(name));
|
|
350
719
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
720
|
+
sandboxDocument.body.removeChild(root);
|
|
721
|
+
defaultComputedStyles.set(key, styles);
|
|
722
|
+
return styles;
|
|
723
|
+
}
|
|
724
|
+
function getDiffStyle(style, defaultStyle, includeStyleProperties) {
|
|
725
|
+
var _a2;
|
|
726
|
+
const diffStyle = /* @__PURE__ */ new Map();
|
|
727
|
+
const prefixs = [];
|
|
728
|
+
const prefixTree = /* @__PURE__ */ new Map();
|
|
729
|
+
if (includeStyleProperties) {
|
|
730
|
+
for (const name of includeStyleProperties) {
|
|
731
|
+
applyTo(name);
|
|
732
|
+
}
|
|
355
733
|
} else {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
734
|
+
for (let len = style.length, i = 0; i < len; i++) {
|
|
735
|
+
const name = style.item(i);
|
|
736
|
+
applyTo(name);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
for (let len = prefixs.length, i = 0; i < len; i++) {
|
|
740
|
+
(_a2 = prefixTree.get(prefixs[i])) == null ? void 0 : _a2.forEach((value, name) => diffStyle.set(name, value));
|
|
741
|
+
}
|
|
742
|
+
function applyTo(name) {
|
|
743
|
+
const value = style.getPropertyValue(name);
|
|
744
|
+
const priority = style.getPropertyPriority(name);
|
|
745
|
+
const subIndex = name.lastIndexOf("-");
|
|
746
|
+
const prefix = subIndex > -1 ? name.substring(0, subIndex) : void 0;
|
|
747
|
+
if (prefix) {
|
|
748
|
+
let map = prefixTree.get(prefix);
|
|
749
|
+
if (!map) {
|
|
750
|
+
map = /* @__PURE__ */ new Map();
|
|
751
|
+
prefixTree.set(prefix, map);
|
|
367
752
|
}
|
|
368
|
-
|
|
369
|
-
}
|
|
753
|
+
map.set(name, [value, priority]);
|
|
754
|
+
}
|
|
755
|
+
if (defaultStyle.get(name) === value && !priority)
|
|
756
|
+
return;
|
|
757
|
+
if (prefix) {
|
|
758
|
+
prefixs.push(prefix);
|
|
759
|
+
} else {
|
|
760
|
+
diffStyle.set(name, [value, priority]);
|
|
761
|
+
}
|
|
370
762
|
}
|
|
763
|
+
return diffStyle;
|
|
371
764
|
}
|
|
372
|
-
function
|
|
373
|
-
|
|
374
|
-
|
|
765
|
+
function copyCssStyles(node, cloned, isRoot, context) {
|
|
766
|
+
var _a2, _b, _c, _d;
|
|
767
|
+
const { ownerWindow, includeStyleProperties, currentParentNodeStyle } = context;
|
|
768
|
+
const clonedStyle = cloned.style;
|
|
769
|
+
const computedStyle = ownerWindow.getComputedStyle(node);
|
|
770
|
+
const defaultStyle = getDefaultStyle(node, null, context);
|
|
771
|
+
currentParentNodeStyle == null ? void 0 : currentParentNodeStyle.forEach((_, key) => {
|
|
772
|
+
defaultStyle.delete(key);
|
|
773
|
+
});
|
|
774
|
+
const style = getDiffStyle(computedStyle, defaultStyle, includeStyleProperties);
|
|
775
|
+
style.delete("transition-property");
|
|
776
|
+
style.delete("all");
|
|
777
|
+
style.delete("d");
|
|
778
|
+
style.delete("content");
|
|
779
|
+
if (isRoot) {
|
|
780
|
+
style.delete("margin-top");
|
|
781
|
+
style.delete("margin-right");
|
|
782
|
+
style.delete("margin-bottom");
|
|
783
|
+
style.delete("margin-left");
|
|
784
|
+
style.delete("margin-block-start");
|
|
785
|
+
style.delete("margin-block-end");
|
|
786
|
+
style.delete("margin-inline-start");
|
|
787
|
+
style.delete("margin-inline-end");
|
|
788
|
+
style.set("box-sizing", ["border-box", ""]);
|
|
375
789
|
}
|
|
376
|
-
if (
|
|
377
|
-
|
|
790
|
+
if (((_a2 = style.get("background-clip")) == null ? void 0 : _a2[0]) === "text") {
|
|
791
|
+
cloned.classList.add("______background-clip--text");
|
|
378
792
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (selectedOption) {
|
|
385
|
-
selectedOption.setAttribute("selected", "");
|
|
793
|
+
if (IN_CHROME) {
|
|
794
|
+
if (!style.has("font-kerning"))
|
|
795
|
+
style.set("font-kerning", ["normal", ""]);
|
|
796
|
+
if ((((_b = style.get("overflow-x")) == null ? void 0 : _b[0]) === "hidden" || ((_c = style.get("overflow-y")) == null ? void 0 : _c[0]) === "hidden") && ((_d = style.get("text-overflow")) == null ? void 0 : _d[0]) === "ellipsis" && node.scrollWidth === node.clientWidth) {
|
|
797
|
+
style.set("text-overflow", ["clip", ""]);
|
|
386
798
|
}
|
|
387
799
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
if (isInstanceOfElement(clonedNode, Element)) {
|
|
391
|
-
cloneCSSStyle(nativeNode, clonedNode, options);
|
|
392
|
-
clonePseudoElements(nativeNode, clonedNode, options);
|
|
393
|
-
cloneInputValue(nativeNode, clonedNode);
|
|
394
|
-
cloneSelectValue(nativeNode, clonedNode);
|
|
800
|
+
for (let len = clonedStyle.length, i = 0; i < len; i++) {
|
|
801
|
+
clonedStyle.removeProperty(clonedStyle.item(i));
|
|
395
802
|
}
|
|
396
|
-
|
|
803
|
+
style.forEach(([value, priority], name) => {
|
|
804
|
+
clonedStyle.setProperty(name, value, priority);
|
|
805
|
+
});
|
|
806
|
+
return style;
|
|
397
807
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return clone;
|
|
808
|
+
function copyInputValue(node, cloned) {
|
|
809
|
+
if (isTextareaElement(node) || isInputElement(node) || isSelectElement(node)) {
|
|
810
|
+
cloned.setAttribute("value", node.value);
|
|
402
811
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
812
|
+
}
|
|
813
|
+
const pseudoClasses = [
|
|
814
|
+
":before",
|
|
815
|
+
":after"
|
|
816
|
+
// ':placeholder', TODO
|
|
817
|
+
];
|
|
818
|
+
const scrollbarPseudoClasses = [
|
|
819
|
+
":-webkit-scrollbar",
|
|
820
|
+
":-webkit-scrollbar-button",
|
|
821
|
+
// ':-webkit-scrollbar:horizontal', TODO
|
|
822
|
+
":-webkit-scrollbar-thumb",
|
|
823
|
+
":-webkit-scrollbar-track",
|
|
824
|
+
":-webkit-scrollbar-track-piece",
|
|
825
|
+
// ':-webkit-scrollbar:vertical', TODO
|
|
826
|
+
":-webkit-scrollbar-corner",
|
|
827
|
+
":-webkit-resizer"
|
|
828
|
+
];
|
|
829
|
+
function copyPseudoClass(node, cloned, copyScrollbar, context, addWordToFontFamilies) {
|
|
830
|
+
const { ownerWindow, svgStyleElement, svgStyles, currentNodeStyle } = context;
|
|
831
|
+
if (!svgStyleElement || !ownerWindow)
|
|
832
|
+
return;
|
|
833
|
+
function copyBy(pseudoClass) {
|
|
834
|
+
var _a2;
|
|
835
|
+
const computedStyle = ownerWindow.getComputedStyle(node, pseudoClass);
|
|
836
|
+
let content = computedStyle.getPropertyValue("content");
|
|
837
|
+
if (!content || content === "none")
|
|
838
|
+
return;
|
|
839
|
+
addWordToFontFamilies == null ? void 0 : addWordToFontFamilies(content);
|
|
840
|
+
content = content.replace(/(')|(")|(counter\(.+\))/g, "");
|
|
841
|
+
const klasses = [uuid()];
|
|
842
|
+
const defaultStyle = getDefaultStyle(node, pseudoClass, context);
|
|
843
|
+
currentNodeStyle == null ? void 0 : currentNodeStyle.forEach((_, key) => {
|
|
844
|
+
defaultStyle.delete(key);
|
|
845
|
+
});
|
|
846
|
+
const style = getDiffStyle(computedStyle, defaultStyle, context.includeStyleProperties);
|
|
847
|
+
style.delete("content");
|
|
848
|
+
style.delete("-webkit-locale");
|
|
849
|
+
if (((_a2 = style.get("background-clip")) == null ? void 0 : _a2[0]) === "text") {
|
|
850
|
+
cloned.classList.add("______background-clip--text");
|
|
413
851
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
852
|
+
const cloneStyle = [
|
|
853
|
+
`content: '${content}';`
|
|
854
|
+
];
|
|
855
|
+
style.forEach(([value, priority], name) => {
|
|
856
|
+
cloneStyle.push(`${name}: ${value}${priority ? " !important" : ""};`);
|
|
857
|
+
});
|
|
858
|
+
if (cloneStyle.length === 1)
|
|
859
|
+
return;
|
|
860
|
+
try {
|
|
861
|
+
cloned.className = [cloned.className, ...klasses].join(" ");
|
|
862
|
+
} catch (err) {
|
|
863
|
+
context.log.warn("Failed to copyPseudoClass", err);
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
const cssText = cloneStyle.join("\n ");
|
|
867
|
+
let allClasses = svgStyles.get(cssText);
|
|
868
|
+
if (!allClasses) {
|
|
869
|
+
allClasses = [];
|
|
870
|
+
svgStyles.set(cssText, allClasses);
|
|
429
871
|
}
|
|
430
|
-
|
|
872
|
+
allClasses.push(`.${klasses[0]}:${pseudoClass}`);
|
|
431
873
|
}
|
|
432
|
-
|
|
874
|
+
pseudoClasses.forEach(copyBy);
|
|
875
|
+
if (copyScrollbar)
|
|
876
|
+
scrollbarPseudoClasses.forEach(copyBy);
|
|
433
877
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
878
|
+
const excludeParentNodes = /* @__PURE__ */ new Set([
|
|
879
|
+
"symbol"
|
|
880
|
+
// test/fixtures/svg.symbol.html
|
|
881
|
+
]);
|
|
882
|
+
async function appendChildNode(node, cloned, child, context, addWordToFontFamilies) {
|
|
883
|
+
if (isElementNode(child) && (isStyleElement(child) || isScriptElement(child)))
|
|
884
|
+
return;
|
|
885
|
+
if (context.filter && !context.filter(child))
|
|
886
|
+
return;
|
|
887
|
+
if (excludeParentNodes.has(cloned.nodeName) || excludeParentNodes.has(child.nodeName)) {
|
|
888
|
+
context.currentParentNodeStyle = void 0;
|
|
889
|
+
} else {
|
|
890
|
+
context.currentParentNodeStyle = context.currentNodeStyle;
|
|
437
891
|
}
|
|
438
|
-
|
|
892
|
+
const childCloned = await cloneNode(child, context, false, addWordToFontFamilies);
|
|
893
|
+
if (context.isEnable("restoreScrollPosition")) {
|
|
894
|
+
restoreScrollPosition(node, childCloned);
|
|
895
|
+
}
|
|
896
|
+
cloned.appendChild(childCloned);
|
|
439
897
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
898
|
+
async function cloneChildNodes(node, cloned, context, addWordToFontFamilies) {
|
|
899
|
+
var _a2;
|
|
900
|
+
const firstChild = (isElementNode(node) ? (_a2 = node.shadowRoot) == null ? void 0 : _a2.firstChild : void 0) ?? node.firstChild;
|
|
901
|
+
for (let child = firstChild; child; child = child.nextSibling) {
|
|
902
|
+
if (isCommentNode(child))
|
|
903
|
+
continue;
|
|
904
|
+
if (isElementNode(child) && isSlotElement(child) && typeof child.assignedNodes === "function") {
|
|
905
|
+
const nodes = child.assignedNodes();
|
|
906
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
907
|
+
await appendChildNode(node, cloned, nodes[i], context, addWordToFontFamilies);
|
|
908
|
+
}
|
|
909
|
+
} else {
|
|
910
|
+
await appendChildNode(node, cloned, child, context, addWordToFontFamilies);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
446
913
|
}
|
|
447
|
-
function
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
914
|
+
function restoreScrollPosition(node, chlidCloned) {
|
|
915
|
+
if (!isHTMLElementNode(node) || !isHTMLElementNode(chlidCloned))
|
|
916
|
+
return;
|
|
917
|
+
const { scrollTop, scrollLeft } = node;
|
|
918
|
+
if (!scrollTop && !scrollLeft) {
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const { transform } = chlidCloned.style;
|
|
922
|
+
const matrix = new DOMMatrix(transform);
|
|
923
|
+
const { a, b, c, d } = matrix;
|
|
924
|
+
matrix.a = 1;
|
|
925
|
+
matrix.b = 0;
|
|
926
|
+
matrix.c = 0;
|
|
927
|
+
matrix.d = 1;
|
|
928
|
+
matrix.translateSelf(-scrollLeft, -scrollTop);
|
|
929
|
+
matrix.a = a;
|
|
930
|
+
matrix.b = b;
|
|
931
|
+
matrix.c = c;
|
|
932
|
+
matrix.d = d;
|
|
933
|
+
chlidCloned.style.transform = matrix.toString();
|
|
454
934
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
|
|
935
|
+
function applyCssStyleWithOptions(cloned, context) {
|
|
936
|
+
const { backgroundColor, width, height, style: styles } = context;
|
|
937
|
+
const clonedStyle = cloned.style;
|
|
938
|
+
if (backgroundColor)
|
|
939
|
+
clonedStyle.setProperty("background-color", backgroundColor, "important");
|
|
940
|
+
if (width)
|
|
941
|
+
clonedStyle.setProperty("width", `${width}px`, "important");
|
|
942
|
+
if (height)
|
|
943
|
+
clonedStyle.setProperty("height", `${height}px`, "important");
|
|
944
|
+
if (styles) {
|
|
945
|
+
for (const name in styles) clonedStyle[name] = styles[name];
|
|
466
946
|
}
|
|
467
|
-
return cssText;
|
|
468
947
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
948
|
+
const NORMAL_ATTRIBUTE_RE = /^[\w-:]+$/;
|
|
949
|
+
async function cloneNode(node, context, isRoot = false, addWordToFontFamilies) {
|
|
950
|
+
var _a2, _b, _c, _d;
|
|
951
|
+
const { ownerDocument, ownerWindow, fontFamilies } = context;
|
|
952
|
+
if (ownerDocument && isTextNode(node)) {
|
|
953
|
+
if (addWordToFontFamilies && /\S/.test(node.data)) {
|
|
954
|
+
addWordToFontFamilies(node.data);
|
|
955
|
+
}
|
|
956
|
+
return ownerDocument.createTextNode(node.data);
|
|
957
|
+
}
|
|
958
|
+
if (ownerDocument && ownerWindow && isElementNode(node) && (isHTMLElementNode(node) || isSVGElementNode(node))) {
|
|
959
|
+
const cloned2 = await cloneElement(node, context);
|
|
960
|
+
if (context.isEnable("removeAbnormalAttributes")) {
|
|
961
|
+
const names = cloned2.getAttributeNames();
|
|
962
|
+
for (let len = names.length, i = 0; i < len; i++) {
|
|
963
|
+
const name = names[i];
|
|
964
|
+
if (!NORMAL_ATTRIBUTE_RE.test(name)) {
|
|
965
|
+
cloned2.removeAttribute(name);
|
|
966
|
+
}
|
|
475
967
|
}
|
|
476
|
-
|
|
477
|
-
|
|
968
|
+
}
|
|
969
|
+
const style = context.currentNodeStyle = copyCssStyles(node, cloned2, isRoot, context);
|
|
970
|
+
if (isRoot)
|
|
971
|
+
applyCssStyleWithOptions(cloned2, context);
|
|
972
|
+
let copyScrollbar = false;
|
|
973
|
+
if (context.isEnable("copyScrollbar")) {
|
|
974
|
+
const overflow = [
|
|
975
|
+
(_a2 = style.get("overflow-x")) == null ? void 0 : _a2[0],
|
|
976
|
+
(_b = style.get("overflow-y")) == null ? void 0 : _b[0]
|
|
977
|
+
];
|
|
978
|
+
copyScrollbar = overflow.includes("scroll") || (overflow.includes("auto") || overflow.includes("overlay")) && (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth);
|
|
979
|
+
}
|
|
980
|
+
const textTransform = (_c = style.get("text-transform")) == null ? void 0 : _c[0];
|
|
981
|
+
const families = splitFontFamily((_d = style.get("font-family")) == null ? void 0 : _d[0]);
|
|
982
|
+
const addWordToFontFamilies2 = families ? (word) => {
|
|
983
|
+
if (textTransform === "uppercase") {
|
|
984
|
+
word = word.toUpperCase();
|
|
985
|
+
} else if (textTransform === "lowercase") {
|
|
986
|
+
word = word.toLowerCase();
|
|
987
|
+
} else if (textTransform === "capitalize") {
|
|
988
|
+
word = word[0].toUpperCase() + word.substring(1);
|
|
478
989
|
}
|
|
990
|
+
families.forEach((family) => {
|
|
991
|
+
let fontFamily = fontFamilies.get(family);
|
|
992
|
+
if (!fontFamily) {
|
|
993
|
+
fontFamilies.set(family, fontFamily = /* @__PURE__ */ new Set());
|
|
994
|
+
}
|
|
995
|
+
word.split("").forEach((text) => fontFamily.add(text));
|
|
996
|
+
});
|
|
997
|
+
} : void 0;
|
|
998
|
+
copyPseudoClass(
|
|
999
|
+
node,
|
|
1000
|
+
cloned2,
|
|
1001
|
+
copyScrollbar,
|
|
1002
|
+
context,
|
|
1003
|
+
addWordToFontFamilies2
|
|
1004
|
+
);
|
|
1005
|
+
copyInputValue(node, cloned2);
|
|
1006
|
+
if (!isVideoElement(node)) {
|
|
1007
|
+
await cloneChildNodes(
|
|
1008
|
+
node,
|
|
1009
|
+
cloned2,
|
|
1010
|
+
context,
|
|
1011
|
+
addWordToFontFamilies2
|
|
1012
|
+
);
|
|
479
1013
|
}
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
function shouldEmbed(url) {
|
|
483
|
-
return url.search(URL_REGEX) !== -1;
|
|
484
|
-
}
|
|
485
|
-
async function embedResources(cssText, baseUrl, options) {
|
|
486
|
-
if (!shouldEmbed(cssText)) {
|
|
487
|
-
return cssText;
|
|
1014
|
+
return cloned2;
|
|
488
1015
|
}
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
return
|
|
1016
|
+
const cloned = node.cloneNode(false);
|
|
1017
|
+
await cloneChildNodes(node, cloned, context);
|
|
1018
|
+
return cloned;
|
|
492
1019
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
1020
|
+
function destroyContext(context) {
|
|
1021
|
+
context.ownerDocument = void 0;
|
|
1022
|
+
context.ownerWindow = void 0;
|
|
1023
|
+
context.svgStyleElement = void 0;
|
|
1024
|
+
context.svgDefsElement = void 0;
|
|
1025
|
+
context.svgStyles.clear();
|
|
1026
|
+
context.defaultComputedStyles.clear();
|
|
1027
|
+
if (context.sandbox) {
|
|
1028
|
+
try {
|
|
1029
|
+
context.sandbox.remove();
|
|
1030
|
+
} catch (err) {
|
|
1031
|
+
context.log.warn("Failed to destroyContext", err);
|
|
1032
|
+
}
|
|
1033
|
+
context.sandbox = void 0;
|
|
500
1034
|
}
|
|
501
|
-
|
|
1035
|
+
context.workers = [];
|
|
1036
|
+
context.fontFamilies.clear();
|
|
1037
|
+
context.fontCssTexts.clear();
|
|
1038
|
+
context.requests.clear();
|
|
1039
|
+
context.tasks = [];
|
|
502
1040
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
1041
|
+
function baseFetch(options) {
|
|
1042
|
+
const { url, timeout, responseType, ...requestInit } = options;
|
|
1043
|
+
const controller = new AbortController();
|
|
1044
|
+
const timer = timeout ? setTimeout(() => controller.abort(), timeout) : void 0;
|
|
1045
|
+
return fetch(url, { signal: controller.signal, ...requestInit }).then((response) => {
|
|
1046
|
+
if (!response.ok) {
|
|
1047
|
+
throw new Error("Failed fetch, not 2xx response", { cause: response });
|
|
1048
|
+
}
|
|
1049
|
+
switch (responseType) {
|
|
1050
|
+
case "arrayBuffer":
|
|
1051
|
+
return response.arrayBuffer();
|
|
1052
|
+
case "dataUrl":
|
|
1053
|
+
return response.blob().then(blobToDataUrl);
|
|
1054
|
+
case "text":
|
|
1055
|
+
default:
|
|
1056
|
+
return response.text();
|
|
1057
|
+
}
|
|
1058
|
+
}).finally(() => clearTimeout(timer));
|
|
506
1059
|
}
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
1060
|
+
function contextFetch(context, options) {
|
|
1061
|
+
const { url: rawUrl, requestType = "text", responseType = "text", imageDom } = options;
|
|
1062
|
+
let url = rawUrl;
|
|
1063
|
+
const {
|
|
1064
|
+
timeout,
|
|
1065
|
+
acceptOfImage,
|
|
1066
|
+
requests,
|
|
1067
|
+
fetchFn,
|
|
1068
|
+
fetch: {
|
|
1069
|
+
requestInit,
|
|
1070
|
+
bypassingCache,
|
|
1071
|
+
placeholderImage
|
|
1072
|
+
},
|
|
1073
|
+
font,
|
|
1074
|
+
workers,
|
|
1075
|
+
fontFamilies
|
|
1076
|
+
} = context;
|
|
1077
|
+
if (requestType === "image" && (IN_SAFARI || IN_FIREFOX)) {
|
|
1078
|
+
context.drawImageCount++;
|
|
511
1079
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
try {
|
|
518
|
-
resolve(options.onImageErrorHandler(...attributes));
|
|
519
|
-
} catch (error) {
|
|
520
|
-
reject(error);
|
|
1080
|
+
let request = requests.get(rawUrl);
|
|
1081
|
+
if (!request) {
|
|
1082
|
+
if (bypassingCache) {
|
|
1083
|
+
if (bypassingCache instanceof RegExp && bypassingCache.test(url)) {
|
|
1084
|
+
url += (/\?/.test(url) ? "&" : "?") + (/* @__PURE__ */ new Date()).getTime();
|
|
521
1085
|
}
|
|
522
|
-
} : reject;
|
|
523
|
-
const image = clonedNode;
|
|
524
|
-
if (image.decode) {
|
|
525
|
-
image.decode = resolve;
|
|
526
1086
|
}
|
|
527
|
-
|
|
528
|
-
|
|
1087
|
+
const canFontMinify = requestType.startsWith("font") && font && font.minify;
|
|
1088
|
+
const fontTexts = /* @__PURE__ */ new Set();
|
|
1089
|
+
if (canFontMinify) {
|
|
1090
|
+
const families = requestType.split(";")[1].split(",");
|
|
1091
|
+
families.forEach((family) => {
|
|
1092
|
+
if (!fontFamilies.has(family))
|
|
1093
|
+
return;
|
|
1094
|
+
fontFamilies.get(family).forEach((text) => fontTexts.add(text));
|
|
1095
|
+
});
|
|
529
1096
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
1097
|
+
const needFontMinify = canFontMinify && fontTexts.size;
|
|
1098
|
+
const baseFetchOptions = {
|
|
1099
|
+
url,
|
|
1100
|
+
timeout,
|
|
1101
|
+
responseType: needFontMinify ? "arrayBuffer" : responseType,
|
|
1102
|
+
headers: requestType === "image" ? { accept: acceptOfImage } : void 0,
|
|
1103
|
+
...requestInit
|
|
1104
|
+
};
|
|
1105
|
+
request = {
|
|
1106
|
+
type: requestType,
|
|
1107
|
+
resolve: void 0,
|
|
1108
|
+
reject: void 0,
|
|
1109
|
+
response: null
|
|
1110
|
+
};
|
|
1111
|
+
request.response = (async () => {
|
|
1112
|
+
if (fetchFn && requestType === "image") {
|
|
1113
|
+
const result = await fetchFn(rawUrl);
|
|
1114
|
+
if (result)
|
|
1115
|
+
return result;
|
|
1116
|
+
}
|
|
1117
|
+
if (!IN_SAFARI && rawUrl.startsWith("http") && workers.length) {
|
|
1118
|
+
return new Promise((resolve, reject) => {
|
|
1119
|
+
const worker = workers[requests.size & workers.length - 1];
|
|
1120
|
+
worker.postMessage({ rawUrl, ...baseFetchOptions });
|
|
1121
|
+
request.resolve = resolve;
|
|
1122
|
+
request.reject = reject;
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
return baseFetch(baseFetchOptions);
|
|
1126
|
+
})().catch((error) => {
|
|
1127
|
+
requests.delete(rawUrl);
|
|
1128
|
+
if (requestType === "image" && placeholderImage) {
|
|
1129
|
+
context.log.warn("Failed to fetch image base64, trying to use placeholder image", url);
|
|
1130
|
+
return typeof placeholderImage === "string" ? placeholderImage : placeholderImage(imageDom);
|
|
1131
|
+
}
|
|
1132
|
+
throw error;
|
|
1133
|
+
});
|
|
1134
|
+
requests.set(rawUrl, request);
|
|
1135
|
+
}
|
|
1136
|
+
return request.response;
|
|
1137
|
+
}
|
|
1138
|
+
async function replaceCssUrlToDataUrl(cssText, baseUrl, context, isImage) {
|
|
1139
|
+
if (!hasCssUrl(cssText))
|
|
1140
|
+
return cssText;
|
|
1141
|
+
for (const [rawUrl, url] of parseCssUrls(cssText, baseUrl)) {
|
|
1142
|
+
try {
|
|
1143
|
+
const dataUrl = await contextFetch(
|
|
1144
|
+
context,
|
|
1145
|
+
{
|
|
1146
|
+
url,
|
|
1147
|
+
requestType: isImage ? "image" : "text",
|
|
1148
|
+
responseType: "dataUrl"
|
|
1149
|
+
}
|
|
1150
|
+
);
|
|
1151
|
+
cssText = cssText.replace(toRE(rawUrl), `$1${dataUrl}$3`);
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
context.log.warn("Failed to fetch css data url", rawUrl, error);
|
|
535
1154
|
}
|
|
1155
|
+
}
|
|
1156
|
+
return cssText;
|
|
1157
|
+
}
|
|
1158
|
+
function hasCssUrl(cssText) {
|
|
1159
|
+
return /url\((['"]?)([^'"]+?)\1\)/.test(cssText);
|
|
1160
|
+
}
|
|
1161
|
+
const URL_RE = /url\((['"]?)([^'"]+?)\1\)/g;
|
|
1162
|
+
function parseCssUrls(cssText, baseUrl) {
|
|
1163
|
+
const result = [];
|
|
1164
|
+
cssText.replace(URL_RE, (raw, quotation, url) => {
|
|
1165
|
+
result.push([url, resolveUrl(url, baseUrl)]);
|
|
1166
|
+
return raw;
|
|
536
1167
|
});
|
|
1168
|
+
return result.filter(([url]) => !isDataUrl(url));
|
|
1169
|
+
}
|
|
1170
|
+
function toRE(url) {
|
|
1171
|
+
const escaped = url.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1");
|
|
1172
|
+
return new RegExp(`(url\\(['"]?)(${escaped})(['"]?\\))`, "g");
|
|
537
1173
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
1174
|
+
const properties = [
|
|
1175
|
+
"background-image",
|
|
1176
|
+
"border-image-source",
|
|
1177
|
+
"-webkit-border-image",
|
|
1178
|
+
"-webkit-mask-image",
|
|
1179
|
+
"list-style-image"
|
|
1180
|
+
];
|
|
1181
|
+
function embedCssStyleImage(style, context) {
|
|
1182
|
+
return properties.map((property) => {
|
|
1183
|
+
const value = style.getPropertyValue(property);
|
|
1184
|
+
if (!value || value === "none") {
|
|
1185
|
+
return null;
|
|
1186
|
+
}
|
|
1187
|
+
if (IN_SAFARI || IN_FIREFOX) {
|
|
1188
|
+
context.drawImageCount++;
|
|
1189
|
+
}
|
|
1190
|
+
return replaceCssUrlToDataUrl(value, null, context, true).then((newValue) => {
|
|
1191
|
+
if (!newValue || value === newValue)
|
|
1192
|
+
return;
|
|
1193
|
+
style.setProperty(
|
|
1194
|
+
property,
|
|
1195
|
+
newValue,
|
|
1196
|
+
style.getPropertyPriority(property)
|
|
1197
|
+
);
|
|
1198
|
+
});
|
|
1199
|
+
}).filter(Boolean);
|
|
542
1200
|
}
|
|
543
|
-
|
|
544
|
-
if (
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
1201
|
+
function embedImageElement(cloned, context) {
|
|
1202
|
+
if (isImageElement(cloned)) {
|
|
1203
|
+
const originalSrc = cloned.currentSrc || cloned.src;
|
|
1204
|
+
if (!isDataUrl(originalSrc)) {
|
|
1205
|
+
return [
|
|
1206
|
+
contextFetch(context, {
|
|
1207
|
+
url: originalSrc,
|
|
1208
|
+
imageDom: cloned,
|
|
1209
|
+
requestType: "image",
|
|
1210
|
+
responseType: "dataUrl"
|
|
1211
|
+
}).then((url) => {
|
|
1212
|
+
if (!url)
|
|
1213
|
+
return;
|
|
1214
|
+
cloned.srcset = "";
|
|
1215
|
+
cloned.dataset.originalSrc = originalSrc;
|
|
1216
|
+
cloned.src = url || "";
|
|
1217
|
+
})
|
|
1218
|
+
];
|
|
1219
|
+
}
|
|
1220
|
+
if (IN_SAFARI || IN_FIREFOX) {
|
|
1221
|
+
context.drawImageCount++;
|
|
1222
|
+
}
|
|
1223
|
+
} else if (isSVGElementNode(cloned) && !isDataUrl(cloned.href.baseVal)) {
|
|
1224
|
+
const originalSrc = cloned.href.baseVal;
|
|
1225
|
+
return [
|
|
1226
|
+
contextFetch(context, {
|
|
1227
|
+
url: originalSrc,
|
|
1228
|
+
imageDom: cloned,
|
|
1229
|
+
requestType: "image",
|
|
1230
|
+
responseType: "dataUrl"
|
|
1231
|
+
}).then((url) => {
|
|
1232
|
+
if (!url)
|
|
1233
|
+
return;
|
|
1234
|
+
cloned.dataset.originalSrc = originalSrc;
|
|
1235
|
+
cloned.href.baseVal = url || "";
|
|
1236
|
+
})
|
|
1237
|
+
];
|
|
548
1238
|
}
|
|
1239
|
+
return [];
|
|
549
1240
|
}
|
|
550
|
-
function
|
|
551
|
-
const {
|
|
552
|
-
|
|
553
|
-
|
|
1241
|
+
function embedSvgUse(cloned, context) {
|
|
1242
|
+
const { ownerDocument, svgDefsElement } = context;
|
|
1243
|
+
const href = cloned.getAttribute("href") ?? cloned.getAttribute("xlink:href");
|
|
1244
|
+
if (!href)
|
|
1245
|
+
return [];
|
|
1246
|
+
const [svgUrl, id] = href.split("#");
|
|
1247
|
+
if (id) {
|
|
1248
|
+
const query = `#${id}`;
|
|
1249
|
+
const definition = ownerDocument == null ? void 0 : ownerDocument.querySelector(`svg ${query}`);
|
|
1250
|
+
if (svgUrl) {
|
|
1251
|
+
cloned.setAttribute("href", query);
|
|
1252
|
+
}
|
|
1253
|
+
if (svgDefsElement == null ? void 0 : svgDefsElement.querySelector(query))
|
|
1254
|
+
return [];
|
|
1255
|
+
if (definition) {
|
|
1256
|
+
svgDefsElement == null ? void 0 : svgDefsElement.appendChild(definition.cloneNode(true));
|
|
1257
|
+
return [];
|
|
1258
|
+
} else if (svgUrl) {
|
|
1259
|
+
return [
|
|
1260
|
+
contextFetch(context, {
|
|
1261
|
+
url: svgUrl,
|
|
1262
|
+
responseType: "text"
|
|
1263
|
+
}).then((svgData) => {
|
|
1264
|
+
svgDefsElement == null ? void 0 : svgDefsElement.insertAdjacentHTML("beforeend", svgData);
|
|
1265
|
+
})
|
|
1266
|
+
];
|
|
1267
|
+
}
|
|
554
1268
|
}
|
|
555
|
-
|
|
556
|
-
|
|
1269
|
+
return [];
|
|
1270
|
+
}
|
|
1271
|
+
function embedNode(cloned, context) {
|
|
1272
|
+
const { tasks } = context;
|
|
1273
|
+
if (isElementNode(cloned)) {
|
|
1274
|
+
if (isImageElement(cloned) || isSVGImageElementNode(cloned)) {
|
|
1275
|
+
tasks.push(...embedImageElement(cloned, context));
|
|
1276
|
+
}
|
|
1277
|
+
if (isSVGUseElementNode(cloned)) {
|
|
1278
|
+
tasks.push(...embedSvgUse(cloned, context));
|
|
1279
|
+
}
|
|
557
1280
|
}
|
|
558
|
-
if (
|
|
559
|
-
style
|
|
1281
|
+
if (isHTMLElementNode(cloned)) {
|
|
1282
|
+
tasks.push(...embedCssStyleImage(cloned.style, context));
|
|
560
1283
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
1284
|
+
cloned.childNodes.forEach((child) => {
|
|
1285
|
+
embedNode(child, context);
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
async function embedWebFont(clone, context) {
|
|
1289
|
+
const {
|
|
1290
|
+
ownerDocument,
|
|
1291
|
+
svgStyleElement,
|
|
1292
|
+
fontFamilies,
|
|
1293
|
+
fontCssTexts,
|
|
1294
|
+
tasks,
|
|
1295
|
+
font
|
|
1296
|
+
} = context;
|
|
1297
|
+
if (!ownerDocument || !svgStyleElement || !fontFamilies.size) {
|
|
1298
|
+
return;
|
|
566
1299
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
return cache2;
|
|
580
|
-
}
|
|
581
|
-
async function embedFonts(data, options) {
|
|
582
|
-
let cssText = data.cssText;
|
|
583
|
-
const regexUrl = /url\(["']?([^"')]+)["']?\)/g;
|
|
584
|
-
const fontLocs = cssText.match(/url\([^)]+\)/g) || [];
|
|
585
|
-
const loadFonts = fontLocs.map(async (loc) => {
|
|
586
|
-
let url = loc.replace(regexUrl, "$1");
|
|
587
|
-
if (!url.startsWith("https://")) {
|
|
588
|
-
url = new URL(url, data.url).href;
|
|
589
|
-
}
|
|
590
|
-
return fetchAsDataURL(url, options.fetchRequestInit, ({ result }) => {
|
|
591
|
-
cssText = cssText.replace(loc, `url(${result})`);
|
|
592
|
-
return [loc, result];
|
|
1300
|
+
if (font && font.cssText) {
|
|
1301
|
+
const cssText = filterPreferredFormat(font.cssText, context);
|
|
1302
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText}
|
|
1303
|
+
`));
|
|
1304
|
+
} else {
|
|
1305
|
+
const styleSheets = Array.from(ownerDocument.styleSheets).filter((styleSheet) => {
|
|
1306
|
+
try {
|
|
1307
|
+
return "cssRules" in styleSheet && Boolean(styleSheet.cssRules.length);
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
context.log.warn(`Error while reading CSS rules from ${styleSheet.href}`, error);
|
|
1310
|
+
return false;
|
|
1311
|
+
}
|
|
593
1312
|
});
|
|
594
|
-
|
|
595
|
-
|
|
1313
|
+
await Promise.all(
|
|
1314
|
+
styleSheets.flatMap((styleSheet) => {
|
|
1315
|
+
return Array.from(styleSheet.cssRules).map(async (cssRule, index) => {
|
|
1316
|
+
if (isCSSImportRule(cssRule)) {
|
|
1317
|
+
let importIndex = index + 1;
|
|
1318
|
+
const baseUrl = cssRule.href;
|
|
1319
|
+
let cssText = "";
|
|
1320
|
+
try {
|
|
1321
|
+
cssText = await contextFetch(context, {
|
|
1322
|
+
url: baseUrl,
|
|
1323
|
+
requestType: "text",
|
|
1324
|
+
responseType: "text"
|
|
1325
|
+
});
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
context.log.warn(`Error fetch remote css import from ${baseUrl}`, error);
|
|
1328
|
+
}
|
|
1329
|
+
const replacedCssText = cssText.replace(
|
|
1330
|
+
URL_RE,
|
|
1331
|
+
(raw, quotation, url) => raw.replace(url, resolveUrl(url, baseUrl))
|
|
1332
|
+
);
|
|
1333
|
+
for (const rule of parseCss(replacedCssText)) {
|
|
1334
|
+
try {
|
|
1335
|
+
styleSheet.insertRule(
|
|
1336
|
+
rule,
|
|
1337
|
+
rule.startsWith("@import") ? importIndex += 1 : styleSheet.cssRules.length
|
|
1338
|
+
);
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
context.log.warn("Error inserting rule from remote css import", { rule, error });
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
});
|
|
1345
|
+
})
|
|
1346
|
+
);
|
|
1347
|
+
const cssRules = styleSheets.flatMap((styleSheet) => Array.from(styleSheet.cssRules));
|
|
1348
|
+
cssRules.filter((cssRule) => {
|
|
1349
|
+
var _a2;
|
|
1350
|
+
return isCssFontFaceRule(cssRule) && hasCssUrl(cssRule.style.getPropertyValue("src")) && ((_a2 = splitFontFamily(cssRule.style.getPropertyValue("font-family"))) == null ? void 0 : _a2.some((val) => fontFamilies.has(val)));
|
|
1351
|
+
}).forEach((value) => {
|
|
1352
|
+
const rule = value;
|
|
1353
|
+
const cssText = fontCssTexts.get(rule.cssText);
|
|
1354
|
+
if (cssText) {
|
|
1355
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText}
|
|
1356
|
+
`));
|
|
1357
|
+
} else {
|
|
1358
|
+
tasks.push(
|
|
1359
|
+
replaceCssUrlToDataUrl(
|
|
1360
|
+
rule.cssText,
|
|
1361
|
+
rule.parentStyleSheet ? rule.parentStyleSheet.href : null,
|
|
1362
|
+
context
|
|
1363
|
+
).then((cssText2) => {
|
|
1364
|
+
cssText2 = filterPreferredFormat(cssText2, context);
|
|
1365
|
+
fontCssTexts.set(rule.cssText, cssText2);
|
|
1366
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(`${cssText2}
|
|
1367
|
+
`));
|
|
1368
|
+
})
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
596
1373
|
}
|
|
597
|
-
|
|
598
|
-
|
|
1374
|
+
const COMMENTS_RE = /(\/\*[\s\S]*?\*\/)/g;
|
|
1375
|
+
const KEYFRAMES_RE = /((@.*?keyframes [\s\S]*?){([\s\S]*?}\s*?)})/gi;
|
|
1376
|
+
function parseCss(source) {
|
|
1377
|
+
if (source == null)
|
|
599
1378
|
return [];
|
|
600
|
-
}
|
|
601
1379
|
const result = [];
|
|
602
|
-
|
|
603
|
-
let cssText = source.replace(commentsRegex, "");
|
|
604
|
-
const keyframesRegex = new RegExp("((@.*?keyframes [\\s\\S]*?){([\\s\\S]*?}\\s*?)})", "gi");
|
|
1380
|
+
let cssText = source.replace(COMMENTS_RE, "");
|
|
605
1381
|
while (true) {
|
|
606
|
-
const matches =
|
|
607
|
-
if (matches
|
|
1382
|
+
const matches = KEYFRAMES_RE.exec(cssText);
|
|
1383
|
+
if (!matches)
|
|
608
1384
|
break;
|
|
609
|
-
}
|
|
610
1385
|
result.push(matches[0]);
|
|
611
1386
|
}
|
|
612
|
-
cssText = cssText.replace(
|
|
613
|
-
const
|
|
614
|
-
const
|
|
615
|
-
|
|
1387
|
+
cssText = cssText.replace(KEYFRAMES_RE, "");
|
|
1388
|
+
const IMPORT_RE = /@import[\s\S]*?url\([^)]*\)[\s\S]*?;/gi;
|
|
1389
|
+
const UNIFIED_RE = new RegExp(
|
|
1390
|
+
// eslint-disable-next-line
|
|
1391
|
+
"((\\s*?(?:\\/\\*[\\s\\S]*?\\*\\/)?\\s*?@media[\\s\\S]*?){([\\s\\S]*?)}\\s*?})|(([\\s\\S]*?){([\\s\\S]*?)})",
|
|
1392
|
+
"gi"
|
|
1393
|
+
);
|
|
616
1394
|
while (true) {
|
|
617
|
-
let matches =
|
|
618
|
-
if (matches
|
|
619
|
-
matches =
|
|
620
|
-
if (matches
|
|
1395
|
+
let matches = IMPORT_RE.exec(cssText);
|
|
1396
|
+
if (!matches) {
|
|
1397
|
+
matches = UNIFIED_RE.exec(cssText);
|
|
1398
|
+
if (!matches) {
|
|
621
1399
|
break;
|
|
622
1400
|
} else {
|
|
623
|
-
|
|
1401
|
+
IMPORT_RE.lastIndex = UNIFIED_RE.lastIndex;
|
|
624
1402
|
}
|
|
625
1403
|
} else {
|
|
626
|
-
|
|
1404
|
+
UNIFIED_RE.lastIndex = IMPORT_RE.lastIndex;
|
|
627
1405
|
}
|
|
628
1406
|
result.push(matches[0]);
|
|
629
1407
|
}
|
|
630
1408
|
return result;
|
|
631
1409
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
1410
|
+
const URL_WITH_FORMAT_RE = /url\([^)]+\)\s*format\((["']?)([^"']+)\1\)/g;
|
|
1411
|
+
const FONT_SRC_RE = /src:\s*(?:url\([^)]+\)\s*format\([^)]+\)[,;]\s*)+/g;
|
|
1412
|
+
function filterPreferredFormat(str, context) {
|
|
1413
|
+
const { font } = context;
|
|
1414
|
+
const preferredFormat = font ? font == null ? void 0 : font.preferredFormat : void 0;
|
|
1415
|
+
return preferredFormat ? str.replace(FONT_SRC_RE, (match) => {
|
|
1416
|
+
while (true) {
|
|
1417
|
+
const [src, , format] = URL_WITH_FORMAT_RE.exec(match) || [];
|
|
1418
|
+
if (!format)
|
|
1419
|
+
return "";
|
|
1420
|
+
if (format === preferredFormat)
|
|
1421
|
+
return `src: ${src};`;
|
|
1422
|
+
}
|
|
1423
|
+
}) : str;
|
|
1424
|
+
}
|
|
1425
|
+
async function domToForeignObjectSvg(node, options) {
|
|
1426
|
+
const context = await orCreateContext(node, options);
|
|
1427
|
+
if (isElementNode(context.node) && isSVGElementNode(context.node))
|
|
1428
|
+
return context.node;
|
|
1429
|
+
const {
|
|
1430
|
+
ownerDocument,
|
|
1431
|
+
log: log2,
|
|
1432
|
+
tasks,
|
|
1433
|
+
svgStyleElement,
|
|
1434
|
+
svgDefsElement,
|
|
1435
|
+
svgStyles,
|
|
1436
|
+
font,
|
|
1437
|
+
progress,
|
|
1438
|
+
autoDestruct,
|
|
1439
|
+
onCloneNode,
|
|
1440
|
+
onEmbedNode,
|
|
1441
|
+
onCreateForeignObjectSvg
|
|
1442
|
+
} = context;
|
|
1443
|
+
log2.time("clone node");
|
|
1444
|
+
const clone = await cloneNode(context.node, context, true);
|
|
1445
|
+
if (svgStyleElement && ownerDocument) {
|
|
1446
|
+
let allCssText = "";
|
|
1447
|
+
svgStyles.forEach((klasses, cssText) => {
|
|
1448
|
+
allCssText += `${klasses.join(",\n")} {
|
|
1449
|
+
${cssText}
|
|
1450
|
+
}
|
|
1451
|
+
`;
|
|
1452
|
+
});
|
|
1453
|
+
svgStyleElement.appendChild(ownerDocument.createTextNode(allCssText));
|
|
1454
|
+
}
|
|
1455
|
+
log2.timeEnd("clone node");
|
|
1456
|
+
await (onCloneNode == null ? void 0 : onCloneNode(clone));
|
|
1457
|
+
if (font !== false && isElementNode(clone)) {
|
|
1458
|
+
log2.time("embed web font");
|
|
1459
|
+
await embedWebFont(clone, context);
|
|
1460
|
+
log2.timeEnd("embed web font");
|
|
1461
|
+
}
|
|
1462
|
+
log2.time("embed node");
|
|
1463
|
+
embedNode(clone, context);
|
|
1464
|
+
const count = tasks.length;
|
|
1465
|
+
let current = 0;
|
|
1466
|
+
const runTask = async () => {
|
|
1467
|
+
while (true) {
|
|
1468
|
+
const task = tasks.pop();
|
|
1469
|
+
if (!task)
|
|
1470
|
+
break;
|
|
637
1471
|
try {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
const url = item.href;
|
|
642
|
-
const deferred = fetchCSS(url).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
643
|
-
try {
|
|
644
|
-
sheet.insertRule(rule, rule.startsWith("@import") ? importIndex += 1 : sheet.cssRules.length);
|
|
645
|
-
} catch (error) {
|
|
646
|
-
console.error("Error inserting rule from remote css", {
|
|
647
|
-
rule,
|
|
648
|
-
error
|
|
649
|
-
});
|
|
650
|
-
}
|
|
651
|
-
})).catch((e) => {
|
|
652
|
-
console.error("Error loading remote css", e.toString());
|
|
653
|
-
});
|
|
654
|
-
deferreds.push(deferred);
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
} catch (e) {
|
|
658
|
-
const inline = styleSheets.find((a) => a.href == null) || document.styleSheets[0];
|
|
659
|
-
if (sheet.href != null) {
|
|
660
|
-
deferreds.push(fetchCSS(sheet.href).then((metadata) => embedFonts(metadata, options)).then((cssText) => parseCSS(cssText).forEach((rule) => {
|
|
661
|
-
inline.insertRule(rule, inline.cssRules.length);
|
|
662
|
-
})).catch((err) => {
|
|
663
|
-
console.error("Error loading remote stylesheet", err);
|
|
664
|
-
}));
|
|
665
|
-
}
|
|
666
|
-
console.error("Error inlining remote css file", e);
|
|
1472
|
+
await task;
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
context.log.warn("Failed to run task", error);
|
|
667
1475
|
}
|
|
1476
|
+
progress == null ? void 0 : progress(++current, count);
|
|
668
1477
|
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
|
-
return ret;
|
|
683
|
-
});
|
|
1478
|
+
};
|
|
1479
|
+
progress == null ? void 0 : progress(current, count);
|
|
1480
|
+
await Promise.all([...Array.from({ length: 4 })].map(runTask));
|
|
1481
|
+
log2.timeEnd("embed node");
|
|
1482
|
+
await (onEmbedNode == null ? void 0 : onEmbedNode(clone));
|
|
1483
|
+
const svg = createForeignObjectSvg(clone, context);
|
|
1484
|
+
svgDefsElement && svg.insertBefore(svgDefsElement, svg.children[0]);
|
|
1485
|
+
svgStyleElement && svg.insertBefore(svgStyleElement, svg.children[0]);
|
|
1486
|
+
autoDestruct && destroyContext(context);
|
|
1487
|
+
await (onCreateForeignObjectSvg == null ? void 0 : onCreateForeignObjectSvg(svg));
|
|
1488
|
+
return svg;
|
|
684
1489
|
}
|
|
685
|
-
function
|
|
686
|
-
|
|
1490
|
+
function createForeignObjectSvg(clone, context) {
|
|
1491
|
+
const { width, height } = context;
|
|
1492
|
+
const svg = createSvg(width, height, clone.ownerDocument);
|
|
1493
|
+
const foreignObject = svg.ownerDocument.createElementNS(svg.namespaceURI, "foreignObject");
|
|
1494
|
+
foreignObject.setAttributeNS(null, "x", "0%");
|
|
1495
|
+
foreignObject.setAttributeNS(null, "y", "0%");
|
|
1496
|
+
foreignObject.setAttributeNS(null, "width", "100%");
|
|
1497
|
+
foreignObject.setAttributeNS(null, "height", "100%");
|
|
1498
|
+
foreignObject.append(clone);
|
|
1499
|
+
svg.appendChild(foreignObject);
|
|
1500
|
+
return svg;
|
|
687
1501
|
}
|
|
688
|
-
async function
|
|
689
|
-
|
|
690
|
-
|
|
1502
|
+
async function domToCanvas(node, options) {
|
|
1503
|
+
var _a2;
|
|
1504
|
+
const context = await orCreateContext(node, options);
|
|
1505
|
+
const svg = await domToForeignObjectSvg(context);
|
|
1506
|
+
const dataUrl = svgToDataUrl(svg, context.isEnable("removeControlCharacter"));
|
|
1507
|
+
if (!context.autoDestruct) {
|
|
1508
|
+
context.svgStyleElement = createStyleElement(context.ownerDocument);
|
|
1509
|
+
context.svgDefsElement = (_a2 = context.ownerDocument) == null ? void 0 : _a2.createElementNS(XMLNS, "defs");
|
|
1510
|
+
context.svgStyles.clear();
|
|
691
1511
|
}
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
return getWebFontRules(cssRules);
|
|
695
|
-
}
|
|
696
|
-
function normalizeFontFamily(font) {
|
|
697
|
-
return font.trim().replace(/["']/g, "");
|
|
1512
|
+
const image = createImage(dataUrl, svg.ownerDocument);
|
|
1513
|
+
return await imageToCanvas(image, context);
|
|
698
1514
|
}
|
|
699
|
-
function
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
1515
|
+
async function domToDataUrl(node, options) {
|
|
1516
|
+
const context = await orCreateContext(node, options);
|
|
1517
|
+
const { log: log2, quality, type, dpi } = context;
|
|
1518
|
+
const canvas = await domToCanvas(context);
|
|
1519
|
+
log2.time("canvas to data url");
|
|
1520
|
+
let dataUrl = canvas.toDataURL(type, quality);
|
|
1521
|
+
if (["image/png", "image/jpeg"].includes(type) && dpi && SUPPORT_ATOB && SUPPORT_BTOA) {
|
|
1522
|
+
const [format, body] = dataUrl.split(",");
|
|
1523
|
+
let headerLength = 0;
|
|
1524
|
+
let overwritepHYs = false;
|
|
1525
|
+
if (type === "image/png") {
|
|
1526
|
+
const b64Index = detectPhysChunkFromDataUrl(body);
|
|
1527
|
+
if (b64Index >= 0) {
|
|
1528
|
+
headerLength = Math.ceil((b64Index + 28) / 3) * 4;
|
|
1529
|
+
overwritepHYs = true;
|
|
1530
|
+
} else {
|
|
1531
|
+
headerLength = 33 / 3 * 4;
|
|
709
1532
|
}
|
|
710
|
-
})
|
|
1533
|
+
} else if (type === "image/jpeg") {
|
|
1534
|
+
headerLength = 18 / 3 * 4;
|
|
1535
|
+
}
|
|
1536
|
+
const stringHeader = body.substring(0, headerLength);
|
|
1537
|
+
const restOfData = body.substring(headerLength);
|
|
1538
|
+
const headerBytes = window.atob(stringHeader);
|
|
1539
|
+
const uint8Array = new Uint8Array(headerBytes.length);
|
|
1540
|
+
for (let i = 0; i < uint8Array.length; i++) {
|
|
1541
|
+
uint8Array[i] = headerBytes.charCodeAt(i);
|
|
1542
|
+
}
|
|
1543
|
+
const finalArray = type === "image/png" ? changePngDpi(uint8Array, dpi, overwritepHYs) : changeJpegDpi(uint8Array, dpi);
|
|
1544
|
+
const base64Header = window.btoa(String.fromCharCode(...finalArray));
|
|
1545
|
+
dataUrl = [format, ",", base64Header, restOfData].join("");
|
|
711
1546
|
}
|
|
712
|
-
|
|
713
|
-
return
|
|
714
|
-
}
|
|
715
|
-
async function getWebFontCSS(node, options) {
|
|
716
|
-
const rules = await parseWebFontRules(node, options);
|
|
717
|
-
const usedFonts = getUsedFonts(node);
|
|
718
|
-
const cssTexts = await Promise.all(rules.filter((rule) => usedFonts.has(normalizeFontFamily(rule.style.fontFamily))).map((rule) => {
|
|
719
|
-
const baseUrl = rule.parentStyleSheet ? rule.parentStyleSheet.href : null;
|
|
720
|
-
return embedResources(rule.cssText, baseUrl, options);
|
|
721
|
-
}));
|
|
722
|
-
return cssTexts.join("\n");
|
|
723
|
-
}
|
|
724
|
-
async function embedWebFonts(clonedNode, options) {
|
|
725
|
-
const cssText = options.fontEmbedCSS != null ? options.fontEmbedCSS : options.skipFonts ? null : await getWebFontCSS(clonedNode, options);
|
|
726
|
-
if (cssText) {
|
|
727
|
-
const styleNode = document.createElement("style");
|
|
728
|
-
const sytleContent = document.createTextNode(cssText);
|
|
729
|
-
styleNode.appendChild(sytleContent);
|
|
730
|
-
if (clonedNode.firstChild) {
|
|
731
|
-
clonedNode.insertBefore(styleNode, clonedNode.firstChild);
|
|
732
|
-
} else {
|
|
733
|
-
clonedNode.appendChild(styleNode);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
async function toSvg(node, options = {}) {
|
|
738
|
-
const { width, height } = getImageSize(node, options);
|
|
739
|
-
const clonedNode = await cloneNode(node, options, true);
|
|
740
|
-
await embedWebFonts(clonedNode, options);
|
|
741
|
-
await embedImages(clonedNode, options);
|
|
742
|
-
applyStyle(clonedNode, options);
|
|
743
|
-
const datauri = await nodeToDataURL(clonedNode, width, height);
|
|
744
|
-
return datauri;
|
|
745
|
-
}
|
|
746
|
-
async function toCanvas(node, options = {}) {
|
|
747
|
-
const { width, height } = getImageSize(node, options);
|
|
748
|
-
const svg = await toSvg(node, options);
|
|
749
|
-
const img = await createImage(svg);
|
|
750
|
-
const canvas = document.createElement("canvas");
|
|
751
|
-
const context = canvas.getContext("2d");
|
|
752
|
-
const ratio = options.pixelRatio || getPixelRatio();
|
|
753
|
-
const canvasWidth = options.canvasWidth || width;
|
|
754
|
-
const canvasHeight = options.canvasHeight || height;
|
|
755
|
-
canvas.width = canvasWidth * ratio;
|
|
756
|
-
canvas.height = canvasHeight * ratio;
|
|
757
|
-
if (!options.skipAutoScale) {
|
|
758
|
-
checkCanvasDimensions(canvas);
|
|
759
|
-
}
|
|
760
|
-
canvas.style.width = `${canvasWidth}`;
|
|
761
|
-
canvas.style.height = `${canvasHeight}`;
|
|
762
|
-
if (options.backgroundColor) {
|
|
763
|
-
context.fillStyle = options.backgroundColor;
|
|
764
|
-
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
765
|
-
}
|
|
766
|
-
context.drawImage(img, 0, 0, canvas.width, canvas.height);
|
|
767
|
-
return canvas;
|
|
1547
|
+
log2.timeEnd("canvas to data url");
|
|
1548
|
+
return dataUrl;
|
|
768
1549
|
}
|
|
769
|
-
async function
|
|
770
|
-
const
|
|
771
|
-
|
|
1550
|
+
async function domToSvg(node, options) {
|
|
1551
|
+
const context = await orCreateContext(node, options);
|
|
1552
|
+
const { width, height, ownerDocument } = context;
|
|
1553
|
+
const dataUrl = await domToDataUrl(context);
|
|
1554
|
+
const svg = createSvg(width, height, ownerDocument);
|
|
1555
|
+
const svgImage = svg.ownerDocument.createElementNS(svg.namespaceURI, "image");
|
|
1556
|
+
svgImage.setAttributeNS(null, "href", dataUrl);
|
|
1557
|
+
svgImage.setAttributeNS(null, "height", "100%");
|
|
1558
|
+
svgImage.setAttributeNS(null, "width", "100%");
|
|
1559
|
+
svg.appendChild(svgImage);
|
|
1560
|
+
return svgToDataUrl(svg, context.isEnable("removeControlCharacter"));
|
|
772
1561
|
}
|
|
773
|
-
async function
|
|
774
|
-
|
|
775
|
-
|
|
1562
|
+
async function domToJpeg(node, options) {
|
|
1563
|
+
return domToDataUrl(
|
|
1564
|
+
await orCreateContext(node, { ...options, type: "image/jpeg" })
|
|
1565
|
+
);
|
|
776
1566
|
}
|
|
777
|
-
function
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const numberPattern = /-(\d+)$/;
|
|
787
|
-
const match = proposedFilename.match(numberPattern);
|
|
788
|
-
if (match) {
|
|
789
|
-
const baseFilename = proposedFilename.replace(numberPattern, "");
|
|
790
|
-
let counter = parseInt(match[1], 10);
|
|
791
|
-
while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
|
|
792
|
-
counter++;
|
|
793
|
-
}
|
|
794
|
-
const newFilename = `${baseFilename}-${counter}${extension}`;
|
|
795
|
-
filenames.push(newFilename);
|
|
796
|
-
return newFilename;
|
|
797
|
-
} else {
|
|
798
|
-
const baseFilename = proposedFilename.replace(extension, "");
|
|
799
|
-
let counter = 2;
|
|
800
|
-
while (filenames.includes(`${baseFilename}-${counter}${extension}`)) {
|
|
801
|
-
counter++;
|
|
802
|
-
}
|
|
803
|
-
const newFilename = `${baseFilename}-${counter}${extension}`;
|
|
804
|
-
filenames.push(newFilename);
|
|
805
|
-
return newFilename;
|
|
806
|
-
}
|
|
1567
|
+
async function domToPng(node, options) {
|
|
1568
|
+
return domToDataUrl(
|
|
1569
|
+
await orCreateContext(node, { ...options, type: "image/png" })
|
|
1570
|
+
);
|
|
1571
|
+
}
|
|
1572
|
+
async function domToWebp(node, options) {
|
|
1573
|
+
return domToDataUrl(
|
|
1574
|
+
await orCreateContext(node, { ...options, type: "image/webp" })
|
|
1575
|
+
);
|
|
807
1576
|
}
|
|
808
1577
|
async function captureElement(element, imageOptions, filenames) {
|
|
809
1578
|
try {
|
|
@@ -812,7 +1581,7 @@ async function captureElement(element, imageOptions, filenames) {
|
|
|
812
1581
|
// Ensure quality is a number
|
|
813
1582
|
quality: imageOptions.quality,
|
|
814
1583
|
// Ensure scale is a number
|
|
815
|
-
|
|
1584
|
+
scale: imageOptions.scale,
|
|
816
1585
|
// Ignores elements with data-ignore-capture attribute
|
|
817
1586
|
filter
|
|
818
1587
|
};
|
|
@@ -826,13 +1595,16 @@ async function captureElement(element, imageOptions, filenames) {
|
|
|
826
1595
|
}
|
|
827
1596
|
switch (imageOptions.format) {
|
|
828
1597
|
case "jpg":
|
|
829
|
-
dataURL = await
|
|
1598
|
+
dataURL = await domToJpeg(element, htmlToImageOptions);
|
|
830
1599
|
break;
|
|
831
1600
|
case "png":
|
|
832
|
-
dataURL = await
|
|
1601
|
+
dataURL = await domToPng(element, htmlToImageOptions);
|
|
833
1602
|
break;
|
|
834
1603
|
case "svg":
|
|
835
|
-
dataURL = await
|
|
1604
|
+
dataURL = await domToSvg(element, htmlToImageOptions);
|
|
1605
|
+
break;
|
|
1606
|
+
case "webp":
|
|
1607
|
+
dataURL = await domToWebp(element, htmlToImageOptions);
|
|
836
1608
|
break;
|
|
837
1609
|
}
|
|
838
1610
|
if (cleanUpBackground) {
|