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 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 pngToPdf = /* @__PURE__ */ __name(async (image, filename, resolve, reject) => {
2376
- const size = await getImageDimension(image);
2377
- const pdfDoc = new import_jspdf.default({
2378
- unit: "px",
2379
- format: [size.width, size.height]
2380
- // Set PDF dimensions to match the image
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
- pdfDoc.addImage(image, "PNG", 0, 0, size.width, size.height);
2383
- pdfDoc.save(filename);
2384
- resolve(image);
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
- const downloadLink = document.createElement("a");
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 pngToPdf = /* @__PURE__ */ __name(async (image, filename, resolve, reject) => {
2273
- const size = await getImageDimension(image);
2274
- const pdfDoc = new jsPDF({
2275
- unit: "px",
2276
- format: [size.width, size.height]
2277
- // Set PDF dimensions to match the image
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
- pdfDoc.addImage(image, "PNG", 0, 0, size.width, size.height);
2280
- pdfDoc.save(filename);
2281
- resolve(image);
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
- const downloadLink = document.createElement("a");
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frontbacked-svg",
3
- "version": "5.0.1",
3
+ "version": "5.0.2",
4
4
  "description": "frontbacked svg renderer",
5
5
  "types": "dist/main.d.ts",
6
6
  "type": "module",
@@ -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
  }