/** * `@printwithsynergy/lens-pdf/browser` * * Browser-only ViewerServices factory. One call gives you a fully * wired {@link ViewerServices} instance backed by pdf.js — every * tool the package ships (page raster, layers, separations, TAC * heatmap, color picker, densitometer, in-browser annotations) * works against any PDF the browser can fetch, without a server * backend. * * ```ts * import { createBrowserViewerServices } from "@printwithsynergy/lens-pdf/browser"; * const services = createBrowserViewerServices({ pdfUrl: "/proofs/abc.pdf" }); * await services.prepare(1); // pre-warm separations + heatmap + layers for page 1 * // ... * services.dispose(); // free blob URLs / pdf.js doc on unmount * ``` * * Server-only features (true ICC separations, preflight findings, * server-persisted annotations, PDF report exports) are explicitly * left as `markUnwired` no-ops; their components self-hide. * * The CMYK numbers here are **rich-black approximations**, not ICC. * Solid black RGB(0,0,0) reads as C=100, M=100, Y=100, K=80, TAC≈380% * — close enough to a press CMYK rich-black to make the TAC heatmap * and densitometer behave like their real backends. Production * hosts wire a Ghostscript / MuPDF backend for ICC-correct readings. * * **Security**: this factory fetches whatever URL the host hands it. * Sign / scope / expire the URL the same way you would any other PDF * download — the viewer is a pure renderer and trusts the host to * enforce access control upstream. * * @public */ declare module "pdfjs-dist/legacy/build/pdf.mjs"; export type { CodexOverlayServices, MinimalCodexClient } from "./codexOverlay"; export { createCodexOverlayServices, extractInksFromColorWorld, extractLayersFromOcgs, } from "./codexOverlay"; import { type ThemeTokens, type ViewerServices } from "../plugin/services"; export { PROCESS_CHANNELS } from "./constants"; export { pantoneGoldLookup, processPlateLookup, resolveInkSwatch, resolveSpotSwatch, rgbToHex, } from "./pantone-gold"; /** * A single ink reported by the densitometer / color picker. * * @public */ export interface DetectedInk { /** Display name — `"Cyan"`, `"PANTONE 185 C"`, `"Reflex Blue"` … */ name: string; /** `process` for CMYK plates pdf.js composites in RGB; `spot` for * Separation / DeviceN colorants the host PDF declares. */ type: "process" | "spot"; /** Synthetic alternate sRGB triplet used to estimate coverage from * the rasterised RGB pixel. For process inks this is the canonical * CMYK→sRGB primary; for spots it's parsed from the PDF when * available, otherwise hash-derived from the spot name so each * spot still gets a stable, distinct hue. */ altRgb: [number, number, number]; } /** * Default URL for the pdf.js worker, served via unpkg pinned to the * exact `pdfjs-dist` version this package was built against. Points at * the **legacy** build (`legacy/build/pdf.worker.min.mjs`) to match the * legacy main-thread import above — both must be the same build so the * worker carries the `getOrInsertComputed` polyfill and the API/Worker * versions agree. Hosts that don't want a runtime CDN dep can override * via the `workerSrc` option, set `pdfjs.GlobalWorkerOptions.workerSrc` * directly before constructing the services, or self-host the file. * * @public */ export declare const defaultBrowserWorkerSrc: string; /** * Convert an sRGB triplet to a CMYK approximation. Uses an additive * decomposition with a "rich-black" K plate: * * C = 1 - R/255 * M = 1 - G/255 * Y = 1 - B/255 * K = min(C, M, Y) × {@link K_FACTOR} * TAC = (C + M + Y + K) × 100 // [0, 380] * * Returns each ink in [0, 1] and total area coverage as a percentage * in [0, 380]. * * This is intentionally NOT a GCR substitution — keeping the full * C/M/Y values lets the TAC heatmap actually trip on solid-black * artwork instead of bottoming out at 100 % from K alone, which is * the behaviour print-prepress reviewers expect. * * @public */ export declare function rgbToCmyk(r: number, g: number, b: number): { c: number; m: number; y: number; k: number; tac: number; }; /** * Best-effort spot-ink detector. Scans the raw PDF bytes (latin-1 * decoded) for `/Separation /Name …` and `/DeviceN [/N1 /N2 …]` * declarations. **Misses spots inside compressed object streams** — * use {@link detectSpotInksFromPdf} (async) to catch those too. Kept * synchronous for callers that can't await. * * @public */ export declare function detectSpotInksFromPdfBytes(bytes: Uint8Array): DetectedInk[]; /** * Spot-ink detector that also sees **compressed** colorant declarations. * * Modern PDF exporters (Illustrator / InDesign) bundle the * `/ColorSpace` resource dict — where `[/Separation /PANTONE… /DeviceCMYK …]` * arrays live — inside `FlateDecode` `/ObjStm` object streams, which the * raw-bytes scan in {@link detectSpotInksFromPdfBytes} can't see. This * runs that scan first (for the uncompressed case + cross-reference * tables), then inflates each `/ObjStm` with the browser-native * `DecompressionStream` and re-scans its decompressed contents, so spots * in those exports surface in the in-browser separations / densitometer * without a backend. * * Best-effort and safe: on a runtime without `DecompressionStream`, or on * any per-stream inflate error (predictor'd / truncated / non-zlib), it * degrades to exactly the raw-bytes result. * * @public */ export declare function detectSpotInksFromPdf(bytes: Uint8Array): Promise; /** * Spot inks **plus** whether the document genuinely uses process color. Runs the * spot scan and the process-color probe over the same chunk stream (raw bytes + * inflated `/ObjStm`s) in one pass, so the separations panel can drop the four * CMYK plates from a spot-only file. `usesProcessColor` is conservative: it's * only `true` when a process color space is found as content. * * @public */ export declare function detectInkUsageFromPdf(bytes: Uint8Array): Promise<{ spots: DetectedInk[]; usesProcessColor: boolean; }>; /** * Options for {@link createBrowserViewerServices}. * * @public */ export interface BrowserViewerServicesOptions { /** Raw PDF URL the browser can fetch. */ pdfUrl: string; /** * Optional override for the pdf.js worker URL. Default: * {@link defaultBrowserWorkerSrc}. */ workerSrc?: string; /** * Theme tokens to expose on `services.tokens`. Defaults to the * package's neutral light palette. */ tokens?: ThemeTokens; /** * Default TAC limit (in percent) used when the host doesn't specify * one explicitly. The TAC heatmap and densitometer both respect the * per-call value first; this is only used when the call site omits * it. Default 300 — typical sheet-fed press limit. */ tacLimit?: number; /** * Email used as the synthetic author for the in-browser annotation * service. Default: `"you@browser.local"`. */ annotationAuthorEmail?: string; /** * Upper bound on the analysis raster's pixel area (width × height). * The analysis raster is rendered once per page at {@link ANALYSIS_DPI} * and every derived plate (CMYK + spot separations, TAC heatmap) is a * canvas of the same size, so this single cap governs the cumulative * canvas memory the separations / densitometer / TAC tools allocate. * * Defaults to a large-format-safe 12 MP. **Memory-constrained hosts * (mobile / iOS Safari, where the per-process canvas budget is small) * should pass a much lower value** — e.g. ~1.5 MP — to keep the * separations view from exhausting the tab's memory and getting * reloaded by the OS. Reads stay accurate; only the sampling grid gets * coarser. The library-internal 12 MP ceiling still applies on top, so * values above it are clamped. */ maxAnalysisPixels?: number; } /** * Augmented `ViewerServices` returned by {@link createBrowserViewerServices}. * Extends the wire protocol with lifecycle helpers, a subscription * hook that fires whenever a lazily-built tile URL becomes available, * and a `prepare()` method that pre-warms the analysis raster, every * channel, the TAC heatmap, and (if the PDF has OCGs) every layer * for a given page. * * @public */ export interface BrowserViewerServices extends ViewerServices { /** How many pages the underlying PDF has (resolves once parsed). */ getPageCount(): Promise; /** Page dimensions in PDF points for `pageNum` (1-indexed). */ getPageDimensions(pageNum: number): Promise<{ widthPts: number; heightPts: number; }>; /** * Inks the densitometer + color picker will report on. Always * includes the four CMYK process plates pdf.js composites in RGB, * plus any spot inks we could extract from the raw PDF bytes * (Separation / DeviceN declarations outside compressed streams). * Resolves once the PDF has been parsed. */ getInks(): Promise; /** * Pre-build analysis raster + every CMYK channel image + the TAC * heatmap + every layer image for `pageNum`. Resolves once they're * all cached so the synchronous URL builders return real blob URLs * rather than placeholders. Hosts mounting ``, * ``, or `` should `await` this * before mounting the canvas — those components cache the first * loaded image and never retry on URL change. */ prepare(pageNum: number, opts?: { tacLimit?: number; }): Promise<{ widthPts: number; heightPts: number; layerCount: number; }>; /** * Subscribe to "URL available" notifications. Components that read * synchronous URL builders (`pageImages.getPageImageUrl` in * particular) should re-render on each event so the next builder * call returns the freshly-cached blob URL instead of the * placeholder. */ subscribe(listener: () => void): () => void; /** Free every blob URL the services minted. Call on unmount. */ dispose(): void; } /** * Build a fully-wired {@link ViewerServices} backed by pdf.js. Every * service the package consumes is implemented; consumers can drop the * returned object straight into a `` * and every viewer-only feature works. * * @public */ export declare function createBrowserViewerServices(opts: BrowserViewerServicesOptions): BrowserViewerServices; /** * React hook that re-renders whenever a {@link BrowserViewerServices} * instance fires a `subscribe` event (i.e. a lazily-built page tile, * channel image, heatmap, or annotation has become available). * * Use this in the top-level component that holds the services instance * so children re-read the synchronous URL builders and pick up freshly- * cached blob URLs. * * @public */ export declare function useBrowserViewerServicesVersion(services: BrowserViewerServices | null | undefined): number; //# sourceMappingURL=index.d.ts.map