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:
@@ -1,6 +1,7 @@
|
||||
// Browser-only module worker: the one place react-pdf runs in the app. Not importable from Node tests.
|
||||
import { installStreamAsyncIterator } from "../../lib/streamPolyfill";
|
||||
import { initBrowserRendering } from "./browserInit";
|
||||
import { runAssembleJob } from "./assembleJob";
|
||||
import { renderInvoicePdf } from "./invoice";
|
||||
import type { WorkerMessage, WorkerRequest } from "./jobs";
|
||||
|
||||
@@ -21,10 +22,24 @@ const failure = (id: number | null, err: unknown): WorkerMessage => ({
|
||||
stack: err instanceof Error ? err.stack : undefined,
|
||||
});
|
||||
|
||||
// Handing a buffer over instead of copying it only works when the view covers all of it.
|
||||
const whole = (b: Uint8Array) => b.byteOffset === 0 && b.byteLength === b.buffer.byteLength;
|
||||
|
||||
const started = initBrowserRendering();
|
||||
|
||||
scope.onmessage = async (event) => {
|
||||
const request = event.data;
|
||||
if (request?.type === "assemble-images") {
|
||||
const { job } = request;
|
||||
try {
|
||||
const out = await runAssembleJob(job);
|
||||
const bytes = whole(out.bytes) ? out.bytes : out.bytes.slice();
|
||||
post({ type: "assembled", result: { ...out, bytes } }, [bytes.buffer]);
|
||||
} catch (err) {
|
||||
post(failure(job.id, err));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (request?.type !== "render") return;
|
||||
const { job } = request;
|
||||
try {
|
||||
@@ -32,9 +47,7 @@ scope.onmessage = async (event) => {
|
||||
const t0 = performance.now();
|
||||
const out = await renderInvoicePdf({ model: job.model, prefs: job.prefs });
|
||||
const ms = Math.round(performance.now() - t0);
|
||||
// Hand the buffer over instead of copying it; a view onto part of a larger buffer is copied first.
|
||||
const whole = out.bytes.byteOffset === 0 && out.bytes.byteLength === out.bytes.buffer.byteLength;
|
||||
const bytes = whole ? out.bytes : out.bytes.slice();
|
||||
const bytes = whole(out.bytes) ? out.bytes : out.bytes.slice();
|
||||
post(
|
||||
{ type: "result", result: { id: job.id, bytes, pages: out.pages, issues: out.issues, fingerprint: out.fingerprint, ms } },
|
||||
[bytes.buffer],
|
||||
|
||||
Reference in New Issue
Block a user