Add searchable and flattened export, history re-export and diagnostics self-test
issueAndArchive keeps an issued invoice even when rendering or archiving fails (Issued - not archived, with a Render & archive action). The export menu offers a searchable PDF or a 300 DPI image-only PDF whose metadata is the invoice number alone; flatten failures offer Retry or searchable export and never silently lower the resolution. History re-exports the original as issued by default and warns when a re-render's layout fingerprint differs. The render client gains a FIFO assemble lane that previews cannot supersede. A Diagnostics panel runs fixtures in the real worker and compares against goldens generated from Node.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import { flattenPages, type CanvasLike, type FlattenResult, type PdfDocLike } from "../pdf/flatten";
|
||||
|
||||
/** Lets the browser paint and handle input; falls back to a timer when rAF is paused (hidden window). */
|
||||
export const yieldToBrowser = (): Promise<void> =>
|
||||
new Promise<void>((resolve) => {
|
||||
let done = false;
|
||||
const finish = () => {
|
||||
if (!done) {
|
||||
done = true;
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
setTimeout(finish, 50);
|
||||
requestAnimationFrame(() => setTimeout(finish, 0));
|
||||
});
|
||||
|
||||
/** JPEG bytes of a canvas via the browser's encoder. */
|
||||
export function encodeCanvasJpeg(canvas: CanvasLike, quality: number): Promise<Uint8Array> {
|
||||
return new Promise((resolve, reject) => {
|
||||
(canvas as unknown as HTMLCanvasElement).toBlob(
|
||||
(blob) => {
|
||||
if (!blob) return reject(new Error("the browser could not encode the page image (out of memory?)"));
|
||||
blob.arrayBuffer().then((buf) => resolve(new Uint8Array(buf)), reject);
|
||||
},
|
||||
"image/jpeg",
|
||||
quality,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export interface BrowserFlattenOptions {
|
||||
signal?: AbortSignal;
|
||||
onProgress?: (page: number, pages: number) => void;
|
||||
}
|
||||
|
||||
/** Flattens PDF bytes at 300 DPI with pdf.js and a DOM canvas; opens and closes the document itself. */
|
||||
export async function flattenInBrowser(bytes: Uint8Array, opts: BrowserFlattenOptions = {}): Promise<FlattenResult> {
|
||||
const { openPdf, closePdf } = await import("./pdfjs");
|
||||
const doc = await openPdf(bytes);
|
||||
try {
|
||||
return await flattenPages({
|
||||
pdf: doc as unknown as PdfDocLike,
|
||||
createCanvas: (width, height) => {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
return canvas as unknown as CanvasLike;
|
||||
},
|
||||
encodeJpeg: encodeCanvasJpeg,
|
||||
yieldToUi: yieldToBrowser,
|
||||
signal: opts.signal,
|
||||
onProgress: opts.onProgress,
|
||||
});
|
||||
} finally {
|
||||
await closePdf(doc).catch(() => {});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user