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