frontbacked-svg 5.0.1 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +172 -16
- package/dist/main.mjs +172 -16
- package/package.json +1 -1
- package/src/base64Image.ts +100 -100
- package/src/cssParser.ts +58 -58
- package/src/filters.ts +225 -225
- package/src/font-utils.ts +192 -192
- package/src/get-default-fields-value.ts +63 -63
- package/src/getSvg.ts +476 -284
- package/src/imageHelper.ts +295 -293
- package/src/imagePassportUtils.ts +699 -699
- package/src/images-processor.ts +179 -179
- package/src/polyfills/DOMParser.ts +27 -27
- package/src/polyfills/File.ts +19 -19
- package/src/polyfills/Image.ts +89 -89
- package/src/svgScaler.ts +178 -178
- package/src/textGenCodeParser.ts +318 -318
- package/src/time.ts +146 -146
- package/src/toolsFunc.ts +78 -78
- package/src/types.ts +176 -176
- package/src/utils.ts +757 -757
- package/src/watermaker.ts +189 -189
package/dist/main.cjs
CHANGED
|
@@ -431,6 +431,7 @@ __name(reduceBase64Image, "reduceBase64Image");
|
|
|
431
431
|
var loadImageWithTimeout = /* @__PURE__ */ __name((src, timeout = 5e3) => {
|
|
432
432
|
return new Promise((resolve, reject) => {
|
|
433
433
|
const img = new Image();
|
|
434
|
+
if (!isNode4 && !src.startsWith("data:")) img.crossOrigin = "anonymous";
|
|
434
435
|
const timer = setTimeout(() => {
|
|
435
436
|
reject(new Error(`Image load timeout: ${src}`));
|
|
436
437
|
}, timeout);
|
|
@@ -2372,16 +2373,173 @@ __name(createFontThumbnail, "createFontThumbnail");
|
|
|
2372
2373
|
|
|
2373
2374
|
// src/getSvg.ts
|
|
2374
2375
|
var import_jspdf = __toESM(require("jspdf"), 1);
|
|
2375
|
-
var
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2376
|
+
var getPdfImageFormat = /* @__PURE__ */ __name((image) => {
|
|
2377
|
+
return image.startsWith("data:image/jpeg") || image.startsWith("data:image/jpg") ? "JPEG" : "PNG";
|
|
2378
|
+
}, "getPdfImageFormat");
|
|
2379
|
+
var dataUrlToBlob = /* @__PURE__ */ __name((dataUrl) => {
|
|
2380
|
+
const [header, data] = dataUrl.split(",");
|
|
2381
|
+
const mimeType = header.match(/:(.*?);/)?.[1] || "application/octet-stream";
|
|
2382
|
+
const binary = atob(data);
|
|
2383
|
+
const bytes = new Uint8Array(binary.length);
|
|
2384
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
2385
|
+
bytes[index] = binary.charCodeAt(index);
|
|
2386
|
+
}
|
|
2387
|
+
return new Blob([bytes], { type: mimeType });
|
|
2388
|
+
}, "dataUrlToBlob");
|
|
2389
|
+
var isAppleMobileDevice = /* @__PURE__ */ __name(() => {
|
|
2390
|
+
if (isNode5 || typeof navigator === "undefined") return false;
|
|
2391
|
+
const userAgent = navigator.userAgent || "";
|
|
2392
|
+
const platform = navigator.platform || "";
|
|
2393
|
+
const maxTouchPoints = navigator.maxTouchPoints || 0;
|
|
2394
|
+
return /iPad|iPhone|iPod/i.test(userAgent) || platform === "MacIntel" && maxTouchPoints > 1;
|
|
2395
|
+
}, "isAppleMobileDevice");
|
|
2396
|
+
var openBlobInBrowser = /* @__PURE__ */ __name((blob, fileName, openInNewTab = false) => {
|
|
2397
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
2398
|
+
const downloadLink = document.createElement("a");
|
|
2399
|
+
downloadLink.href = objectUrl;
|
|
2400
|
+
downloadLink.rel = "noopener";
|
|
2401
|
+
downloadLink.style.display = "none";
|
|
2402
|
+
if (openInNewTab) {
|
|
2403
|
+
downloadLink.target = "_blank";
|
|
2404
|
+
} else {
|
|
2405
|
+
downloadLink.download = fileName;
|
|
2406
|
+
}
|
|
2407
|
+
document.body.appendChild(downloadLink);
|
|
2408
|
+
downloadLink.click();
|
|
2409
|
+
document.body.removeChild(downloadLink);
|
|
2410
|
+
setTimeout(() => URL.revokeObjectURL(objectUrl), 3e4);
|
|
2411
|
+
}, "openBlobInBrowser");
|
|
2412
|
+
var shareBlob = /* @__PURE__ */ __name(async (blob, fileName) => {
|
|
2413
|
+
const shareApi = typeof navigator !== "undefined" ? navigator.share : null;
|
|
2414
|
+
const canShare = typeof navigator !== "undefined" ? navigator.canShare : null;
|
|
2415
|
+
if (!shareApi || typeof File === "undefined") return false;
|
|
2416
|
+
const file = new File([blob], fileName, { type: blob.type || "application/octet-stream" });
|
|
2417
|
+
const shareData = { files: [file], title: fileName };
|
|
2418
|
+
if (canShare && !canShare.call(navigator, shareData)) return false;
|
|
2419
|
+
await shareApi.call(navigator, shareData);
|
|
2420
|
+
return true;
|
|
2421
|
+
}, "shareBlob");
|
|
2422
|
+
var promptAppleMobileSave = /* @__PURE__ */ __name((blob, fileName) => {
|
|
2423
|
+
return new Promise((resolve, reject) => {
|
|
2424
|
+
const overlay = document.createElement("div");
|
|
2425
|
+
overlay.setAttribute("role", "dialog");
|
|
2426
|
+
overlay.setAttribute("aria-modal", "true");
|
|
2427
|
+
overlay.style.cssText = [
|
|
2428
|
+
"position:fixed",
|
|
2429
|
+
"inset:0",
|
|
2430
|
+
"z-index:2147483647",
|
|
2431
|
+
"display:flex",
|
|
2432
|
+
"align-items:center",
|
|
2433
|
+
"justify-content:center",
|
|
2434
|
+
"padding:24px",
|
|
2435
|
+
"background:rgba(15,23,42,0.58)",
|
|
2436
|
+
"font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif"
|
|
2437
|
+
].join(";");
|
|
2438
|
+
const dialog = document.createElement("div");
|
|
2439
|
+
dialog.style.cssText = [
|
|
2440
|
+
"width:min(360px,100%)",
|
|
2441
|
+
"border-radius:12px",
|
|
2442
|
+
"background:#fff",
|
|
2443
|
+
"box-shadow:0 20px 60px rgba(15,23,42,0.3)",
|
|
2444
|
+
"padding:18px",
|
|
2445
|
+
"color:#111827",
|
|
2446
|
+
"text-align:left"
|
|
2447
|
+
].join(";");
|
|
2448
|
+
const title = document.createElement("div");
|
|
2449
|
+
title.textContent = "File ready";
|
|
2450
|
+
title.style.cssText = "font-size:17px;font-weight:700;margin-bottom:8px;";
|
|
2451
|
+
const body = document.createElement("div");
|
|
2452
|
+
body.textContent = "Choose where to save this file.";
|
|
2453
|
+
body.style.cssText = "font-size:14px;line-height:1.45;color:#4b5563;margin-bottom:16px;";
|
|
2454
|
+
const actions = document.createElement("div");
|
|
2455
|
+
actions.style.cssText = "display:flex;gap:10px;justify-content:flex-end;";
|
|
2456
|
+
const cancelButton = document.createElement("button");
|
|
2457
|
+
cancelButton.type = "button";
|
|
2458
|
+
cancelButton.textContent = "Cancel";
|
|
2459
|
+
cancelButton.style.cssText = [
|
|
2460
|
+
"border:0",
|
|
2461
|
+
"border-radius:8px",
|
|
2462
|
+
"padding:10px 14px",
|
|
2463
|
+
"background:#f3f4f6",
|
|
2464
|
+
"color:#111827",
|
|
2465
|
+
"font-size:14px",
|
|
2466
|
+
"font-weight:600"
|
|
2467
|
+
].join(";");
|
|
2468
|
+
const saveButton = document.createElement("button");
|
|
2469
|
+
saveButton.type = "button";
|
|
2470
|
+
saveButton.textContent = "Save File";
|
|
2471
|
+
saveButton.style.cssText = [
|
|
2472
|
+
"border:0",
|
|
2473
|
+
"border-radius:8px",
|
|
2474
|
+
"padding:10px 14px",
|
|
2475
|
+
"background:#2563eb",
|
|
2476
|
+
"color:#fff",
|
|
2477
|
+
"font-size:14px",
|
|
2478
|
+
"font-weight:700"
|
|
2479
|
+
].join(";");
|
|
2480
|
+
const cleanup = /* @__PURE__ */ __name(() => {
|
|
2481
|
+
document.body.removeChild(overlay);
|
|
2482
|
+
}, "cleanup");
|
|
2483
|
+
cancelButton.onclick = () => {
|
|
2484
|
+
cleanup();
|
|
2485
|
+
reject(new Error("File save was cancelled."));
|
|
2486
|
+
};
|
|
2487
|
+
saveButton.onclick = async () => {
|
|
2488
|
+
saveButton.setAttribute("disabled", "true");
|
|
2489
|
+
try {
|
|
2490
|
+
const shared = await shareBlob(blob, fileName);
|
|
2491
|
+
if (!shared) openBlobInBrowser(blob, fileName, true);
|
|
2492
|
+
cleanup();
|
|
2493
|
+
resolve();
|
|
2494
|
+
} catch (error) {
|
|
2495
|
+
cleanup();
|
|
2496
|
+
if (error?.name === "AbortError") {
|
|
2497
|
+
reject(new Error("File save was cancelled."));
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
openBlobInBrowser(blob, fileName, true);
|
|
2501
|
+
resolve();
|
|
2502
|
+
}
|
|
2503
|
+
};
|
|
2504
|
+
actions.appendChild(cancelButton);
|
|
2505
|
+
actions.appendChild(saveButton);
|
|
2506
|
+
dialog.appendChild(title);
|
|
2507
|
+
dialog.appendChild(body);
|
|
2508
|
+
dialog.appendChild(actions);
|
|
2509
|
+
overlay.appendChild(dialog);
|
|
2510
|
+
document.body.appendChild(overlay);
|
|
2381
2511
|
});
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2512
|
+
}, "promptAppleMobileSave");
|
|
2513
|
+
var downloadBlobInBrowser = /* @__PURE__ */ __name(async (blob, fileName) => {
|
|
2514
|
+
if (isNode5) return;
|
|
2515
|
+
if (isAppleMobileDevice()) {
|
|
2516
|
+
await promptAppleMobileSave(blob, fileName);
|
|
2517
|
+
return;
|
|
2518
|
+
}
|
|
2519
|
+
openBlobInBrowser(blob, fileName);
|
|
2520
|
+
}, "downloadBlobInBrowser");
|
|
2521
|
+
var downloadDataUrlInBrowser = /* @__PURE__ */ __name(async (dataUrl, fileName) => {
|
|
2522
|
+
await downloadBlobInBrowser(dataUrlToBlob(dataUrl), fileName);
|
|
2523
|
+
}, "downloadDataUrlInBrowser");
|
|
2524
|
+
var pngToPdf = /* @__PURE__ */ __name(async (image, filename, resolve, reject, skipBrowserDownload) => {
|
|
2525
|
+
try {
|
|
2526
|
+
const size = await getImageDimension(image);
|
|
2527
|
+
const pdfDoc = new import_jspdf.default({
|
|
2528
|
+
orientation: size.width >= size.height ? "landscape" : "portrait",
|
|
2529
|
+
unit: "pt",
|
|
2530
|
+
format: [size.width, size.height],
|
|
2531
|
+
compress: true
|
|
2532
|
+
});
|
|
2533
|
+
pdfDoc.addImage(image, getPdfImageFormat(image), 0, 0, size.width, size.height, void 0, "FAST");
|
|
2534
|
+
const pdfDataUrl = pdfDoc.output("datauristring");
|
|
2535
|
+
if (!isNode5 && !skipBrowserDownload) {
|
|
2536
|
+
const pdfBlob = new Blob([pdfDoc.output("arraybuffer")], { type: "application/pdf" });
|
|
2537
|
+
await downloadBlobInBrowser(pdfBlob, filename);
|
|
2538
|
+
}
|
|
2539
|
+
resolve(pdfDataUrl);
|
|
2540
|
+
} catch (error) {
|
|
2541
|
+
reject(error);
|
|
2542
|
+
}
|
|
2385
2543
|
}, "pngToPdf");
|
|
2386
2544
|
var isNode5 = typeof window === "undefined";
|
|
2387
2545
|
var getNodeImageDimensionAttr = /* @__PURE__ */ __name((img, attr) => {
|
|
@@ -2394,6 +2552,7 @@ var getNodeImageDimensionAttr = /* @__PURE__ */ __name((img, attr) => {
|
|
|
2394
2552
|
async function processBase64Image(base64Image, side, format = "png") {
|
|
2395
2553
|
return new Promise((resolve, reject) => {
|
|
2396
2554
|
const img = new Image();
|
|
2555
|
+
if (!isNode5) img.crossOrigin = "anonymous";
|
|
2397
2556
|
img.onload = () => {
|
|
2398
2557
|
const canvas = getCanvas(img.width, img.height);
|
|
2399
2558
|
const ctx = canvas.getContext("2d");
|
|
@@ -2449,16 +2608,13 @@ var downloadSvgAsImage = /* @__PURE__ */ __name((svgString, format = "png", file
|
|
|
2449
2608
|
let canvasHeight = img.height * scaleFactor;
|
|
2450
2609
|
canvas.width = canvasWidth;
|
|
2451
2610
|
canvas.height = canvasHeight;
|
|
2452
|
-
const downloadImage = /* @__PURE__ */ __name((dataUrl2, sideLabel) => {
|
|
2611
|
+
const downloadImage = /* @__PURE__ */ __name(async (dataUrl2, sideLabel) => {
|
|
2453
2612
|
const fileFullname = sideLabel ? `${sideLabel.toUpperCase()}_${fileName}.${format}` : `${fileName}.${format}`;
|
|
2454
2613
|
if (format === "pdf") {
|
|
2455
|
-
pngToPdf(dataUrl2, fileFullname, resolve, reject);
|
|
2614
|
+
await pngToPdf(dataUrl2, fileFullname, resolve, reject, skipBrowserDownload);
|
|
2456
2615
|
} else {
|
|
2457
2616
|
if (!isNode5 && !skipBrowserDownload) {
|
|
2458
|
-
|
|
2459
|
-
downloadLink.href = dataUrl2;
|
|
2460
|
-
downloadLink.download = fileFullname;
|
|
2461
|
-
downloadLink.click();
|
|
2617
|
+
await downloadDataUrlInBrowser(dataUrl2, fileFullname);
|
|
2462
2618
|
}
|
|
2463
2619
|
resolve(dataUrl2);
|
|
2464
2620
|
}
|
|
@@ -2506,7 +2662,7 @@ var downloadSvgAsImage = /* @__PURE__ */ __name((svgString, format = "png", file
|
|
|
2506
2662
|
var dataUrl = canvas.toDataURL(`image/png`);
|
|
2507
2663
|
dataUrl = await processBase64Image(dataUrl, downloadSide, format);
|
|
2508
2664
|
}
|
|
2509
|
-
downloadImage(dataUrl, downloadSide);
|
|
2665
|
+
await downloadImage(dataUrl, downloadSide);
|
|
2510
2666
|
} else {
|
|
2511
2667
|
reject(new Error("Unsupported Browser. Please try to download from another browser."));
|
|
2512
2668
|
}
|
package/dist/main.mjs
CHANGED
|
@@ -328,6 +328,7 @@ __name(reduceBase64Image, "reduceBase64Image");
|
|
|
328
328
|
var loadImageWithTimeout = /* @__PURE__ */ __name((src, timeout = 5e3) => {
|
|
329
329
|
return new Promise((resolve, reject) => {
|
|
330
330
|
const img = new Image();
|
|
331
|
+
if (!isNode4 && !src.startsWith("data:")) img.crossOrigin = "anonymous";
|
|
331
332
|
const timer = setTimeout(() => {
|
|
332
333
|
reject(new Error(`Image load timeout: ${src}`));
|
|
333
334
|
}, timeout);
|
|
@@ -2269,16 +2270,173 @@ __name(createFontThumbnail, "createFontThumbnail");
|
|
|
2269
2270
|
|
|
2270
2271
|
// src/getSvg.ts
|
|
2271
2272
|
import jsPDF from "jspdf";
|
|
2272
|
-
var
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2273
|
+
var getPdfImageFormat = /* @__PURE__ */ __name((image) => {
|
|
2274
|
+
return image.startsWith("data:image/jpeg") || image.startsWith("data:image/jpg") ? "JPEG" : "PNG";
|
|
2275
|
+
}, "getPdfImageFormat");
|
|
2276
|
+
var dataUrlToBlob = /* @__PURE__ */ __name((dataUrl) => {
|
|
2277
|
+
const [header, data] = dataUrl.split(",");
|
|
2278
|
+
const mimeType = header.match(/:(.*?);/)?.[1] || "application/octet-stream";
|
|
2279
|
+
const binary = atob(data);
|
|
2280
|
+
const bytes = new Uint8Array(binary.length);
|
|
2281
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
2282
|
+
bytes[index] = binary.charCodeAt(index);
|
|
2283
|
+
}
|
|
2284
|
+
return new Blob([bytes], { type: mimeType });
|
|
2285
|
+
}, "dataUrlToBlob");
|
|
2286
|
+
var isAppleMobileDevice = /* @__PURE__ */ __name(() => {
|
|
2287
|
+
if (isNode5 || typeof navigator === "undefined") return false;
|
|
2288
|
+
const userAgent = navigator.userAgent || "";
|
|
2289
|
+
const platform = navigator.platform || "";
|
|
2290
|
+
const maxTouchPoints = navigator.maxTouchPoints || 0;
|
|
2291
|
+
return /iPad|iPhone|iPod/i.test(userAgent) || platform === "MacIntel" && maxTouchPoints > 1;
|
|
2292
|
+
}, "isAppleMobileDevice");
|
|
2293
|
+
var openBlobInBrowser = /* @__PURE__ */ __name((blob, fileName, openInNewTab = false) => {
|
|
2294
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
2295
|
+
const downloadLink = document.createElement("a");
|
|
2296
|
+
downloadLink.href = objectUrl;
|
|
2297
|
+
downloadLink.rel = "noopener";
|
|
2298
|
+
downloadLink.style.display = "none";
|
|
2299
|
+
if (openInNewTab) {
|
|
2300
|
+
downloadLink.target = "_blank";
|
|
2301
|
+
} else {
|
|
2302
|
+
downloadLink.download = fileName;
|
|
2303
|
+
}
|
|
2304
|
+
document.body.appendChild(downloadLink);
|
|
2305
|
+
downloadLink.click();
|
|
2306
|
+
document.body.removeChild(downloadLink);
|
|
2307
|
+
setTimeout(() => URL.revokeObjectURL(objectUrl), 3e4);
|
|
2308
|
+
}, "openBlobInBrowser");
|
|
2309
|
+
var shareBlob = /* @__PURE__ */ __name(async (blob, fileName) => {
|
|
2310
|
+
const shareApi = typeof navigator !== "undefined" ? navigator.share : null;
|
|
2311
|
+
const canShare = typeof navigator !== "undefined" ? navigator.canShare : null;
|
|
2312
|
+
if (!shareApi || typeof File === "undefined") return false;
|
|
2313
|
+
const file = new File([blob], fileName, { type: blob.type || "application/octet-stream" });
|
|
2314
|
+
const shareData = { files: [file], title: fileName };
|
|
2315
|
+
if (canShare && !canShare.call(navigator, shareData)) return false;
|
|
2316
|
+
await shareApi.call(navigator, shareData);
|
|
2317
|
+
return true;
|
|
2318
|
+
}, "shareBlob");
|
|
2319
|
+
var promptAppleMobileSave = /* @__PURE__ */ __name((blob, fileName) => {
|
|
2320
|
+
return new Promise((resolve, reject) => {
|
|
2321
|
+
const overlay = document.createElement("div");
|
|
2322
|
+
overlay.setAttribute("role", "dialog");
|
|
2323
|
+
overlay.setAttribute("aria-modal", "true");
|
|
2324
|
+
overlay.style.cssText = [
|
|
2325
|
+
"position:fixed",
|
|
2326
|
+
"inset:0",
|
|
2327
|
+
"z-index:2147483647",
|
|
2328
|
+
"display:flex",
|
|
2329
|
+
"align-items:center",
|
|
2330
|
+
"justify-content:center",
|
|
2331
|
+
"padding:24px",
|
|
2332
|
+
"background:rgba(15,23,42,0.58)",
|
|
2333
|
+
"font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif"
|
|
2334
|
+
].join(";");
|
|
2335
|
+
const dialog = document.createElement("div");
|
|
2336
|
+
dialog.style.cssText = [
|
|
2337
|
+
"width:min(360px,100%)",
|
|
2338
|
+
"border-radius:12px",
|
|
2339
|
+
"background:#fff",
|
|
2340
|
+
"box-shadow:0 20px 60px rgba(15,23,42,0.3)",
|
|
2341
|
+
"padding:18px",
|
|
2342
|
+
"color:#111827",
|
|
2343
|
+
"text-align:left"
|
|
2344
|
+
].join(";");
|
|
2345
|
+
const title = document.createElement("div");
|
|
2346
|
+
title.textContent = "File ready";
|
|
2347
|
+
title.style.cssText = "font-size:17px;font-weight:700;margin-bottom:8px;";
|
|
2348
|
+
const body = document.createElement("div");
|
|
2349
|
+
body.textContent = "Choose where to save this file.";
|
|
2350
|
+
body.style.cssText = "font-size:14px;line-height:1.45;color:#4b5563;margin-bottom:16px;";
|
|
2351
|
+
const actions = document.createElement("div");
|
|
2352
|
+
actions.style.cssText = "display:flex;gap:10px;justify-content:flex-end;";
|
|
2353
|
+
const cancelButton = document.createElement("button");
|
|
2354
|
+
cancelButton.type = "button";
|
|
2355
|
+
cancelButton.textContent = "Cancel";
|
|
2356
|
+
cancelButton.style.cssText = [
|
|
2357
|
+
"border:0",
|
|
2358
|
+
"border-radius:8px",
|
|
2359
|
+
"padding:10px 14px",
|
|
2360
|
+
"background:#f3f4f6",
|
|
2361
|
+
"color:#111827",
|
|
2362
|
+
"font-size:14px",
|
|
2363
|
+
"font-weight:600"
|
|
2364
|
+
].join(";");
|
|
2365
|
+
const saveButton = document.createElement("button");
|
|
2366
|
+
saveButton.type = "button";
|
|
2367
|
+
saveButton.textContent = "Save File";
|
|
2368
|
+
saveButton.style.cssText = [
|
|
2369
|
+
"border:0",
|
|
2370
|
+
"border-radius:8px",
|
|
2371
|
+
"padding:10px 14px",
|
|
2372
|
+
"background:#2563eb",
|
|
2373
|
+
"color:#fff",
|
|
2374
|
+
"font-size:14px",
|
|
2375
|
+
"font-weight:700"
|
|
2376
|
+
].join(";");
|
|
2377
|
+
const cleanup = /* @__PURE__ */ __name(() => {
|
|
2378
|
+
document.body.removeChild(overlay);
|
|
2379
|
+
}, "cleanup");
|
|
2380
|
+
cancelButton.onclick = () => {
|
|
2381
|
+
cleanup();
|
|
2382
|
+
reject(new Error("File save was cancelled."));
|
|
2383
|
+
};
|
|
2384
|
+
saveButton.onclick = async () => {
|
|
2385
|
+
saveButton.setAttribute("disabled", "true");
|
|
2386
|
+
try {
|
|
2387
|
+
const shared = await shareBlob(blob, fileName);
|
|
2388
|
+
if (!shared) openBlobInBrowser(blob, fileName, true);
|
|
2389
|
+
cleanup();
|
|
2390
|
+
resolve();
|
|
2391
|
+
} catch (error) {
|
|
2392
|
+
cleanup();
|
|
2393
|
+
if (error?.name === "AbortError") {
|
|
2394
|
+
reject(new Error("File save was cancelled."));
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2397
|
+
openBlobInBrowser(blob, fileName, true);
|
|
2398
|
+
resolve();
|
|
2399
|
+
}
|
|
2400
|
+
};
|
|
2401
|
+
actions.appendChild(cancelButton);
|
|
2402
|
+
actions.appendChild(saveButton);
|
|
2403
|
+
dialog.appendChild(title);
|
|
2404
|
+
dialog.appendChild(body);
|
|
2405
|
+
dialog.appendChild(actions);
|
|
2406
|
+
overlay.appendChild(dialog);
|
|
2407
|
+
document.body.appendChild(overlay);
|
|
2278
2408
|
});
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2409
|
+
}, "promptAppleMobileSave");
|
|
2410
|
+
var downloadBlobInBrowser = /* @__PURE__ */ __name(async (blob, fileName) => {
|
|
2411
|
+
if (isNode5) return;
|
|
2412
|
+
if (isAppleMobileDevice()) {
|
|
2413
|
+
await promptAppleMobileSave(blob, fileName);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
openBlobInBrowser(blob, fileName);
|
|
2417
|
+
}, "downloadBlobInBrowser");
|
|
2418
|
+
var downloadDataUrlInBrowser = /* @__PURE__ */ __name(async (dataUrl, fileName) => {
|
|
2419
|
+
await downloadBlobInBrowser(dataUrlToBlob(dataUrl), fileName);
|
|
2420
|
+
}, "downloadDataUrlInBrowser");
|
|
2421
|
+
var pngToPdf = /* @__PURE__ */ __name(async (image, filename, resolve, reject, skipBrowserDownload) => {
|
|
2422
|
+
try {
|
|
2423
|
+
const size = await getImageDimension(image);
|
|
2424
|
+
const pdfDoc = new jsPDF({
|
|
2425
|
+
orientation: size.width >= size.height ? "landscape" : "portrait",
|
|
2426
|
+
unit: "pt",
|
|
2427
|
+
format: [size.width, size.height],
|
|
2428
|
+
compress: true
|
|
2429
|
+
});
|
|
2430
|
+
pdfDoc.addImage(image, getPdfImageFormat(image), 0, 0, size.width, size.height, void 0, "FAST");
|
|
2431
|
+
const pdfDataUrl = pdfDoc.output("datauristring");
|
|
2432
|
+
if (!isNode5 && !skipBrowserDownload) {
|
|
2433
|
+
const pdfBlob = new Blob([pdfDoc.output("arraybuffer")], { type: "application/pdf" });
|
|
2434
|
+
await downloadBlobInBrowser(pdfBlob, filename);
|
|
2435
|
+
}
|
|
2436
|
+
resolve(pdfDataUrl);
|
|
2437
|
+
} catch (error) {
|
|
2438
|
+
reject(error);
|
|
2439
|
+
}
|
|
2282
2440
|
}, "pngToPdf");
|
|
2283
2441
|
var isNode5 = typeof window === "undefined";
|
|
2284
2442
|
var getNodeImageDimensionAttr = /* @__PURE__ */ __name((img, attr) => {
|
|
@@ -2291,6 +2449,7 @@ var getNodeImageDimensionAttr = /* @__PURE__ */ __name((img, attr) => {
|
|
|
2291
2449
|
async function processBase64Image(base64Image, side, format = "png") {
|
|
2292
2450
|
return new Promise((resolve, reject) => {
|
|
2293
2451
|
const img = new Image();
|
|
2452
|
+
if (!isNode5) img.crossOrigin = "anonymous";
|
|
2294
2453
|
img.onload = () => {
|
|
2295
2454
|
const canvas = getCanvas(img.width, img.height);
|
|
2296
2455
|
const ctx = canvas.getContext("2d");
|
|
@@ -2346,16 +2505,13 @@ var downloadSvgAsImage = /* @__PURE__ */ __name((svgString, format = "png", file
|
|
|
2346
2505
|
let canvasHeight = img.height * scaleFactor;
|
|
2347
2506
|
canvas.width = canvasWidth;
|
|
2348
2507
|
canvas.height = canvasHeight;
|
|
2349
|
-
const downloadImage = /* @__PURE__ */ __name((dataUrl2, sideLabel) => {
|
|
2508
|
+
const downloadImage = /* @__PURE__ */ __name(async (dataUrl2, sideLabel) => {
|
|
2350
2509
|
const fileFullname = sideLabel ? `${sideLabel.toUpperCase()}_${fileName}.${format}` : `${fileName}.${format}`;
|
|
2351
2510
|
if (format === "pdf") {
|
|
2352
|
-
pngToPdf(dataUrl2, fileFullname, resolve, reject);
|
|
2511
|
+
await pngToPdf(dataUrl2, fileFullname, resolve, reject, skipBrowserDownload);
|
|
2353
2512
|
} else {
|
|
2354
2513
|
if (!isNode5 && !skipBrowserDownload) {
|
|
2355
|
-
|
|
2356
|
-
downloadLink.href = dataUrl2;
|
|
2357
|
-
downloadLink.download = fileFullname;
|
|
2358
|
-
downloadLink.click();
|
|
2514
|
+
await downloadDataUrlInBrowser(dataUrl2, fileFullname);
|
|
2359
2515
|
}
|
|
2360
2516
|
resolve(dataUrl2);
|
|
2361
2517
|
}
|
|
@@ -2403,7 +2559,7 @@ var downloadSvgAsImage = /* @__PURE__ */ __name((svgString, format = "png", file
|
|
|
2403
2559
|
var dataUrl = canvas.toDataURL(`image/png`);
|
|
2404
2560
|
dataUrl = await processBase64Image(dataUrl, downloadSide, format);
|
|
2405
2561
|
}
|
|
2406
|
-
downloadImage(dataUrl, downloadSide);
|
|
2562
|
+
await downloadImage(dataUrl, downloadSide);
|
|
2407
2563
|
} else {
|
|
2408
2564
|
reject(new Error("Unsupported Browser. Please try to download from another browser."));
|
|
2409
2565
|
}
|
package/package.json
CHANGED
package/src/base64Image.ts
CHANGED
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import { ImageWrapper } from "./polyfills/Image.ts";
|
|
2
|
-
|
|
3
|
-
const isNode = typeof window === 'undefined';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Gets the dimensions (width and height) of an image from a base64 string.
|
|
7
|
-
*
|
|
8
|
-
* @param base64Image - The base64 string of the image.
|
|
9
|
-
* @returns A Promise that resolves to an object containing the base64 string, width, and height of the image.
|
|
10
|
-
*/
|
|
11
|
-
export function getImageDimensions(base64Image: string): Promise<{ width: number, height: number }> {
|
|
12
|
-
return new Promise<{ width: number, height: number }>((resolve, reject) => {
|
|
13
|
-
const img = new Image();
|
|
14
|
-
|
|
15
|
-
img.onload = () => {
|
|
16
|
-
resolve({
|
|
17
|
-
width: img.width,
|
|
18
|
-
height: img.height,
|
|
19
|
-
});
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
img.onerror = (error) => {
|
|
23
|
-
reject(new Error(`Failed to load image: ${error}`));
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
img.src = base64Image;
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Resizes a base64 image string to the given width while maintaining the aspect ratio.
|
|
32
|
-
*
|
|
33
|
-
* @param base64Image - The base64 string of the image.
|
|
34
|
-
* @param targetWidth - The desired width for the resized image.
|
|
35
|
-
* @returns A Promise that resolves to the resized image as a base64 string.
|
|
36
|
-
*/
|
|
37
|
-
export async function resizeBase64Image(base64Image: string, targetWidth: number): Promise<string> {
|
|
38
|
-
return new Promise<string>((resolve, reject) => {
|
|
39
|
-
const img = new Image();
|
|
40
|
-
|
|
41
|
-
// Set up the onload handler to resize the image once it's loaded
|
|
42
|
-
img.onload = () => {
|
|
43
|
-
const aspectRatio = img.width / img.height;
|
|
44
|
-
const targetHeight = targetWidth / aspectRatio;
|
|
45
|
-
|
|
46
|
-
// Create a canvas and draw the resized image on it
|
|
47
|
-
const canvas = document.createElement('canvas');
|
|
48
|
-
canvas.width = targetWidth;
|
|
49
|
-
canvas.height = targetHeight;
|
|
50
|
-
const ctx = canvas.getContext('2d') as any;
|
|
51
|
-
|
|
52
|
-
if (ctx) {
|
|
53
|
-
const realImage = ((img as any) as ImageWrapper).realImage? ((img as any) as ImageWrapper).realImage : img
|
|
54
|
-
ctx.drawImage(realImage, 0, 0, targetWidth, targetHeight);
|
|
55
|
-
|
|
56
|
-
// Convert the canvas to a base64 string and resolve the promise
|
|
57
|
-
const resizedBase64Image = canvas.toDataURL('image/png');
|
|
58
|
-
resolve(resizedBase64Image);
|
|
59
|
-
} else {
|
|
60
|
-
reject(new Error('Failed to get canvas context'));
|
|
61
|
-
}
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// Set up the onerror handler to reject the promise if the image fails to load
|
|
65
|
-
img.onerror = (error) => {
|
|
66
|
-
reject(new Error(`Failed to load image: ${error}`));
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// Set the src attribute to the base64 image string to start loading the image
|
|
70
|
-
img.src = base64Image;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function base64UrlToFile(base64Url: string, fileName?: string, fileType?: string): File {
|
|
75
|
-
// Extract base64 data and the MIME type from the header
|
|
76
|
-
const [header, base64Data] = base64Url.split(',');
|
|
77
|
-
|
|
78
|
-
// Infer the file type from the header if not provided
|
|
79
|
-
fileType = fileType || header.match(/:(.*?);/)?.[1];
|
|
80
|
-
|
|
81
|
-
// If the fileName is not provided, generate a random one with an appropriate extension
|
|
82
|
-
if (!fileName) {
|
|
83
|
-
const extension = fileType?.split('/')[1] || 'bin'; // Default to 'bin' if no type is inferred
|
|
84
|
-
fileName = `file_${Date.now()}.${extension}`;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Decode the base64 string to a binary string
|
|
88
|
-
const binaryString = window.atob(base64Data);
|
|
89
|
-
const len = binaryString.length;
|
|
90
|
-
const bytes = new Uint8Array(len);
|
|
91
|
-
|
|
92
|
-
for (let i = 0; i < len; i++) {
|
|
93
|
-
bytes[i] = binaryString.charCodeAt(i);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Create a Blob from the decoded data
|
|
97
|
-
const blob = new Blob([bytes], { type: fileType });
|
|
98
|
-
|
|
99
|
-
// Create and return a File from the Blob
|
|
100
|
-
return new File([blob], fileName, { type: fileType });
|
|
1
|
+
import { ImageWrapper } from "./polyfills/Image.ts";
|
|
2
|
+
|
|
3
|
+
const isNode = typeof window === 'undefined';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Gets the dimensions (width and height) of an image from a base64 string.
|
|
7
|
+
*
|
|
8
|
+
* @param base64Image - The base64 string of the image.
|
|
9
|
+
* @returns A Promise that resolves to an object containing the base64 string, width, and height of the image.
|
|
10
|
+
*/
|
|
11
|
+
export function getImageDimensions(base64Image: string): Promise<{ width: number, height: number }> {
|
|
12
|
+
return new Promise<{ width: number, height: number }>((resolve, reject) => {
|
|
13
|
+
const img = new Image();
|
|
14
|
+
|
|
15
|
+
img.onload = () => {
|
|
16
|
+
resolve({
|
|
17
|
+
width: img.width,
|
|
18
|
+
height: img.height,
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
img.onerror = (error) => {
|
|
23
|
+
reject(new Error(`Failed to load image: ${error}`));
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
img.src = base64Image;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Resizes a base64 image string to the given width while maintaining the aspect ratio.
|
|
32
|
+
*
|
|
33
|
+
* @param base64Image - The base64 string of the image.
|
|
34
|
+
* @param targetWidth - The desired width for the resized image.
|
|
35
|
+
* @returns A Promise that resolves to the resized image as a base64 string.
|
|
36
|
+
*/
|
|
37
|
+
export async function resizeBase64Image(base64Image: string, targetWidth: number): Promise<string> {
|
|
38
|
+
return new Promise<string>((resolve, reject) => {
|
|
39
|
+
const img = new Image();
|
|
40
|
+
|
|
41
|
+
// Set up the onload handler to resize the image once it's loaded
|
|
42
|
+
img.onload = () => {
|
|
43
|
+
const aspectRatio = img.width / img.height;
|
|
44
|
+
const targetHeight = targetWidth / aspectRatio;
|
|
45
|
+
|
|
46
|
+
// Create a canvas and draw the resized image on it
|
|
47
|
+
const canvas = document.createElement('canvas');
|
|
48
|
+
canvas.width = targetWidth;
|
|
49
|
+
canvas.height = targetHeight;
|
|
50
|
+
const ctx = canvas.getContext('2d') as any;
|
|
51
|
+
|
|
52
|
+
if (ctx) {
|
|
53
|
+
const realImage = ((img as any) as ImageWrapper).realImage? ((img as any) as ImageWrapper).realImage : img
|
|
54
|
+
ctx.drawImage(realImage, 0, 0, targetWidth, targetHeight);
|
|
55
|
+
|
|
56
|
+
// Convert the canvas to a base64 string and resolve the promise
|
|
57
|
+
const resizedBase64Image = canvas.toDataURL('image/png');
|
|
58
|
+
resolve(resizedBase64Image);
|
|
59
|
+
} else {
|
|
60
|
+
reject(new Error('Failed to get canvas context'));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Set up the onerror handler to reject the promise if the image fails to load
|
|
65
|
+
img.onerror = (error) => {
|
|
66
|
+
reject(new Error(`Failed to load image: ${error}`));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Set the src attribute to the base64 image string to start loading the image
|
|
70
|
+
img.src = base64Image;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function base64UrlToFile(base64Url: string, fileName?: string, fileType?: string): File {
|
|
75
|
+
// Extract base64 data and the MIME type from the header
|
|
76
|
+
const [header, base64Data] = base64Url.split(',');
|
|
77
|
+
|
|
78
|
+
// Infer the file type from the header if not provided
|
|
79
|
+
fileType = fileType || header.match(/:(.*?);/)?.[1];
|
|
80
|
+
|
|
81
|
+
// If the fileName is not provided, generate a random one with an appropriate extension
|
|
82
|
+
if (!fileName) {
|
|
83
|
+
const extension = fileType?.split('/')[1] || 'bin'; // Default to 'bin' if no type is inferred
|
|
84
|
+
fileName = `file_${Date.now()}.${extension}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Decode the base64 string to a binary string
|
|
88
|
+
const binaryString = window.atob(base64Data);
|
|
89
|
+
const len = binaryString.length;
|
|
90
|
+
const bytes = new Uint8Array(len);
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < len; i++) {
|
|
93
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Create a Blob from the decoded data
|
|
97
|
+
const blob = new Blob([bytes], { type: fileType });
|
|
98
|
+
|
|
99
|
+
// Create and return a File from the Blob
|
|
100
|
+
return new File([blob], fileName, { type: fileType });
|
|
101
101
|
}
|