@pixldocs/canvas-renderer 0.5.56 → 0.5.57
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 +118 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +118 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -12471,7 +12471,7 @@ function PixldocsPreview(props) {
|
|
|
12471
12471
|
!canvasSettled && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
|
|
12472
12472
|
] });
|
|
12473
12473
|
}
|
|
12474
|
-
const PACKAGE_VERSION = "0.5.
|
|
12474
|
+
const PACKAGE_VERSION = "0.5.57";
|
|
12475
12475
|
let __underlineFixInstalled = false;
|
|
12476
12476
|
function installUnderlineFix(fab) {
|
|
12477
12477
|
var _a;
|
|
@@ -12568,6 +12568,22 @@ function installUnderlineFix(fab) {
|
|
|
12568
12568
|
__underlineFixInstalled = true;
|
|
12569
12569
|
console.log(`[canvas-renderer] underline-fix monkey patch installed (v${PACKAGE_VERSION})`);
|
|
12570
12570
|
}
|
|
12571
|
+
function configHasAutoShrinkText(config) {
|
|
12572
|
+
var _a;
|
|
12573
|
+
if (!((_a = config == null ? void 0 : config.pages) == null ? void 0 : _a.length)) return false;
|
|
12574
|
+
const walk = (nodes) => {
|
|
12575
|
+
for (const node of nodes || []) {
|
|
12576
|
+
if (!node) continue;
|
|
12577
|
+
if (node.type === "text" && node.overflowPolicy === "auto-shrink") return true;
|
|
12578
|
+
if (Array.isArray(node.children) && node.children.length && walk(node.children)) return true;
|
|
12579
|
+
}
|
|
12580
|
+
return false;
|
|
12581
|
+
};
|
|
12582
|
+
for (const page of config.pages) {
|
|
12583
|
+
if (walk(page.children || [])) return true;
|
|
12584
|
+
}
|
|
12585
|
+
return false;
|
|
12586
|
+
}
|
|
12571
12587
|
class PixldocsRenderer {
|
|
12572
12588
|
constructor(config) {
|
|
12573
12589
|
__publicField(this, "config");
|
|
@@ -12594,6 +12610,11 @@ class PixldocsRenderer {
|
|
|
12594
12610
|
throw new Error(`Page index ${pageIndex} not found (template has ${templateConfig.pages.length} pages)`);
|
|
12595
12611
|
}
|
|
12596
12612
|
await ensureFontsForResolvedConfig(templateConfig);
|
|
12613
|
+
if (!options.skipFontReadyWait) {
|
|
12614
|
+
const hasAutoShrink = configHasAutoShrinkText(templateConfig);
|
|
12615
|
+
const defaultWait = hasAutoShrink ? 4e3 : 1800;
|
|
12616
|
+
await this.awaitFontsForConfig(templateConfig, options.waitForFontsMs ?? defaultWait);
|
|
12617
|
+
}
|
|
12597
12618
|
const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
|
|
12598
12619
|
setPackageApiUrl2(this.config.imageProxyUrl);
|
|
12599
12620
|
const dataUrl = await this.renderPageViaPreviewCanvas(
|
|
@@ -12601,7 +12622,8 @@ class PixldocsRenderer {
|
|
|
12601
12622
|
pageIndex,
|
|
12602
12623
|
pixelRatio,
|
|
12603
12624
|
format,
|
|
12604
|
-
quality
|
|
12625
|
+
quality,
|
|
12626
|
+
{ skipFontReadyWait: options.skipFontReadyWait, waitForFontsMs: options.waitForFontsMs }
|
|
12605
12627
|
);
|
|
12606
12628
|
return {
|
|
12607
12629
|
dataUrl,
|
|
@@ -12615,9 +12637,14 @@ class PixldocsRenderer {
|
|
|
12615
12637
|
* Render all pages and return array of results.
|
|
12616
12638
|
*/
|
|
12617
12639
|
async renderAllPages(templateConfig, options = {}) {
|
|
12640
|
+
if (!options.skipFontReadyWait) {
|
|
12641
|
+
const hasAutoShrink = configHasAutoShrinkText(templateConfig);
|
|
12642
|
+
const defaultWait = hasAutoShrink ? 4e3 : 1800;
|
|
12643
|
+
await this.awaitFontsForConfig(templateConfig, options.waitForFontsMs ?? defaultWait);
|
|
12644
|
+
}
|
|
12618
12645
|
const results = [];
|
|
12619
12646
|
for (let i = 0; i < templateConfig.pages.length; i++) {
|
|
12620
|
-
results.push(await this.render(templateConfig, { ...options, pageIndex: i }));
|
|
12647
|
+
results.push(await this.render(templateConfig, { ...options, pageIndex: i, skipFontReadyWait: true }));
|
|
12621
12648
|
}
|
|
12622
12649
|
return results;
|
|
12623
12650
|
}
|
|
@@ -12654,6 +12681,8 @@ class PixldocsRenderer {
|
|
|
12654
12681
|
throw new Error(`Page index ${pageIndex} not found (template has ${templateConfig.pages.length} pages)`);
|
|
12655
12682
|
}
|
|
12656
12683
|
await ensureFontsForResolvedConfig(templateConfig);
|
|
12684
|
+
const hasAutoShrinkSvg = configHasAutoShrinkText(templateConfig);
|
|
12685
|
+
await this.awaitFontsForConfig(templateConfig, hasAutoShrinkSvg ? 4e3 : 1800);
|
|
12657
12686
|
const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
|
|
12658
12687
|
setPackageApiUrl2(this.config.imageProxyUrl);
|
|
12659
12688
|
const canvasWidth = templateConfig.canvas.width;
|
|
@@ -12665,6 +12694,8 @@ class PixldocsRenderer {
|
|
|
12665
12694
|
*/
|
|
12666
12695
|
async renderAllPageSvgs(templateConfig) {
|
|
12667
12696
|
await ensureFontsForResolvedConfig(templateConfig);
|
|
12697
|
+
const hasAutoShrinkSvg = configHasAutoShrinkText(templateConfig);
|
|
12698
|
+
await this.awaitFontsForConfig(templateConfig, hasAutoShrinkSvg ? 4e3 : 1800);
|
|
12668
12699
|
const { setPackageApiUrl: setPackageApiUrl2 } = await Promise.resolve().then(() => appApi);
|
|
12669
12700
|
setPackageApiUrl2(this.config.imageProxyUrl);
|
|
12670
12701
|
const results = [];
|
|
@@ -12917,6 +12948,26 @@ class PixldocsRenderer {
|
|
|
12917
12948
|
]);
|
|
12918
12949
|
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
|
|
12919
12950
|
}
|
|
12951
|
+
/**
|
|
12952
|
+
* Block until the webfonts referenced by `config` have actually loaded
|
|
12953
|
+
* (or `maxWaitMs` elapses). Used by the headless capture path BEFORE
|
|
12954
|
+
* mounting `PreviewCanvas`, so the synchronous `createText` auto-shrink
|
|
12955
|
+
* loop measures against final font metrics instead of fallback ones.
|
|
12956
|
+
*
|
|
12957
|
+
* Stronger than `ensureFontsForResolvedConfig` (which is fire-and-forget)
|
|
12958
|
+
* — this awaits each `document.fonts.load(spec)` AND `document.fonts.ready`,
|
|
12959
|
+
* racing the whole thing against `maxWaitMs` so a slow CDN can't hang the
|
|
12960
|
+
* renderer.
|
|
12961
|
+
*/
|
|
12962
|
+
async awaitFontsForConfig(config, maxWaitMs) {
|
|
12963
|
+
if (typeof document === "undefined" || !document.fonts) return;
|
|
12964
|
+
void ensureFontsForResolvedConfig(config);
|
|
12965
|
+
await this.waitForRelevantFonts(config, maxWaitMs);
|
|
12966
|
+
await Promise.race([
|
|
12967
|
+
document.fonts.ready.catch(() => void 0).then(() => void 0),
|
|
12968
|
+
new Promise((r) => setTimeout(r, Math.min(500, maxWaitMs)))
|
|
12969
|
+
]);
|
|
12970
|
+
}
|
|
12920
12971
|
getNormalizedGradientStops(gradient) {
|
|
12921
12972
|
const stops = Array.isArray(gradient == null ? void 0 : gradient.stops) ? gradient.stops.map((stop) => ({
|
|
12922
12973
|
offset: Math.max(0, Math.min(1, Number((stop == null ? void 0 : stop.offset) ?? 0))),
|
|
@@ -12990,10 +13041,19 @@ class PixldocsRenderer {
|
|
|
12990
13041
|
} catch {
|
|
12991
13042
|
}
|
|
12992
13043
|
}
|
|
12993
|
-
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality) {
|
|
13044
|
+
async renderPageViaPreviewCanvas(config, pageIndex, pixelRatio, format, quality, options = {}) {
|
|
12994
13045
|
const { PreviewCanvas: PreviewCanvas2 } = await Promise.resolve().then(() => PreviewCanvas$1);
|
|
12995
13046
|
const canvasWidth = config.canvas.width;
|
|
12996
13047
|
const canvasHeight = config.canvas.height;
|
|
13048
|
+
const hasAutoShrink = configHasAutoShrinkText(config);
|
|
13049
|
+
let firstMountSettled = false;
|
|
13050
|
+
let lateFontSettleDetected = false;
|
|
13051
|
+
if (typeof document !== "undefined" && document.fonts && hasAutoShrink) {
|
|
13052
|
+
document.fonts.ready.then(() => {
|
|
13053
|
+
if (firstMountSettled) lateFontSettleDetected = true;
|
|
13054
|
+
}).catch(() => {
|
|
13055
|
+
});
|
|
13056
|
+
}
|
|
12997
13057
|
return new Promise((resolve, reject) => {
|
|
12998
13058
|
const container = document.createElement("div");
|
|
12999
13059
|
container.style.cssText = `
|
|
@@ -13006,6 +13066,8 @@ class PixldocsRenderer {
|
|
|
13006
13066
|
cleanup();
|
|
13007
13067
|
reject(new Error("Render timeout (30s)"));
|
|
13008
13068
|
}, 3e4);
|
|
13069
|
+
let root;
|
|
13070
|
+
let mountKey = 0;
|
|
13009
13071
|
const cleanup = () => {
|
|
13010
13072
|
clearTimeout(timeout);
|
|
13011
13073
|
try {
|
|
@@ -13014,6 +13076,46 @@ class PixldocsRenderer {
|
|
|
13014
13076
|
}
|
|
13015
13077
|
container.remove();
|
|
13016
13078
|
};
|
|
13079
|
+
const remountWithFreshKey = async () => {
|
|
13080
|
+
mountKey += 1;
|
|
13081
|
+
try {
|
|
13082
|
+
clearMeasurementCache();
|
|
13083
|
+
} catch {
|
|
13084
|
+
}
|
|
13085
|
+
try {
|
|
13086
|
+
clearFabricCharCache();
|
|
13087
|
+
} catch {
|
|
13088
|
+
}
|
|
13089
|
+
try {
|
|
13090
|
+
root.unmount();
|
|
13091
|
+
} catch {
|
|
13092
|
+
}
|
|
13093
|
+
root = client.createRoot(container);
|
|
13094
|
+
await new Promise((settle) => {
|
|
13095
|
+
const onReadyOnce = () => {
|
|
13096
|
+
this.waitForCanvasScene(container, config, pageIndex).then(async () => {
|
|
13097
|
+
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
13098
|
+
const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
|
|
13099
|
+
await this.waitForCanvasImages(container, expectedImageCount);
|
|
13100
|
+
await this.waitForStableTextMetrics(container, config);
|
|
13101
|
+
await this.waitForCanvasScene(container, config, pageIndex);
|
|
13102
|
+
if (!fabricInstance) return settle();
|
|
13103
|
+
settle();
|
|
13104
|
+
}).catch(() => settle());
|
|
13105
|
+
};
|
|
13106
|
+
root.render(
|
|
13107
|
+
react.createElement(PreviewCanvas2, {
|
|
13108
|
+
key: `remount-${mountKey}`,
|
|
13109
|
+
config,
|
|
13110
|
+
pageIndex,
|
|
13111
|
+
zoom: pixelRatio,
|
|
13112
|
+
absoluteZoom: true,
|
|
13113
|
+
skipFontReadyWait: false,
|
|
13114
|
+
onReady: onReadyOnce
|
|
13115
|
+
})
|
|
13116
|
+
);
|
|
13117
|
+
});
|
|
13118
|
+
};
|
|
13017
13119
|
const onReady = () => {
|
|
13018
13120
|
this.waitForCanvasScene(container, config, pageIndex).then(async () => {
|
|
13019
13121
|
try {
|
|
@@ -13022,16 +13124,23 @@ class PixldocsRenderer {
|
|
|
13022
13124
|
await this.waitForCanvasImages(container, expectedImageCount);
|
|
13023
13125
|
await this.waitForStableTextMetrics(container, config);
|
|
13024
13126
|
await this.waitForCanvasScene(container, config, pageIndex);
|
|
13127
|
+
firstMountSettled = true;
|
|
13128
|
+
if (hasAutoShrink && lateFontSettleDetected) {
|
|
13129
|
+
console.log("[canvas-renderer][parity] late font-settle detected — remounting for auto-shrink reflow");
|
|
13130
|
+
await remountWithFreshKey();
|
|
13131
|
+
}
|
|
13025
13132
|
const fabricCanvas = container.querySelector("canvas.upper-canvas, canvas");
|
|
13026
13133
|
const sourceCanvas = (fabricInstance == null ? void 0 : fabricInstance.lowerCanvasEl) || container.querySelector("canvas.lower-canvas") || fabricCanvas;
|
|
13134
|
+
const fabricInstanceAfter = this.getFabricCanvasFromContainer(container) || fabricInstance;
|
|
13135
|
+
const sourceCanvasAfter = (fabricInstanceAfter == null ? void 0 : fabricInstanceAfter.lowerCanvasEl) || container.querySelector("canvas.lower-canvas") || sourceCanvas;
|
|
13027
13136
|
if (!sourceCanvas) {
|
|
13028
13137
|
cleanup();
|
|
13029
13138
|
reject(new Error("No canvas element found after render"));
|
|
13030
13139
|
return;
|
|
13031
13140
|
}
|
|
13032
13141
|
const exportCanvas = document.createElement("canvas");
|
|
13033
|
-
exportCanvas.width =
|
|
13034
|
-
exportCanvas.height =
|
|
13142
|
+
exportCanvas.width = sourceCanvasAfter.width;
|
|
13143
|
+
exportCanvas.height = sourceCanvasAfter.height;
|
|
13035
13144
|
const exportCtx = exportCanvas.getContext("2d");
|
|
13036
13145
|
if (!exportCtx) {
|
|
13037
13146
|
cleanup();
|
|
@@ -13039,10 +13148,10 @@ class PixldocsRenderer {
|
|
|
13039
13148
|
return;
|
|
13040
13149
|
}
|
|
13041
13150
|
exportCtx.save();
|
|
13042
|
-
exportCtx.scale(
|
|
13151
|
+
exportCtx.scale(sourceCanvasAfter.width / canvasWidth, sourceCanvasAfter.height / canvasHeight);
|
|
13043
13152
|
this.paintPageBackground(exportCtx, config.pages[pageIndex], canvasWidth, canvasHeight);
|
|
13044
13153
|
exportCtx.restore();
|
|
13045
|
-
exportCtx.drawImage(
|
|
13154
|
+
exportCtx.drawImage(sourceCanvasAfter, 0, 0);
|
|
13046
13155
|
const mimeType = format === "jpeg" ? "image/jpeg" : format === "webp" ? "image/webp" : "image/png";
|
|
13047
13156
|
const dataUrl = exportCanvas.toDataURL(mimeType, quality);
|
|
13048
13157
|
cleanup();
|
|
@@ -13053,7 +13162,7 @@ class PixldocsRenderer {
|
|
|
13053
13162
|
}
|
|
13054
13163
|
});
|
|
13055
13164
|
};
|
|
13056
|
-
|
|
13165
|
+
root = client.createRoot(container);
|
|
13057
13166
|
root.render(
|
|
13058
13167
|
react.createElement(PreviewCanvas2, {
|
|
13059
13168
|
config,
|