Files
Voiced/src/pdf/assemble.ts
T
xavierk 98e9e5860f 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.
2026-10-04 06:11:23 +05:30

38 lines
1.2 KiB
TypeScript

import { createElement as h, type ReactElement } from "react";
import { Document, Image, Page } from "@react-pdf/renderer";
export interface AssemblePage {
/** A baseline JPEG; react-pdf embeds it as is (DCTDecode passthrough, no re-encoding). */
jpeg: Uint8Array;
widthPt: number;
heightPt: number;
}
export interface AssembleInput {
pages: AssemblePage[];
/** The only metadata written: a flattened file must not leak the client name or GSTINs. */
title: string;
}
/**
* An image-only document: one full-bleed JPEG per page. No author, subject or keywords, and the
* title is whatever the caller passes (the invoice number). The images go in as untyped Blobs because
* that is the form react-pdf's browser build resolves without sniffing the type.
*/
export function assembleImagePdf({ pages, title }: AssembleInput): ReactElement {
return h(
Document,
{ title },
...pages.map((p, i) =>
h(
Page,
{ key: i, size: [p.widthPt, p.heightPt] as [number, number], style: { padding: 0 }, wrap: false },
h(Image, {
src: new Blob([p.jpeg as BlobPart]) as never,
style: { width: p.widthPt, height: p.heightPt },
}),
),
),
);
}