mirror of https://github.com/garrytan/gstack.git
feat(diagram-render): __downscaleRaster for print-resolution image normalization
Data-URI rasters re-encode in their own format (JPEG stays JPEG at q0.9 — PNG-encoding photos bloats them) at an explicit target pixel width. Used by make-pdf's pre-pass for the 300dpi content-box ceiling (eng-review D4). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a311cb1ec5
commit
69bf3b07a1
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "gstack-diagram-render",
|
"name": "gstack-diagram-render",
|
||||||
"sha256": "9b1da250ef93f176bfb7b96c6eb735645a879702539bb6a58c378273d90aca23",
|
"sha256": "0ee91aef5a8da85c8941c26ebf2991bbeba82412644bb070d5c5dd2e23538b81",
|
||||||
"bytes": 9644924,
|
"bytes": 9645503,
|
||||||
"bunVersion": "1.3.13",
|
"bunVersion": "1.3.13",
|
||||||
"deps": {
|
"deps": {
|
||||||
"@excalidraw/excalidraw": "0.18.0",
|
"@excalidraw/excalidraw": "0.18.0",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -34,6 +34,7 @@ declare global {
|
||||||
__mermaidToExcalidraw: (text: string) => Promise<string>;
|
__mermaidToExcalidraw: (text: string) => Promise<string>;
|
||||||
__excalidrawToSvg: (sceneJson: string) => Promise<string>;
|
__excalidrawToSvg: (sceneJson: string) => Promise<string>;
|
||||||
__rasterize: (svgText: string, targetWidthPx: number) => Promise<string>;
|
__rasterize: (svgText: string, targetWidthPx: number) => Promise<string>;
|
||||||
|
__downscaleRaster: (dataUri: string, targetWidthPx: number, mime: string) => Promise<string>;
|
||||||
__mountForScreenshot: (svgText: string, targetWidthPx: number) => string;
|
__mountForScreenshot: (svgText: string, targetWidthPx: number) => string;
|
||||||
__probeImage: (src: string) => Promise<string>;
|
__probeImage: (src: string) => Promise<string>;
|
||||||
EXCALIDRAW_ASSET_PATH?: string;
|
EXCALIDRAW_ASSET_PATH?: string;
|
||||||
|
|
@ -152,6 +153,37 @@ window.__mountForScreenshot = (svgText: string, targetWidthPx: number): string =
|
||||||
return `mounted:${targetWidthPx}`;
|
return `mounted:${targetWidthPx}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downscale a raster image (data URI) to targetWidthPx, preserving aspect.
|
||||||
|
* Re-encodes in the requested mime — JPEG photos stay JPEG (q0.9); PNG-encoding
|
||||||
|
* a photo would bloat it past the original. Data URIs are same-origin, so the
|
||||||
|
* canvas never taints.
|
||||||
|
*/
|
||||||
|
window.__downscaleRaster = async (
|
||||||
|
dataUri: string,
|
||||||
|
targetWidthPx: number,
|
||||||
|
mime: string,
|
||||||
|
): Promise<string> => {
|
||||||
|
if (!(targetWidthPx > 0 && targetWidthPx <= 10000)) {
|
||||||
|
throw new Error(`targetWidthPx out of range: ${targetWidthPx}`);
|
||||||
|
}
|
||||||
|
const img = new Image();
|
||||||
|
await new Promise<void>((resolve, reject) => {
|
||||||
|
img.onload = () => resolve();
|
||||||
|
img.onerror = () => reject(new Error("image decode failed"));
|
||||||
|
img.src = dataUri;
|
||||||
|
});
|
||||||
|
const scale = targetWidthPx / (img.naturalWidth || targetWidthPx);
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = Math.round(img.naturalWidth * scale);
|
||||||
|
canvas.height = Math.round(img.naturalHeight * scale);
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) throw new Error("2d canvas context unavailable");
|
||||||
|
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||||
|
const outMime = mime === "image/jpeg" ? "image/jpeg" : "image/png";
|
||||||
|
return outMime === "image/jpeg" ? canvas.toDataURL(outMime, 0.9) : canvas.toDataURL(outMime);
|
||||||
|
};
|
||||||
|
|
||||||
/** Probe intrinsic dimensions of an image (data URI or URL). Returns JSON. */
|
/** Probe intrinsic dimensions of an image (data URI or URL). Returns JSON. */
|
||||||
window.__probeImage = async (src: string): Promise<string> => {
|
window.__probeImage = async (src: string): Promise<string> => {
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue