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",
|
||||
"sha256": "9b1da250ef93f176bfb7b96c6eb735645a879702539bb6a58c378273d90aca23",
|
||||
"bytes": 9644924,
|
||||
"sha256": "0ee91aef5a8da85c8941c26ebf2991bbeba82412644bb070d5c5dd2e23538b81",
|
||||
"bytes": 9645503,
|
||||
"bunVersion": "1.3.13",
|
||||
"deps": {
|
||||
"@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>;
|
||||
__excalidrawToSvg: (sceneJson: string) => Promise<string>;
|
||||
__rasterize: (svgText: string, targetWidthPx: number) => Promise<string>;
|
||||
__downscaleRaster: (dataUri: string, targetWidthPx: number, mime: string) => Promise<string>;
|
||||
__mountForScreenshot: (svgText: string, targetWidthPx: number) => string;
|
||||
__probeImage: (src: string) => Promise<string>;
|
||||
EXCALIDRAW_ASSET_PATH?: string;
|
||||
|
|
@ -152,6 +153,37 @@ window.__mountForScreenshot = (svgText: string, targetWidthPx: number): string =
|
|||
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. */
|
||||
window.__probeImage = async (src: string): Promise<string> => {
|
||||
const img = new Image();
|
||||
|
|
|
|||
Loading…
Reference in New Issue