@pixldocs/canvas-renderer 0.5.18 → 0.5.19
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/index.cjs +84 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +84 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11985,6 +11985,66 @@ function PixldocsPreview(props) {
|
|
|
11985
11985
|
!canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
|
|
11986
11986
|
] });
|
|
11987
11987
|
}
|
|
11988
|
+
const inlinedAssetCache = /* @__PURE__ */ new Map();
|
|
11989
|
+
function shouldInlineImageUrl(url) {
|
|
11990
|
+
if (!url || url.startsWith("data:")) return false;
|
|
11991
|
+
if (url.startsWith("blob:")) return true;
|
|
11992
|
+
if (url.startsWith("/") && !url.startsWith("//")) return true;
|
|
11993
|
+
try {
|
|
11994
|
+
const parsed = new URL(url, window.location.href);
|
|
11995
|
+
const current = new URL(window.location.href);
|
|
11996
|
+
const host = parsed.hostname.toLowerCase();
|
|
11997
|
+
return parsed.origin === current.origin || host === "localhost" || host === "127.0.0.1" || host === "0.0.0.0" || host.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(host);
|
|
11998
|
+
} catch {
|
|
11999
|
+
return false;
|
|
12000
|
+
}
|
|
12001
|
+
}
|
|
12002
|
+
async function imageUrlToDataUrl(url) {
|
|
12003
|
+
if (inlinedAssetCache.has(url)) return inlinedAssetCache.get(url) ?? null;
|
|
12004
|
+
try {
|
|
12005
|
+
const response = await fetch(url);
|
|
12006
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
12007
|
+
const blob = await response.blob();
|
|
12008
|
+
if (blob.type.includes("text/html")) throw new Error("Expected image but got text/html");
|
|
12009
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
12010
|
+
const reader = new FileReader();
|
|
12011
|
+
reader.onloadend = () => resolve(String(reader.result || ""));
|
|
12012
|
+
reader.onerror = reject;
|
|
12013
|
+
reader.readAsDataURL(blob);
|
|
12014
|
+
});
|
|
12015
|
+
inlinedAssetCache.set(url, dataUrl);
|
|
12016
|
+
return dataUrl;
|
|
12017
|
+
} catch (error) {
|
|
12018
|
+
console.warn("[@pixldocs/canvas-renderer] Failed to inline image asset:", url, error);
|
|
12019
|
+
inlinedAssetCache.set(url, null);
|
|
12020
|
+
return null;
|
|
12021
|
+
}
|
|
12022
|
+
}
|
|
12023
|
+
async function inlineNodeAssets(node) {
|
|
12024
|
+
if (node.type === "image") {
|
|
12025
|
+
const url = typeof node.src === "string" && node.src.trim() ? node.src.trim() : typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
|
|
12026
|
+
if (shouldInlineImageUrl(url)) {
|
|
12027
|
+
const dataUrl = await imageUrlToDataUrl(url);
|
|
12028
|
+
if (dataUrl) {
|
|
12029
|
+
node.src = dataUrl;
|
|
12030
|
+
node.imageUrl = dataUrl;
|
|
12031
|
+
}
|
|
12032
|
+
}
|
|
12033
|
+
}
|
|
12034
|
+
if (Array.isArray(node.children)) {
|
|
12035
|
+
await Promise.all(node.children.map(inlineNodeAssets));
|
|
12036
|
+
}
|
|
12037
|
+
}
|
|
12038
|
+
async function inlineBrowserReachableImageAssets(config) {
|
|
12039
|
+
if (typeof window === "undefined" || typeof fetch === "undefined" || typeof FileReader === "undefined") {
|
|
12040
|
+
return config;
|
|
12041
|
+
}
|
|
12042
|
+
const cloned = JSON.parse(JSON.stringify(config));
|
|
12043
|
+
await Promise.all(
|
|
12044
|
+
(cloned.pages || []).flatMap((page) => (page.children || []).map(inlineNodeAssets))
|
|
12045
|
+
);
|
|
12046
|
+
return cloned;
|
|
12047
|
+
}
|
|
11988
12048
|
class PixldocsRenderer {
|
|
11989
12049
|
constructor(config) {
|
|
11990
12050
|
__publicField(this, "config");
|
|
@@ -11995,21 +12055,22 @@ class PixldocsRenderer {
|
|
|
11995
12055
|
* Mounts a hidden PreviewCanvas component and captures the Fabric canvas output.
|
|
11996
12056
|
*/
|
|
11997
12057
|
async render(templateConfig, options = {}) {
|
|
12058
|
+
const renderConfig = await inlineBrowserReachableImageAssets(templateConfig);
|
|
11998
12059
|
const pageIndex = options.pageIndex ?? 0;
|
|
11999
12060
|
const format = options.format ?? "png";
|
|
12000
12061
|
const quality = options.quality ?? 0.92;
|
|
12001
12062
|
const pixelRatio = options.pixelRatio ?? this.config.pixelRatio ?? 2;
|
|
12002
|
-
const canvasWidth =
|
|
12003
|
-
const canvasHeight =
|
|
12004
|
-
const page =
|
|
12063
|
+
const canvasWidth = renderConfig.canvas.width;
|
|
12064
|
+
const canvasHeight = renderConfig.canvas.height;
|
|
12065
|
+
const page = renderConfig.pages[pageIndex];
|
|
12005
12066
|
if (!page) {
|
|
12006
|
-
throw new Error(`Page index ${pageIndex} not found (template has ${
|
|
12067
|
+
throw new Error(`Page index ${pageIndex} not found (template has ${renderConfig.pages.length} pages)`);
|
|
12007
12068
|
}
|
|
12008
|
-
await ensureFontsForResolvedConfig(
|
|
12069
|
+
await ensureFontsForResolvedConfig(renderConfig);
|
|
12009
12070
|
const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
|
|
12010
12071
|
setPackageApiUrl2(this.config.imageProxyUrl);
|
|
12011
12072
|
const dataUrl = await this.renderPageViaPreviewCanvas(
|
|
12012
|
-
|
|
12073
|
+
renderConfig,
|
|
12013
12074
|
pageIndex,
|
|
12014
12075
|
pixelRatio,
|
|
12015
12076
|
format,
|
|
@@ -12061,16 +12122,17 @@ class PixldocsRenderer {
|
|
|
12061
12122
|
* This is the key building block for client-side vector PDF export.
|
|
12062
12123
|
*/
|
|
12063
12124
|
async renderPageSvg(templateConfig, pageIndex = 0) {
|
|
12064
|
-
const
|
|
12125
|
+
const renderConfig = await inlineBrowserReachableImageAssets(templateConfig);
|
|
12126
|
+
const page = renderConfig.pages[pageIndex];
|
|
12065
12127
|
if (!page) {
|
|
12066
|
-
throw new Error(`Page index ${pageIndex} not found (template has ${
|
|
12128
|
+
throw new Error(`Page index ${pageIndex} not found (template has ${renderConfig.pages.length} pages)`);
|
|
12067
12129
|
}
|
|
12068
|
-
await ensureFontsForResolvedConfig(
|
|
12130
|
+
await ensureFontsForResolvedConfig(renderConfig);
|
|
12069
12131
|
const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
|
|
12070
12132
|
setPackageApiUrl2(this.config.imageProxyUrl);
|
|
12071
|
-
const canvasWidth =
|
|
12072
|
-
const canvasHeight =
|
|
12073
|
-
return this.captureSvgViaPreviewCanvas(
|
|
12133
|
+
const canvasWidth = renderConfig.canvas.width;
|
|
12134
|
+
const canvasHeight = renderConfig.canvas.height;
|
|
12135
|
+
return this.captureSvgViaPreviewCanvas(renderConfig, pageIndex, canvasWidth, canvasHeight);
|
|
12074
12136
|
}
|
|
12075
12137
|
/**
|
|
12076
12138
|
* Render all pages and return SVG strings for each.
|
|
@@ -12418,7 +12480,7 @@ class PixldocsRenderer {
|
|
|
12418
12480
|
cleanup();
|
|
12419
12481
|
reject(new Error("Render timeout (30s)"));
|
|
12420
12482
|
}, 3e4);
|
|
12421
|
-
let
|
|
12483
|
+
let finished = false;
|
|
12422
12484
|
const cleanup = () => {
|
|
12423
12485
|
clearTimeout(timeout);
|
|
12424
12486
|
try {
|
|
@@ -12428,22 +12490,12 @@ class PixldocsRenderer {
|
|
|
12428
12490
|
container.remove();
|
|
12429
12491
|
};
|
|
12430
12492
|
const onReady = () => {
|
|
12431
|
-
if (
|
|
12432
|
-
|
|
12433
|
-
root.render(
|
|
12434
|
-
createElement(PreviewCanvas2, {
|
|
12435
|
-
config,
|
|
12436
|
-
pageIndex,
|
|
12437
|
-
zoom: pixelRatio,
|
|
12438
|
-
absoluteZoom: true,
|
|
12439
|
-
skipFontReadyWait: true,
|
|
12440
|
-
onReady
|
|
12441
|
-
})
|
|
12442
|
-
);
|
|
12443
|
-
return;
|
|
12444
|
-
}
|
|
12493
|
+
if (finished) return;
|
|
12494
|
+
finished = true;
|
|
12445
12495
|
this.waitForCanvasScene(container, config, pageIndex).then(async () => {
|
|
12446
12496
|
try {
|
|
12497
|
+
await this.waitForStableTextMetrics(container, config);
|
|
12498
|
+
await this.waitForCanvasScene(container, config, pageIndex, 2500, 50);
|
|
12447
12499
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
12448
12500
|
const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
|
|
12449
12501
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
@@ -12514,7 +12566,7 @@ class PixldocsRenderer {
|
|
|
12514
12566
|
cleanup();
|
|
12515
12567
|
reject(new Error("SVG render timeout (30s)"));
|
|
12516
12568
|
}, 3e4);
|
|
12517
|
-
let
|
|
12569
|
+
let finished = false;
|
|
12518
12570
|
const cleanup = () => {
|
|
12519
12571
|
clearTimeout(timeout);
|
|
12520
12572
|
try {
|
|
@@ -12524,23 +12576,13 @@ class PixldocsRenderer {
|
|
|
12524
12576
|
container.remove();
|
|
12525
12577
|
};
|
|
12526
12578
|
const onReady = () => {
|
|
12527
|
-
if (
|
|
12528
|
-
|
|
12529
|
-
root.render(
|
|
12530
|
-
createElement(PreviewCanvas2, {
|
|
12531
|
-
config,
|
|
12532
|
-
pageIndex,
|
|
12533
|
-
zoom: 1,
|
|
12534
|
-
absoluteZoom: true,
|
|
12535
|
-
skipFontReadyWait: true,
|
|
12536
|
-
onReady
|
|
12537
|
-
})
|
|
12538
|
-
);
|
|
12539
|
-
return;
|
|
12540
|
-
}
|
|
12579
|
+
if (finished) return;
|
|
12580
|
+
finished = true;
|
|
12541
12581
|
this.waitForCanvasScene(container, config, pageIndex).then(async () => {
|
|
12542
12582
|
var _a, _b;
|
|
12543
12583
|
try {
|
|
12584
|
+
await this.waitForStableTextMetrics(container, config);
|
|
12585
|
+
await this.waitForCanvasScene(container, config, pageIndex, 2500, 50);
|
|
12544
12586
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
12545
12587
|
if (!fabricInstance) {
|
|
12546
12588
|
cleanup();
|