@forgeroute/ui 0.4.57 → 0.4.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"forgeroute-ui-pdf.mjs","names":[],"sources":["../../../projects/forgeroute-ui/pdf/src/pdf-viewer/pdf-viewer.component.ts","../../../projects/forgeroute-ui/pdf/src/provide-forgeroute-pdf.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n PLATFORM_ID, Component, ChangeDetectionStrategy, DestroyRef, ElementRef, InjectionToken, Injector, afterNextRender, computed, effect,\n inject, input, output, signal, untracked,\n} from '@angular/core';\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\n\n/** Where pdf.js finds its worker script. Provide it once in the app (`{ provide: PDF_VIEWER_WORKER_SRC, useValue: '/assets/pdf.worker.min.mjs' }`). */\nexport const PDF_VIEWER_WORKER_SRC = new InjectionToken<string>('PDF_VIEWER_WORKER_SRC', { providedIn: 'root', factory: () => '' });\n\n/** Colour of an overlay badge. */\nexport type PdfViewerTone = 'blue' | 'green' | 'amber' | 'red' | 'slate';\n\n/** A labelled box drawn over a page. Positions are PDF points with the origin at the page's bottom-left corner. */\nexport interface PdfViewerField {\n id: string;\n /** 1-based page number; a field on a page that does not exist is ignored. */\n page: number;\n /** Bottom-left corner of the box, in PDF points. */\n x: number;\n y: number;\n /** Size in PDF points. Without a width the box fits its label; the default height is 14. */\n width?: number;\n height?: number;\n label: string;\n tone?: PdfViewerTone;\n}\n\nexport interface PdfViewerLoadedEvent {\n pageCount: number;\n /** Every page's size in PDF points at scale 1. */\n pages: { width: number; height: number }[];\n /** The loaded pdf.js document, so the host can read text content. The viewer destroys it when it is replaced or the viewer goes away. */\n document: PDFDocumentProxy;\n}\n\nexport interface PdfViewerRenderedEvent {\n pageCount: number;\n /** CSS px per PDF point of the first page. */\n scale: number;\n /** Displayed (CSS px) width of every page. */\n pageWidths: number[];\n}\n\ntype PdfJsModule = typeof import('pdfjs-dist');\ntype Status = 'idle' | 'loading' | 'ready' | 'error' | 'unsupported';\n\ninterface PageView {\n number: number;\n cssWidth: number;\n cssHeight: number;\n canvasWidth: number;\n canvasHeight: number;\n fields: FieldView[];\n}\ninterface FieldView {\n id: string; label: string; tone: PdfViewerTone;\n left: number; top: number; width: number | null; maxWidth: number; height: number; fontPx: number;\n}\n\nconst DEFAULT_MAX_WIDTH = 700;\nconst DEFAULT_FIELD_HEIGHT = 14;\n/** Most pixels one page's canvas may have; Safari (iOS especially) refuses or blanks bigger ones. */\nconst MAX_CANVAS_PIXELS = 16_000_000;\n/** Most pixels all canvases together may have; iOS Safari limits total canvas memory. */\nconst MAX_TOTAL_CANVAS_PIXELS = 48_000_000;\nconst WORKER_MISSING = 'PDF worker is not configured.';\n\n/**\n * Language features the pdf.js modern build calls WITHOUT a polyfill or guard on the path every document takes, found by reading\n * `pdfjs-dist/build/pdf.mjs` + `pdf.worker.mjs` of 5.7.284 (this library's dev copy) and 6.2.108 (the apps' copy). The `legacy/` build\n * polyfills them; this component imports the modern one. Details and the evidence per feature: docs/pdf-viewer.md.\n *\n * - `Promise.withResolvers`, `Promise.try`: the main-thread API and the worker message handler (every message).\n * - `Map.prototype.getOrInsertComputed`: per-page intent state and the object store on every render (6.x also in the message handler).\n * - `Math.sumPrecise`: the worker, when it builds a font's name / glyf tables (every document with text).\n * - `Iterator`: pdf.js 6.x touches `Iterator.prototype` while the module loads, so a browser without the global cannot even import it.\n * - `URL.parse`: opening a document by URL (the stream choice and the worker origin check).\n *\n * Left out on purpose: `Worker` and `structuredClone` (pdf.js falls back to a main-thread worker that only needs one of them),\n * `OffscreenCanvas` / `Float16Array` (feature-tested by pdf.js), `Uint8Array.fromBase64` / `toBase64` and `Iterator` helpers (XFA / editor / text-layer paths only).\n */\nexport const PDF_VIEWER_REQUIRED_FEATURES = [\n 'Promise.withResolvers', 'Promise.try', 'Map.prototype.getOrInsertComputed', 'Math.sumPrecise', 'Iterator', 'URL.parse',\n] as const;\nexport type PdfViewerRequiredFeature = typeof PDF_VIEWER_REQUIRED_FEATURES[number];\n\n/**\n * Names of the required features `scope` (default: the global object) lacks; [] = pdf.js can run. Synchronous and\n * side-effect free. `URL.parse` is only needed to open a URL, so it is skipped when `needsUrl` is false (bytes are given).\n */\nexport function findMissingPdfFeatures(scope: object = globalThis, needsUrl = true): PdfViewerRequiredFeature[] {\n const g = scope as Record<string, any>;\n const has: Record<PdfViewerRequiredFeature, () => boolean> = {\n 'Promise.withResolvers': () => typeof g['Promise']?.withResolvers === 'function',\n 'Promise.try': () => typeof g['Promise']?.try === 'function',\n 'Map.prototype.getOrInsertComputed': () => typeof g['Map']?.prototype?.getOrInsertComputed === 'function',\n 'Math.sumPrecise': () => typeof g['Math']?.sumPrecise === 'function',\n 'Iterator': () => typeof g['Iterator'] === 'function' && typeof g['Iterator'].prototype === 'object',\n 'URL.parse': () => !needsUrl || typeof g['URL']?.parse === 'function',\n };\n return PDF_VIEWER_REQUIRED_FEATURES.filter(f => !has[f]());\n}\n\n/** Emitted by `loadFailed` when the browser lacks a feature pdf.js needs (nothing was loaded). */\nexport class PdfViewerUnsupportedError extends Error {\n readonly reason = 'unsupported-browser' as const;\n constructor(readonly missing: readonly PdfViewerRequiredFeature[]) {\n super('This browser cannot run pdf.js; missing: ' + missing.join(', '));\n this.name = 'PdfViewerUnsupportedError';\n }\n}\n\n/**\n * Renders every page of a PDF into a canvas (crisp on high-density screens), with optional labelled boxes overlaid in PDF coordinates.\n * Needs `pdfjs-dist` (loaded on first use, so it stays out of bundles that never show a PDF).\n *\n * <ui-pdf-viewer [data]=\"buffer()\" [fields]=\"badges()\" ariaLabel=\"Contract preview\" (loaded)=\"onLoaded($event)\" />\n * <ui-pdf-viewer src=\"/files/contract.pdf\" [httpHeaders]=\"{ Authorization: 'Bearer ' + token }\" />\n *\n * - `src` / `data`: a URL, or the bytes (`data` wins). The bytes are copied, so your buffer stays usable.\n * - `workerSrc` or the `PDF_VIEWER_WORKER_SRC` token: the pdf.js worker URL. Without either the viewer shows \"PDF worker is not configured.\"\n * - `maxPageWidth` (default 700): pages are drawn this wide, or the container's width when narrower; never larger.\n * - `pixelRatio`: canvas pixels per CSS pixel; default is the screen's ratio between 1 and 3.\n * - `fields`: badges positioned in PDF points (origin bottom-left); they scale with the page.\n * - `loaded` / `pagesRendered` / `loadFailed`: see the event types. A new `src` / `data` replaces the document; a slow older load never paints.\n * A page that fails to draw after `loaded` also emits `loadFailed`, shows the error state and destroys the document that `loaded` handed to the host.\n * - A page's canvas never exceeds 16,000,000 pixels (Safari limit); the pixel ratio is lowered for such a page, its CSS size is unchanged.\n * - All canvases together never exceed 48,000,000 pixels (iOS Safari limits total canvas memory): a long document is drawn at a lower\n * density, the same for every page and never below 1 pixel per CSS pixel. CSS sizes are unchanged.\n * - `httpHeaders` and `withCredentials` apply to `src` only; they are not used when `data` is given.\n * - `fields[].id` is only a display / test hook; duplicates are tolerated. `httpHeaders` is compared by content, but a stable reference is still preferred.\n * - The worker-missing message is fixed text and ignores `errorText`, because it is a setup problem for the developer.\n * - Needs pdf.js 5 (`pdfjs-dist` >= 5); the legacy pdf.js build is not supported by this component.\n * - Browser check: before pdf.js is imported, the viewer checks (once per load, synchronously) that the browser has the features\n * `PDF_VIEWER_REQUIRED_FEATURES` lists. Without them it shows `unsupportedText` (role=\"status\") and, when `src` is a URL, an\n * \"Open the document\" link (new tab); `loadFailed` emits a `PdfViewerUnsupportedError` (`reason: 'unsupported-browser'`, `missing`).\n * `[checkBrowserSupport]=\"false\"` skips the check. See docs/pdf-viewer.md.\n * - The public typings reference `pdfjs-dist` types: apps that type-check the library without `pdfjs-dist` installed need `skipLibCheck` or the\n * package (the same trade-off as `@forgeroute/ui/charts` and chart.js).\n */\n@Component({\n selector: 'ui-pdf-viewer',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"pv-root\" [attr.role]=\"ariaLabel() ? 'region' : null\" [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"status() === 'loading'\">\n <div class=\"pv-live\" role=\"status\" aria-live=\"polite\">{{ liveText() }}</div>\n @switch (status()) {\n @case ('loading') { <div class=\"pv-state pv-loading\" aria-hidden=\"true\">{{ loadingText() }}</div> }\n @case ('error') { <div class=\"pv-state pv-error\" role=\"alert\">{{ errorMessage() }}</div> }\n @case ('unsupported') {\n <div class=\"pv-state pv-unsupported\">\n <p class=\"pv-unsupported-text\">{{ unsupportedText() }}</p>\n @if (openUrl(); as url) {\n <a class=\"pv-open\" [href]=\"url\" target=\"_blank\" rel=\"noopener\">{{ openText() }}<span class=\"pv-sr\"> (opens in a new tab)</span></a>\n }\n </div>\n }\n @case ('ready') {\n <div class=\"pv-pages\">\n @for (p of views(); track p.number) {\n <div class=\"pv-page\" [style.width.px]=\"p.cssWidth\">\n <canvas class=\"pv-canvas\" role=\"img\" [attr.aria-label]=\"'Page ' + p.number + ' of ' + views().length\"\n [attr.width]=\"p.canvasWidth\" [attr.height]=\"p.canvasHeight\"></canvas>\n @for (f of p.fields; track $index) {\n <div class=\"pv-field pv-tone-{{ f.tone }}\"\n [style.left.%]=\"f.left\" [style.top.%]=\"f.top\" [style.width.%]=\"f.width\" [style.max-width.%]=\"f.maxWidth\"\n [style.height.%]=\"f.height\" [style.font-size.px]=\"f.fontPx\">\n <span class=\"pv-field-label\">{{ f.label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n `,\n styles: [`\n :host { display: block; width: 100%; }\n .pv-root { position: relative; }\n .pv-live, .pv-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }\n .pv-state { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 12rem; padding: 0 16px; text-align: center;\n font-size: 0.8125rem; line-height: 1.4; overflow-wrap: anywhere; box-sizing: border-box; }\n .pv-loading { color: #64748b; }\n .pv-error { color: #dc2626; }\n .pv-unsupported { flex-direction: column; gap: 8px; color: #334155; }\n .pv-unsupported-text { margin: 0; }\n .pv-open { display: inline-flex; align-items: center; justify-content: center; position: relative; min-height: 44px; min-width: 44px; padding: 0 16px;\n box-sizing: border-box; color: #1d4ed8; font-weight: 600; text-decoration: underline; border-radius: 6px; }\n .pv-open:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }\n .pv-pages { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 100%; }\n .pv-page { position: relative; margin: 0 auto; max-width: 100%; }\n .pv-canvas { display: block; width: 100%; height: auto; background: #fff; border-radius: 2px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); }\n .pv-field { position: absolute; display: flex; align-items: center; box-sizing: border-box; padding: 0 4px; overflow: hidden;\n pointer-events: none; border: 1px solid; border-radius: 2px; -webkit-user-select: none; user-select: none; }\n .pv-field-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; }\n .pv-tone-blue { background: #dbeafe; border-color: #93c5fd; color: #1d4ed8; }\n .pv-tone-green { background: #dcfce7; border-color: #86efac; color: #15803d; }\n .pv-tone-amber { background: #fef3c7; border-color: #fcd34d; color: #92400e; }\n .pv-tone-red { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }\n .pv-tone-slate { background: #f1f5f9; border-color: #cbd5e1; color: #334155; }\n `],\n})\nexport class PdfViewerComponent {\n /** PDF URL. Ignored while `data` is set. */\n readonly src = input<string | null>(null);\n /** PDF bytes. Wins over `src`. */\n readonly data = input<ArrayBuffer | Uint8Array | null>(null);\n /** Request headers for `src`. Changes are detected by content, but pass a stable reference when you can. */\n readonly httpHeaders = input<Record<string, string>>({});\n readonly withCredentials = input(false);\n /** pdf.js worker URL; falls back to the `PDF_VIEWER_WORKER_SRC` token. */\n readonly workerSrc = input('');\n /** Widest a page is drawn, in CSS px. */\n readonly maxPageWidth = input(DEFAULT_MAX_WIDTH);\n /** Canvas pixels per CSS pixel; null = the screen's ratio, limited to 1..3. */\n readonly pixelRatio = input<number | null>(null);\n readonly fields = input<PdfViewerField[]>([]);\n /** Accessible name of the viewer. */\n readonly ariaLabel = input('');\n readonly loadingText = input('Loading document…');\n readonly errorText = input('Failed to load document.');\n /** Shown when the browser cannot run pdf.js (see `checkBrowserSupport`). */\n readonly unsupportedText = input(\"This browser can't show the document preview.\");\n /** Text of the link that opens `src` in a new tab on such a browser. */\n readonly openText = input('Open the document');\n /** Check for the browser features pdf.js needs before loading it; false = always try to load. */\n readonly checkBrowserSupport = input(true);\n\n readonly loaded = output<PdfViewerLoadedEvent>();\n readonly loadFailed = output<unknown>();\n readonly pagesRendered = output<PdfViewerRenderedEvent>();\n\n protected readonly status = signal<Status>('idle');\n private readonly pageSizes = signal<{ width: number; height: number }[]>([]);\n private readonly containerWidth = signal(0);\n private readonly customError = signal('');\n /** The URL the \"Open the document\" link points at: `src` when no bytes are given. */\n protected readonly openUrl = computed(() => (this.data() == null ? this.src() || '' : ''));\n protected readonly errorMessage = computed(() => this.customError() || this.errorText());\n /** What the always-present live region announces: loading progress, or why no preview is shown (a live region that already exists is announced reliably; one inserted with its text is not). */\n protected readonly liveText = computed(() => {\n const status = this.status();\n return status === 'loading' ? this.loadingText() : status === 'unsupported' ? this.unsupportedText() : '';\n });\n\n /** Changes exactly when some canvas bitmap changes size (and so is cleared). */\n private readonly drawKey = computed(() => this.views().map(v => v.canvasWidth + 'x' + v.canvasHeight).join('|'));\n private readonly effectiveWidth = computed(() => this.widthFor(this.containerWidth()));\n protected readonly views = computed((): PageView[] => {\n const sizes = this.pageSizes();\n const cssWidth = this.effectiveWidth();\n const ratio = this.resolvedRatio();\n const byPage = new Map<number, PdfViewerField[]>();\n for (const f of this.fields()) {\n if (!Number.isInteger(f.page) || f.page < 1 || f.page > sizes.length) continue;\n const list = byPage.get(f.page) ?? [];\n list.push(f);\n byPage.set(f.page, list);\n }\n const base = sizes.map(size => {\n const scale = cssWidth / size.width;\n const cssHeight = size.height * scale;\n // lower the density for a page whose bitmap would pass the canvas limit; the CSS size stays\n const pageRatio = Math.min(ratio, Math.sqrt(MAX_CANVAS_PIXELS / (cssWidth * cssHeight)));\n return { scale, cssHeight, pageRatio };\n });\n // a total budget for long documents: shrink every page's density by the same factor, but never below 1 (or below a cap-lowered ratio)\n let factor = 1;\n const bitmaps = () => base.map(b => {\n const r = Math.max(Math.min(b.pageRatio, 1), b.pageRatio * factor);\n return { w: Math.floor(cssWidth * r), h: Math.floor(b.cssHeight * r) };\n });\n let sized = bitmaps();\n for (let i = 0; i < 6; i++) {\n const total = sized.reduce((sum, b) => sum + b.w * b.h, 0);\n if (total <= MAX_TOTAL_CANVAS_PIXELS) break;\n factor *= Math.sqrt(MAX_TOTAL_CANVAS_PIXELS / total);\n sized = bitmaps();\n }\n return sizes.map((size, i) => {\n const { scale, cssHeight } = base[i];\n const fields = (byPage.get(i + 1) ?? []).map((f): FieldView => {\n const h = f.height ?? DEFAULT_FIELD_HEIGHT;\n const left = (f.x / size.width) * 100;\n return {\n id: f.id, label: f.label, tone: f.tone ?? 'blue',\n left, top: ((size.height - f.y - h) / size.height) * 100,\n width: f.width == null ? null : (f.width / size.width) * 100,\n maxWidth: Math.max(0, 100 - left),\n height: (h / size.height) * 100,\n fontPx: Math.max(9, h * scale * 0.7),\n };\n });\n return { number: i + 1, cssWidth, cssHeight, canvasWidth: sized[i].w, canvasHeight: sized[i].h, fields };\n });\n });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n private readonly tokenWorkerSrc = inject(PDF_VIEWER_WORKER_SRC);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly headersKey = computed(() => JSON.stringify(this.httpHeaders()));\n\n /** Bumped by every new load, replacement and destroy; an async step whose token no longer matches stops. */\n private loadToken = 0;\n /** Bumped by every (re)draw so only the newest draw paints. */\n private drawToken = 0;\n private doc: PDFDocumentProxy | null = null;\n private loadingTask: { destroy?: () => unknown } | null = null;\n private pageProxies: PDFPageProxy[] = [];\n private renderTasks: { cancel?: () => void }[] = [];\n private observer: ResizeObserver | null = null;\n private frame = 0;\n private lastDrawKey = '';\n\n constructor() {\n effect(() => {\n this.src(); this.data(); this.headersKey(); this.withCredentials(); this.workerSrc(); this.checkBrowserSupport();\n if (!this.isBrowser) return; // nothing to load or draw on the server\n untracked(() => void this.startLoad());\n });\n // Whatever resizes the canvases (maxPageWidth, pixelRatio) clears them, so a changed bitmap size needs a redraw. Draws the code\n // already scheduled record their key in scheduleDraw(), so this only fires for changes nothing else handled.\n effect(() => {\n const key = this.drawKey();\n untracked(() => { if (this.status() === 'ready' && key !== this.lastDrawKey) this.scheduleDraw(); });\n });\n if (this.isBrowser && typeof ResizeObserver !== 'undefined') {\n this.observer = new ResizeObserver(() => this.onResize());\n this.observer.observe(this.host.nativeElement);\n }\n inject(DestroyRef).onDestroy(() => {\n this.loadToken++;\n this.drawToken++;\n this.observer?.disconnect();\n if (this.frame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(this.frame);\n this.teardown();\n });\n }\n\n /** Test seam: loads pdf.js on first use so apps that never show a PDF do not bundle or fetch it. */\n protected loadPdfJs(): Promise<PdfJsModule> {\n return import('pdfjs-dist');\n }\n\n /** Test seam: the required features this browser lacks. */\n protected missingFeatures(needsUrl: boolean): readonly PdfViewerRequiredFeature[] {\n return findMissingPdfFeatures(globalThis, needsUrl);\n }\n\n private resolvedRatio(): number {\n const explicit = this.pixelRatio();\n if (explicit != null && explicit > 0) return explicit;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n return Math.min(Math.max(dpr, 1), 3);\n }\n\n private widthFor(container: number): number {\n const max = this.maxPageWidth() > 0 ? this.maxPageWidth() : DEFAULT_MAX_WIDTH;\n return container > 0 ? Math.min(max, container) : max;\n }\n\n private async startLoad(): Promise<void> {\n const token = ++this.loadToken;\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set('');\n const data = this.data();\n const src = this.src();\n if (data == null && !src) { this.status.set('idle'); return; }\n if (this.checkBrowserSupport()) {\n const missing = this.missingFeatures(data == null);\n if (missing.length > 0) { // pdf.js would throw on first use: do not even import it\n this.loadToken++;\n this.pageSizes.set([]);\n this.status.set('unsupported');\n this.loadFailed.emit(new PdfViewerUnsupportedError(missing));\n return;\n }\n }\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n this.status.set('loading');\n try {\n const pdfjs = await this.loadPdfJs();\n if (token !== this.loadToken) return;\n const worker = this.workerSrc() || this.tokenWorkerSrc;\n if (worker) pdfjs.GlobalWorkerOptions.workerSrc = worker;\n else if (!pdfjs.GlobalWorkerOptions.workerSrc) { this.fail(new Error(WORKER_MISSING), WORKER_MISSING); return; }\n\n const params: Record<string, unknown> = data != null\n ? { data: data instanceof Uint8Array ? data.slice() : new Uint8Array(data.slice(0)) } // pdf.js detaches what it is given\n : { url: src };\n if (data == null) { // request options only make sense for a URL\n const headers = this.httpHeaders();\n if (Object.keys(headers).length > 0) params['httpHeaders'] = headers;\n if (this.withCredentials()) params['withCredentials'] = true;\n }\n const task = pdfjs.getDocument(params as unknown as Parameters<PdfJsModule['getDocument']>[0]);\n this.loadingTask = task;\n const doc = await task.promise;\n if (token !== this.loadToken) { this.destroyDoc(doc); return; }\n this.doc = doc;\n\n const proxies: PDFPageProxy[] = [];\n const sizes: { width: number; height: number }[] = [];\n for (let i = 1; i <= doc.numPages; i++) {\n const page = await doc.getPage(i);\n if (token !== this.loadToken) return;\n const vp = page.getViewport({ scale: 1 });\n if (!(Number.isFinite(vp.width) && Number.isFinite(vp.height) && vp.width > 0 && vp.height > 0)) throw new Error('invalid page size');\n proxies.push(page);\n sizes.push({ width: vp.width, height: vp.height });\n }\n this.pageProxies = proxies;\n this.containerWidth.set(this.host.nativeElement.clientWidth); // the container may have changed while the file loaded\n this.pageSizes.set(sizes);\n this.status.set('ready');\n this.loaded.emit({ pageCount: sizes.length, pages: sizes.map(s => ({ ...s })), document: doc });\n this.scheduleDraw();\n } catch (err) {\n if (token !== this.loadToken) return;\n this.fail(err);\n }\n }\n\n private fail(err: unknown, message = ''): void {\n this.loadToken++; // nothing still in flight may paint over the error\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set(message);\n this.status.set('error');\n this.loadFailed.emit(err);\n }\n\n private scheduleDraw(): void {\n const id = ++this.drawToken;\n this.lastDrawKey = this.drawKey();\n this.cancelRenders(); // a still-running paint must not land in canvases about to be resized\n afterNextRender(() => void this.draw(id), { injector: this.injector });\n }\n\n private async draw(id: number): Promise<void> {\n const token = this.loadToken;\n const stale = () => id !== this.drawToken || token !== this.loadToken;\n this.cancelRenders();\n const views = this.views();\n const canvases = Array.from(this.host.nativeElement.querySelectorAll<HTMLCanvasElement>('canvas.pv-canvas'));\n const sizes = this.pageSizes();\n const scales: number[] = [];\n try {\n for (let i = 0; i < views.length; i++) {\n if (stale()) return;\n const canvas = canvases[i];\n const page = this.pageProxies[i];\n if (!canvas || !page || !canvas.getContext('2d')) throw new Error('canvas unavailable');\n const cssScale = views[i].cssWidth / sizes[i].width;\n scales.push(cssScale);\n const viewport = page.getViewport({ scale: (views[i].canvasWidth / sizes[i].width) });\n const task = page.render({ canvas, viewport });\n this.renderTasks.push(task);\n await task.promise;\n }\n } catch (err) {\n if (stale() || (err as { name?: string } | null)?.name === 'RenderingCancelledException') return;\n this.fail(err);\n return;\n }\n if (stale()) return;\n this.pagesRendered.emit({ pageCount: views.length, scale: scales[0] ?? 1, pageWidths: views.map(v => v.cssWidth) });\n }\n\n private onResize(): void {\n if (this.frame || typeof requestAnimationFrame !== 'function') return;\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n const before = this.effectiveWidth();\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n if (this.effectiveWidth() !== before && this.status() === 'ready') this.scheduleDraw();\n });\n }\n\n private cancelRenders(): void {\n for (const t of this.renderTasks) { try { t.cancel?.(); } catch { /* already finished */ } }\n this.renderTasks = [];\n }\n\n private teardown(): void {\n this.cancelRenders();\n const task = this.loadingTask;\n this.loadingTask = null;\n if (task) { try { Promise.resolve(task.destroy?.()).catch(() => undefined); } catch { /* ignore */ } }\n const doc = this.doc;\n this.doc = null;\n this.pageProxies = [];\n if (doc) this.destroyDoc(doc);\n }\n\n private destroyDoc(doc: PDFDocumentProxy): void {\n try { Promise.resolve(doc.destroy()).catch(() => undefined); } catch { /* ignore */ }\n }\n}\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { PDF_VIEWER_WORKER_SRC } from './pdf-viewer/pdf-viewer.component';\n\nexport interface ForgeroutePdfOptions {\n /** URL of the pdf.js worker script every `ui-pdf-viewer` in the app uses, e.g. `'/pdf.worker.min.mjs'`. Must not be blank. */\n workerSrc: string;\n}\n\n/**\n * Sets the pdf.js worker location once for the whole app (provides `PDF_VIEWER_WORKER_SRC`):\n *\n * providers: [provideForgeroutePdf({ workerSrc: '/pdf.worker.min.mjs' })]\n *\n * A `workerSrc` input on a viewer still wins over this value. Throws at provide time when `workerSrc` is blank, because a viewer\n * without a worker can only show \"PDF worker is not configured.\".\n */\nexport function provideForgeroutePdf(options: ForgeroutePdfOptions): EnvironmentProviders {\n const workerSrc = typeof options?.workerSrc === 'string' ? options.workerSrc.trim() : '';\n if (!workerSrc) {\n throw new Error('provideForgeroutePdf: \"workerSrc\" must be a non-empty URL of the pdf.js worker script (for example \"/pdf.worker.min.mjs\").');\n }\n return makeEnvironmentProviders([{ provide: PDF_VIEWER_WORKER_SRC, useValue: workerSrc }]);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"forgeroute-ui-pdf.mjs","names":[],"sources":["../../../projects/forgeroute-ui/pdf/src/pdf-viewer/pdf-viewer.component.ts","../../../projects/forgeroute-ui/pdf/src/provide-forgeroute-pdf.ts","../../../projects/forgeroute-ui/pdf/src/document-viewer-modal/document-viewer-modal.component.ts","../../../projects/forgeroute-ui/pdf/src/document-sign-modal/document-sign-modal.component.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n PLATFORM_ID, Component, ChangeDetectionStrategy, DestroyRef, ElementRef, InjectionToken, Injector, afterNextRender, computed, effect,\n inject, input, output, signal, untracked,\n} from '@angular/core';\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\n\n/** Where pdf.js finds its worker script. Provide it once in the app (`{ provide: PDF_VIEWER_WORKER_SRC, useValue: '/assets/pdf.worker.min.mjs' }`). */\nexport const PDF_VIEWER_WORKER_SRC = new InjectionToken<string>('PDF_VIEWER_WORKER_SRC', { providedIn: 'root', factory: () => '' });\n\n/** Colour of an overlay badge. */\nexport type PdfViewerTone = 'blue' | 'green' | 'amber' | 'red' | 'slate';\n\n/** A labelled box drawn over a page. Positions are PDF points with the origin at the page's bottom-left corner. */\nexport interface PdfViewerField {\n id: string;\n /** 1-based page number; a field on a page that does not exist is ignored. */\n page: number;\n /** Bottom-left corner of the box, in PDF points. */\n x: number;\n y: number;\n /** Size in PDF points. Without a width the box fits its label; the default height is 14. */\n width?: number;\n height?: number;\n label: string;\n tone?: PdfViewerTone;\n}\n\n/** State of a clickable spot over a page. */\nexport type PdfViewerHotspotState = 'ready' | 'blocked' | 'signed';\n\n/** A clickable box drawn over a page (for example \"Sign here\"). Positions are PDF points with the origin at the page's bottom-left corner. */\nexport interface PdfViewerHotspot {\n id: string;\n /** 1-based page number; a hotspot on a page that does not exist is ignored. */\n page: number;\n x: number;\n y: number;\n width: number;\n height: number;\n /** `ready` = a button, `blocked` = the same button disabled (shows `blockedLabel`), `signed` = shows `image` instead of a button. */\n state: PdfViewerHotspotState;\n label?: string;\n blockedLabel?: string;\n ariaLabel?: string;\n /** The image (a data URL) shown while `state` is `signed`. */\n image?: string | null;\n imageAlt?: string;\n}\n\nexport interface PdfViewerLoadedEvent {\n pageCount: number;\n /** Every page's size in PDF points at scale 1. */\n pages: { width: number; height: number }[];\n /** The loaded pdf.js document, so the host can read text content. The viewer destroys it when it is replaced or the viewer goes away. */\n document: PDFDocumentProxy;\n}\n\nexport interface PdfViewerRenderedEvent {\n pageCount: number;\n /** CSS px per PDF point of the first page. */\n scale: number;\n /** Displayed (CSS px) width of every page. */\n pageWidths: number[];\n}\n\ntype PdfJsModule = typeof import('pdfjs-dist');\ntype Status = 'idle' | 'loading' | 'ready' | 'error' | 'unsupported';\n\ninterface PageView {\n number: number;\n cssWidth: number;\n cssHeight: number;\n canvasWidth: number;\n canvasHeight: number;\n fields: FieldView[];\n hotspots: HotspotView[];\n}\ninterface HotspotView { hotspot: PdfViewerHotspot; left: number; top: number; width: number; height: number; fontPx: number }\ninterface FieldView {\n id: string; label: string; tone: PdfViewerTone;\n left: number; top: number; width: number | null; maxWidth: number; height: number; fontPx: number;\n}\n\nconst DEFAULT_MAX_WIDTH = 700;\nconst DEFAULT_FIELD_HEIGHT = 14;\n/** Most pixels one page's canvas may have; Safari (iOS especially) refuses or blanks bigger ones. */\nconst MAX_CANVAS_PIXELS = 16_000_000;\n/** Most pixels all canvases together may have; iOS Safari limits total canvas memory. */\nconst MAX_TOTAL_CANVAS_PIXELS = 48_000_000;\nconst WORKER_MISSING = 'PDF worker is not configured.';\n\n/**\n * Language features the pdf.js modern build calls WITHOUT a polyfill or guard on the path every document takes, found by reading\n * `pdfjs-dist/build/pdf.mjs` + `pdf.worker.mjs` of 5.7.284 (this library's dev copy) and 6.2.108 (the apps' copy). The `legacy/` build\n * polyfills them; this component imports the modern one. Details and the evidence per feature: docs/pdf-viewer.md.\n *\n * - `Promise.withResolvers`, `Promise.try`: the main-thread API and the worker message handler (every message).\n * - `Map.prototype.getOrInsertComputed`: per-page intent state and the object store on every render (6.x also in the message handler).\n * - `Math.sumPrecise`: the worker, when it builds a font's name / glyf tables (every document with text).\n * - `Iterator`: pdf.js 6.x touches `Iterator.prototype` while the module loads, so a browser without the global cannot even import it.\n * - `URL.parse`: opening a document by URL (the stream choice and the worker origin check).\n *\n * Left out on purpose: `Worker` and `structuredClone` (pdf.js falls back to a main-thread worker that only needs one of them),\n * `OffscreenCanvas` / `Float16Array` (feature-tested by pdf.js), `Uint8Array.fromBase64` / `toBase64` and `Iterator` helpers (XFA / editor / text-layer paths only).\n */\nexport const PDF_VIEWER_REQUIRED_FEATURES = [\n 'Promise.withResolvers', 'Promise.try', 'Map.prototype.getOrInsertComputed', 'Math.sumPrecise', 'Iterator', 'URL.parse',\n] as const;\nexport type PdfViewerRequiredFeature = typeof PDF_VIEWER_REQUIRED_FEATURES[number];\n\n/**\n * Names of the required features `scope` (default: the global object) lacks; [] = pdf.js can run. Synchronous and\n * side-effect free. `URL.parse` is only needed to open a URL, so it is skipped when `needsUrl` is false (bytes are given).\n */\nexport function findMissingPdfFeatures(scope: object = globalThis, needsUrl = true): PdfViewerRequiredFeature[] {\n const g = scope as Record<string, any>;\n const has: Record<PdfViewerRequiredFeature, () => boolean> = {\n 'Promise.withResolvers': () => typeof g['Promise']?.withResolvers === 'function',\n 'Promise.try': () => typeof g['Promise']?.try === 'function',\n 'Map.prototype.getOrInsertComputed': () => typeof g['Map']?.prototype?.getOrInsertComputed === 'function',\n 'Math.sumPrecise': () => typeof g['Math']?.sumPrecise === 'function',\n 'Iterator': () => typeof g['Iterator'] === 'function' && typeof g['Iterator'].prototype === 'object',\n 'URL.parse': () => !needsUrl || typeof g['URL']?.parse === 'function',\n };\n return PDF_VIEWER_REQUIRED_FEATURES.filter(f => !has[f]());\n}\n\n/** Emitted by `loadFailed` when the browser lacks a feature pdf.js needs (nothing was loaded). */\nexport class PdfViewerUnsupportedError extends Error {\n readonly reason = 'unsupported-browser' as const;\n constructor(readonly missing: readonly PdfViewerRequiredFeature[]) {\n super('This browser cannot run pdf.js; missing: ' + missing.join(', '));\n this.name = 'PdfViewerUnsupportedError';\n }\n}\n\n/**\n * Renders every page of a PDF into a canvas (crisp on high-density screens), with optional labelled boxes overlaid in PDF coordinates.\n * Needs `pdfjs-dist` (loaded on first use, so it stays out of bundles that never show a PDF).\n *\n * <ui-pdf-viewer [data]=\"buffer()\" [fields]=\"badges()\" ariaLabel=\"Contract preview\" (loaded)=\"onLoaded($event)\" />\n * <ui-pdf-viewer src=\"/files/contract.pdf\" [httpHeaders]=\"{ Authorization: 'Bearer ' + token }\" />\n *\n * - `src` / `data`: a URL, or the bytes (`data` wins). The bytes are copied, so your buffer stays usable.\n * - `workerSrc` or the `PDF_VIEWER_WORKER_SRC` token: the pdf.js worker URL. Without either the viewer shows \"PDF worker is not configured.\"\n * - `maxPageWidth` (default 700): pages are drawn this wide, or the container's width when narrower; never larger.\n * - `pixelRatio`: canvas pixels per CSS pixel; default is the screen's ratio between 1 and 3.\n * - `fields`: badges positioned in PDF points (origin bottom-left); they scale with the page.\n * - `hotspots`: clickable boxes (e.g. \"Sign here\") in the same coordinates; `ready` is a button, `blocked` a disabled one, `signed` shows an image. `(hotspotActivate)` gives the id.\n * - `loaded` / `pagesRendered` / `loadFailed`: see the event types. A new `src` / `data` replaces the document; a slow older load never paints.\n * A page that fails to draw after `loaded` also emits `loadFailed`, shows the error state and destroys the document that `loaded` handed to the host.\n * - A page's canvas never exceeds 16,000,000 pixels (Safari limit); the pixel ratio is lowered for such a page, its CSS size is unchanged.\n * - All canvases together never exceed 48,000,000 pixels (iOS Safari limits total canvas memory): a long document is drawn at a lower\n * density, the same for every page and never below 1 pixel per CSS pixel. CSS sizes are unchanged.\n * - `httpHeaders` and `withCredentials` apply to `src` only; they are not used when `data` is given.\n * - `fields[].id` is only a display / test hook; duplicates are tolerated. `httpHeaders` is compared by content, but a stable reference is still preferred.\n * - The worker-missing message is fixed text and ignores `errorText`, because it is a setup problem for the developer.\n * - Needs pdf.js 5 (`pdfjs-dist` >= 5); the legacy pdf.js build is not supported by this component.\n * - Browser check: before pdf.js is imported, the viewer checks (once per load, synchronously) that the browser has the features\n * `PDF_VIEWER_REQUIRED_FEATURES` lists. Without them it shows `unsupportedText` (role=\"status\") and, when `src` is a URL, an\n * \"Open the document\" link (new tab); `loadFailed` emits a `PdfViewerUnsupportedError` (`reason: 'unsupported-browser'`, `missing`).\n * `[checkBrowserSupport]=\"false\"` skips the check. See docs/pdf-viewer.md.\n * - The public typings reference `pdfjs-dist` types: apps that type-check the library without `pdfjs-dist` installed need `skipLibCheck` or the\n * package (the same trade-off as `@forgeroute/ui/charts` and chart.js).\n */\n@Component({\n selector: 'ui-pdf-viewer',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"pv-root\" [attr.role]=\"ariaLabel() ? 'region' : null\" [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"status() === 'loading'\">\n <div class=\"pv-live\" role=\"status\" aria-live=\"polite\">{{ liveText() }}</div>\n @switch (status()) {\n @case ('loading') { <div class=\"pv-state pv-loading\" aria-hidden=\"true\">{{ loadingText() }}</div> }\n @case ('error') { <div class=\"pv-state pv-error\" role=\"alert\">{{ errorMessage() }}</div> }\n @case ('unsupported') {\n <div class=\"pv-state pv-unsupported\">\n <p class=\"pv-unsupported-text\">{{ unsupportedText() }}</p>\n @if (openUrl(); as url) {\n <a class=\"pv-open\" [href]=\"url\" target=\"_blank\" rel=\"noopener\">{{ openText() }}<span class=\"pv-sr\"> (opens in a new tab)</span></a>\n }\n </div>\n }\n @case ('ready') {\n <div class=\"pv-pages\">\n @for (p of views(); track p.number) {\n <div class=\"pv-page\" [style.width.px]=\"p.cssWidth\">\n <canvas class=\"pv-canvas\" role=\"img\" [attr.aria-label]=\"'Page ' + p.number + ' of ' + views().length\"\n [attr.width]=\"p.canvasWidth\" [attr.height]=\"p.canvasHeight\"></canvas>\n @for (f of p.fields; track $index) {\n <div class=\"pv-field pv-tone-{{ f.tone }}\"\n [style.left.%]=\"f.left\" [style.top.%]=\"f.top\" [style.width.%]=\"f.width\" [style.max-width.%]=\"f.maxWidth\"\n [style.height.%]=\"f.height\" [style.font-size.px]=\"f.fontPx\">\n <span class=\"pv-field-label\">{{ f.label }}</span>\n </div>\n }\n @for (h of p.hotspots; track h.hotspot.id) {\n <div class=\"pv-hotspot\" [attr.data-state]=\"h.hotspot.state\" [attr.data-testid]=\"'pdf-hotspot-' + h.hotspot.id\"\n [style.left.%]=\"h.left\" [style.top.%]=\"h.top\" [style.width.%]=\"h.width\" [style.height.%]=\"h.height\">\n @if (h.hotspot.state === 'signed' && h.hotspot.image) {\n <img class=\"pv-hotspot-image\" [src]=\"h.hotspot.image\" [alt]=\"h.hotspot.imageAlt || 'Applied signature'\" />\n } @else {\n <button type=\"button\" class=\"pv-hotspot-button\" [class.pv-hotspot-ready]=\"h.hotspot.state === 'ready'\"\n [disabled]=\"h.hotspot.state !== 'ready'\" [attr.aria-label]=\"h.hotspot.ariaLabel || null\"\n [style.font-size.px]=\"h.fontPx\" (click)=\"hotspotActivate.emit(h.hotspot.id)\">\n <span class=\"pv-hotspot-text\">{{ h.hotspot.state === 'blocked' ? (h.hotspot.blockedLabel || h.hotspot.label || '') : (h.hotspot.label || '') }}</span>\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n `,\n styles: [`\n :host { display: block; width: 100%; }\n .pv-root { position: relative; }\n .pv-live, .pv-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }\n .pv-state { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 12rem; padding: 0 16px; text-align: center;\n font-size: 0.8125rem; line-height: 1.4; overflow-wrap: anywhere; box-sizing: border-box; }\n .pv-loading { color: #64748b; }\n .pv-error { color: #dc2626; }\n .pv-unsupported { flex-direction: column; gap: 8px; color: #334155; }\n .pv-unsupported-text { margin: 0; }\n .pv-open { display: inline-flex; align-items: center; justify-content: center; position: relative; min-height: 44px; min-width: 44px; padding: 0 16px;\n box-sizing: border-box; color: #1d4ed8; font-weight: 600; text-decoration: underline; border-radius: 6px; }\n .pv-open:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }\n .pv-pages { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 100%; }\n .pv-page { position: relative; margin: 0 auto; max-width: 100%; }\n .pv-canvas { display: block; width: 100%; height: auto; background: #fff; border-radius: 2px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); }\n .pv-field { position: absolute; display: flex; align-items: center; box-sizing: border-box; padding: 0 4px; overflow: hidden;\n pointer-events: none; border: 1px solid; border-radius: 2px; -webkit-user-select: none; user-select: none; }\n .pv-field-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; }\n .pv-hotspot { position: absolute; box-sizing: border-box; }\n .pv-hotspot-image { display: block; width: 100%; height: 100%; object-fit: contain; object-position: left; }\n .pv-hotspot-button { position: relative; box-sizing: border-box; width: 100%; height: 100%; white-space: nowrap; font: inherit; font-weight: 500; line-height: 1;\n border: 2px solid #d1d5db; background: #f3f4f6; color: #9ca3af; cursor: not-allowed; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18); }\n .pv-hotspot-button.pv-hotspot-ready { border-color: #2563eb; background: #dbeafe; color: #1d4ed8; cursor: pointer; }\n .pv-hotspot-button.pv-hotspot-ready:hover { background: #bfdbfe; }\n .pv-hotspot-button:focus-visible { outline: 2px solid #2563eb; outline-offset: 2px; }\n .pv-hotspot-text { display: block; overflow: hidden; text-overflow: ellipsis; line-height: 1.2; }\n /* A spot is only as tall as the document says: extend the tap area on touch screens, not the look. */\n @media (pointer: coarse) { .pv-hotspot-button::after { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 100%; min-height: 44px; transform: translateY(-50%); } }\n .pv-tone-blue { background: #dbeafe; border-color: #93c5fd; color: #1d4ed8; }\n .pv-tone-green { background: #dcfce7; border-color: #86efac; color: #15803d; }\n .pv-tone-amber { background: #fef3c7; border-color: #fcd34d; color: #92400e; }\n .pv-tone-red { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }\n .pv-tone-slate { background: #f1f5f9; border-color: #cbd5e1; color: #334155; }\n `],\n})\nexport class PdfViewerComponent {\n /** PDF URL. Ignored while `data` is set. */\n readonly src = input<string | null>(null);\n /** PDF bytes. Wins over `src`. */\n readonly data = input<ArrayBuffer | Uint8Array | null>(null);\n /** Request headers for `src`. Changes are detected by content, but pass a stable reference when you can. */\n readonly httpHeaders = input<Record<string, string>>({});\n readonly withCredentials = input(false);\n /** pdf.js worker URL; falls back to the `PDF_VIEWER_WORKER_SRC` token. */\n readonly workerSrc = input('');\n /** Widest a page is drawn, in CSS px. */\n readonly maxPageWidth = input(DEFAULT_MAX_WIDTH);\n /** Canvas pixels per CSS pixel; null = the screen's ratio, limited to 1..3. */\n readonly pixelRatio = input<number | null>(null);\n readonly fields = input<PdfViewerField[]>([]);\n /** Clickable boxes over the pages (e.g. \"Sign here\"), in PDF points with the origin bottom-left; `(hotspotActivate)` gives the id of the one clicked. */\n readonly hotspots = input<PdfViewerHotspot[]>([]);\n /** Accessible name of the viewer. */\n readonly ariaLabel = input('');\n readonly loadingText = input('Loading document…');\n readonly errorText = input('Failed to load document.');\n /** Shown when the browser cannot run pdf.js (see `checkBrowserSupport`). */\n readonly unsupportedText = input(\"This browser can't show the document preview.\");\n /** Text of the link that opens `src` in a new tab on such a browser. */\n readonly openText = input('Open the document');\n /** Check for the browser features pdf.js needs before loading it; false = always try to load. */\n readonly checkBrowserSupport = input(true);\n\n /** The id of the hotspot that was clicked (only a `ready` hotspot can be). */\n readonly hotspotActivate = output<string>();\n readonly loaded = output<PdfViewerLoadedEvent>();\n readonly loadFailed = output<unknown>();\n readonly pagesRendered = output<PdfViewerRenderedEvent>();\n\n protected readonly status = signal<Status>('idle');\n private readonly pageSizes = signal<{ width: number; height: number }[]>([]);\n private readonly containerWidth = signal(0);\n private readonly customError = signal('');\n /** The URL the \"Open the document\" link points at: `src` when no bytes are given. */\n protected readonly openUrl = computed(() => (this.data() == null ? this.src() || '' : ''));\n protected readonly errorMessage = computed(() => this.customError() || this.errorText());\n /** What the always-present live region announces: loading progress, or why no preview is shown (a live region that already exists is announced reliably; one inserted with its text is not). */\n protected readonly liveText = computed(() => {\n const status = this.status();\n return status === 'loading' ? this.loadingText() : status === 'unsupported' ? this.unsupportedText() : '';\n });\n\n /** Changes exactly when some canvas bitmap changes size (and so is cleared). */\n private readonly drawKey = computed(() => this.views().map(v => v.canvasWidth + 'x' + v.canvasHeight).join('|'));\n private readonly effectiveWidth = computed(() => this.widthFor(this.containerWidth()));\n protected readonly views = computed((): PageView[] => {\n const sizes = this.pageSizes();\n const cssWidth = this.effectiveWidth();\n const ratio = this.resolvedRatio();\n const byPage = new Map<number, PdfViewerField[]>();\n for (const f of this.fields()) {\n if (!Number.isInteger(f.page) || f.page < 1 || f.page > sizes.length) continue;\n const list = byPage.get(f.page) ?? [];\n list.push(f);\n byPage.set(f.page, list);\n }\n const base = sizes.map(size => {\n const scale = cssWidth / size.width;\n const cssHeight = size.height * scale;\n // lower the density for a page whose bitmap would pass the canvas limit; the CSS size stays\n const pageRatio = Math.min(ratio, Math.sqrt(MAX_CANVAS_PIXELS / (cssWidth * cssHeight)));\n return { scale, cssHeight, pageRatio };\n });\n // a total budget for long documents: shrink every page's density by the same factor, but never below 1 (or below a cap-lowered ratio)\n let factor = 1;\n const bitmaps = () => base.map(b => {\n const r = Math.max(Math.min(b.pageRatio, 1), b.pageRatio * factor);\n return { w: Math.floor(cssWidth * r), h: Math.floor(b.cssHeight * r) };\n });\n let sized = bitmaps();\n for (let i = 0; i < 6; i++) {\n const total = sized.reduce((sum, b) => sum + b.w * b.h, 0);\n if (total <= MAX_TOTAL_CANVAS_PIXELS) break;\n factor *= Math.sqrt(MAX_TOTAL_CANVAS_PIXELS / total);\n sized = bitmaps();\n }\n return sizes.map((size, i) => {\n const { scale, cssHeight } = base[i];\n const fields = (byPage.get(i + 1) ?? []).map((f): FieldView => {\n const h = f.height ?? DEFAULT_FIELD_HEIGHT;\n const left = (f.x / size.width) * 100;\n return {\n id: f.id, label: f.label, tone: f.tone ?? 'blue',\n left, top: ((size.height - f.y - h) / size.height) * 100,\n width: f.width == null ? null : (f.width / size.width) * 100,\n maxWidth: Math.max(0, 100 - left),\n height: (h / size.height) * 100,\n fontPx: Math.max(9, h * scale * 0.7),\n };\n });\n const hotspots = this.hotspots().filter(h => h.page === i + 1).map((h): HotspotView => ({\n hotspot: h,\n left: (h.x / size.width) * 100,\n top: ((size.height - h.y - h.height) / size.height) * 100,\n width: (h.width / size.width) * 100,\n height: (h.height / size.height) * 100,\n fontPx: Math.max(7, Math.min(11, h.height * scale * 0.25)),\n }));\n return { number: i + 1, cssWidth, cssHeight, canvasWidth: sized[i].w, canvasHeight: sized[i].h, fields, hotspots };\n });\n });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n private readonly tokenWorkerSrc = inject(PDF_VIEWER_WORKER_SRC);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly headersKey = computed(() => JSON.stringify(this.httpHeaders()));\n\n /** Bumped by every new load, replacement and destroy; an async step whose token no longer matches stops. */\n private loadToken = 0;\n /** Bumped by every (re)draw so only the newest draw paints. */\n private drawToken = 0;\n private doc: PDFDocumentProxy | null = null;\n private loadingTask: { destroy?: () => unknown } | null = null;\n private pageProxies: PDFPageProxy[] = [];\n private renderTasks: { cancel?: () => void }[] = [];\n private observer: ResizeObserver | null = null;\n private frame = 0;\n private lastDrawKey = '';\n\n constructor() {\n effect(() => {\n this.src(); this.data(); this.headersKey(); this.withCredentials(); this.workerSrc(); this.checkBrowserSupport();\n if (!this.isBrowser) return; // nothing to load or draw on the server\n untracked(() => void this.startLoad());\n });\n // Whatever resizes the canvases (maxPageWidth, pixelRatio) clears them, so a changed bitmap size needs a redraw. Draws the code\n // already scheduled record their key in scheduleDraw(), so this only fires for changes nothing else handled.\n effect(() => {\n const key = this.drawKey();\n untracked(() => { if (this.status() === 'ready' && key !== this.lastDrawKey) this.scheduleDraw(); });\n });\n if (this.isBrowser && typeof ResizeObserver !== 'undefined') {\n this.observer = new ResizeObserver(() => this.onResize());\n this.observer.observe(this.host.nativeElement);\n }\n inject(DestroyRef).onDestroy(() => {\n this.loadToken++;\n this.drawToken++;\n this.observer?.disconnect();\n if (this.frame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(this.frame);\n this.teardown();\n });\n }\n\n /** Test seam: loads pdf.js on first use so apps that never show a PDF do not bundle or fetch it. */\n protected loadPdfJs(): Promise<PdfJsModule> {\n return import('pdfjs-dist');\n }\n\n /** Test seam: the required features this browser lacks. */\n protected missingFeatures(needsUrl: boolean): readonly PdfViewerRequiredFeature[] {\n return findMissingPdfFeatures(globalThis, needsUrl);\n }\n\n private resolvedRatio(): number {\n const explicit = this.pixelRatio();\n if (explicit != null && explicit > 0) return explicit;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n return Math.min(Math.max(dpr, 1), 3);\n }\n\n private widthFor(container: number): number {\n const max = this.maxPageWidth() > 0 ? this.maxPageWidth() : DEFAULT_MAX_WIDTH;\n return container > 0 ? Math.min(max, container) : max;\n }\n\n private async startLoad(): Promise<void> {\n const token = ++this.loadToken;\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set('');\n const data = this.data();\n const src = this.src();\n if (data == null && !src) { this.status.set('idle'); return; }\n if (this.checkBrowserSupport()) {\n const missing = this.missingFeatures(data == null);\n if (missing.length > 0) { // pdf.js would throw on first use: do not even import it\n this.loadToken++;\n this.pageSizes.set([]);\n this.status.set('unsupported');\n this.loadFailed.emit(new PdfViewerUnsupportedError(missing));\n return;\n }\n }\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n this.status.set('loading');\n try {\n const pdfjs = await this.loadPdfJs();\n if (token !== this.loadToken) return;\n const worker = this.workerSrc() || this.tokenWorkerSrc;\n if (worker) pdfjs.GlobalWorkerOptions.workerSrc = worker;\n else if (!pdfjs.GlobalWorkerOptions.workerSrc) { this.fail(new Error(WORKER_MISSING), WORKER_MISSING); return; }\n\n const params: Record<string, unknown> = data != null\n ? { data: data instanceof Uint8Array ? data.slice() : new Uint8Array(data.slice(0)) } // pdf.js detaches what it is given\n : { url: src };\n if (data == null) { // request options only make sense for a URL\n const headers = this.httpHeaders();\n if (Object.keys(headers).length > 0) params['httpHeaders'] = headers;\n if (this.withCredentials()) params['withCredentials'] = true;\n }\n const task = pdfjs.getDocument(params as unknown as Parameters<PdfJsModule['getDocument']>[0]);\n this.loadingTask = task;\n const doc = await task.promise;\n if (token !== this.loadToken) { this.destroyDoc(doc); return; }\n this.doc = doc;\n\n const proxies: PDFPageProxy[] = [];\n const sizes: { width: number; height: number }[] = [];\n for (let i = 1; i <= doc.numPages; i++) {\n const page = await doc.getPage(i);\n if (token !== this.loadToken) return;\n const vp = page.getViewport({ scale: 1 });\n if (!(Number.isFinite(vp.width) && Number.isFinite(vp.height) && vp.width > 0 && vp.height > 0)) throw new Error('invalid page size');\n proxies.push(page);\n sizes.push({ width: vp.width, height: vp.height });\n }\n this.pageProxies = proxies;\n this.containerWidth.set(this.host.nativeElement.clientWidth); // the container may have changed while the file loaded\n this.pageSizes.set(sizes);\n this.status.set('ready');\n this.loaded.emit({ pageCount: sizes.length, pages: sizes.map(s => ({ ...s })), document: doc });\n this.scheduleDraw();\n } catch (err) {\n if (token !== this.loadToken) return;\n this.fail(err);\n }\n }\n\n private fail(err: unknown, message = ''): void {\n this.loadToken++; // nothing still in flight may paint over the error\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set(message);\n this.status.set('error');\n this.loadFailed.emit(err);\n }\n\n private scheduleDraw(): void {\n const id = ++this.drawToken;\n this.lastDrawKey = this.drawKey();\n this.cancelRenders(); // a still-running paint must not land in canvases about to be resized\n afterNextRender(() => void this.draw(id), { injector: this.injector });\n }\n\n private async draw(id: number): Promise<void> {\n const token = this.loadToken;\n const stale = () => id !== this.drawToken || token !== this.loadToken;\n this.cancelRenders();\n const views = this.views();\n const canvases = Array.from(this.host.nativeElement.querySelectorAll<HTMLCanvasElement>('canvas.pv-canvas'));\n const sizes = this.pageSizes();\n const scales: number[] = [];\n try {\n for (let i = 0; i < views.length; i++) {\n if (stale()) return;\n const canvas = canvases[i];\n const page = this.pageProxies[i];\n if (!canvas || !page || !canvas.getContext('2d')) throw new Error('canvas unavailable');\n const cssScale = views[i].cssWidth / sizes[i].width;\n scales.push(cssScale);\n const viewport = page.getViewport({ scale: (views[i].canvasWidth / sizes[i].width) });\n const task = page.render({ canvas, viewport });\n this.renderTasks.push(task);\n await task.promise;\n }\n } catch (err) {\n if (stale() || (err as { name?: string } | null)?.name === 'RenderingCancelledException') return;\n this.fail(err);\n return;\n }\n if (stale()) return;\n this.pagesRendered.emit({ pageCount: views.length, scale: scales[0] ?? 1, pageWidths: views.map(v => v.cssWidth) });\n }\n\n private onResize(): void {\n if (this.frame || typeof requestAnimationFrame !== 'function') return;\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n const before = this.effectiveWidth();\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n if (this.effectiveWidth() !== before && this.status() === 'ready') this.scheduleDraw();\n });\n }\n\n private cancelRenders(): void {\n for (const t of this.renderTasks) { try { t.cancel?.(); } catch { /* already finished */ } }\n this.renderTasks = [];\n }\n\n private teardown(): void {\n this.cancelRenders();\n const task = this.loadingTask;\n this.loadingTask = null;\n if (task) { try { Promise.resolve(task.destroy?.()).catch(() => undefined); } catch { /* ignore */ } }\n const doc = this.doc;\n this.doc = null;\n this.pageProxies = [];\n if (doc) this.destroyDoc(doc);\n }\n\n private destroyDoc(doc: PDFDocumentProxy): void {\n try { Promise.resolve(doc.destroy()).catch(() => undefined); } catch { /* ignore */ }\n }\n}\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { PDF_VIEWER_WORKER_SRC } from './pdf-viewer/pdf-viewer.component';\n\nexport interface ForgeroutePdfOptions {\n /** URL of the pdf.js worker script every `ui-pdf-viewer` in the app uses, e.g. `'/pdf.worker.min.mjs'`. Must not be blank. */\n workerSrc: string;\n}\n\n/**\n * Sets the pdf.js worker location once for the whole app (provides `PDF_VIEWER_WORKER_SRC`):\n *\n * providers: [provideForgeroutePdf({ workerSrc: '/pdf.worker.min.mjs' })]\n *\n * A `workerSrc` input on a viewer still wins over this value. Throws at provide time when `workerSrc` is blank, because a viewer\n * without a worker can only show \"PDF worker is not configured.\".\n */\nexport function provideForgeroutePdf(options: ForgeroutePdfOptions): EnvironmentProviders {\n const workerSrc = typeof options?.workerSrc === 'string' ? options.workerSrc.trim() : '';\n if (!workerSrc) {\n throw new Error('provideForgeroutePdf: \"workerSrc\" must be a non-empty URL of the pdf.js worker script (for example \"/pdf.worker.min.mjs\").');\n }\n return makeEnvironmentProviders([{ provide: PDF_VIEWER_WORKER_SRC, useValue: workerSrc }]);\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';\nimport { AlertComponent, ButtonComponent, IconComponent, ModalComponent, SkeletonComponent } from '@forgeroute/ui';\nimport { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';\n\n/**\n * A dialog that shows one PDF with Download and Print buttons: the file name as the title, the pages below (`ui-pdf-viewer`), a skeleton\n * while the host is still fetching the bytes, an alert when the fetch failed. The HOST fetches (so its own auth / interceptors apply) and\n * passes the bytes:\n *\n * <ui-document-viewer-modal [open]=\"open()\" [fileName]=\"name()\" [data]=\"bytes()\" [loading]=\"loading()\" [error]=\"error()\" (closed)=\"close()\" />\n *\n * Download saves the bytes under `fileName`; Print opens the browser's print dialog for them (a new tab when the browser blocks the\n * hidden print frame). Both are disabled until there are bytes. The pdf.js worker comes from `provideForgeroutePdf` (or `workerSrc`).\n * Test ids: `document-viewer`, `document-viewer-download`, `document-viewer-print`, `document-viewer-loading`, `document-viewer-error`.\n */\n@Component({\n selector: 'ui-document-viewer-modal',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [ModalComponent, ButtonComponent, IconComponent, AlertComponent, SkeletonComponent, PdfViewerComponent],\n template: `\n <ui-modal [open]=\"open()\" [title]=\"fileName()\" maxWidth=\"64rem\" dialogTestId=\"document-viewer\" (close)=\"closed.emit()\">\n <div class=\"dvm-actions\" modal-header-end>\n <ui-button variant=\"secondary\" size=\"sm\" data-testid=\"document-viewer-download\" [disabled]=\"!hasData()\" ariaLabel=\"Download document\" (click)=\"download()\">\n <ui-icon name=\"download\" [interactive]=\"false\" /> Download\n </ui-button>\n <ui-button variant=\"secondary\" size=\"sm\" data-testid=\"document-viewer-print\" [disabled]=\"!hasData()\" ariaLabel=\"Print document\" (click)=\"print()\">\n <ui-icon name=\"print\" [interactive]=\"false\" /> Print\n </ui-button>\n </div>\n @if (loading()) {\n <div class=\"dvm-state\" role=\"status\" aria-live=\"polite\" data-testid=\"document-viewer-loading\">\n <ui-skeleton [height]=\"320\" />\n <span class=\"dvm-sr\">{{ loadingText() }}</span>\n </div>\n } @else if (error()) {\n <ui-alert variant=\"danger\" data-testid=\"document-viewer-error\">{{ error() }}</ui-alert>\n } @else if (data(); as bytes) {\n <ui-pdf-viewer [data]=\"bytes\" [ariaLabel]=\"fileName()\" [loadingText]=\"loadingText()\" />\n }\n </ui-modal>\n `,\n styles: [`\n .dvm-actions { display: inline-flex; gap: 8px; align-items: center; }\n .dvm-state { display: block; }\n .dvm-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; }\n `],\n})\nexport class DocumentViewerModalComponent {\n readonly open = input(false);\n /** Shown as the dialog title and used as the Download file name. */\n readonly fileName = input('Document');\n /** The PDF bytes (null until fetched). */\n readonly data = input<ArrayBuffer | Uint8Array | null>(null);\n /** The host is still fetching the bytes: a skeleton shows. */\n readonly loading = input(false);\n /** The host's message when the fetch failed (empty = no error). */\n readonly error = input('');\n readonly loadingText = input('Loading document…');\n readonly closed = output<void>();\n\n protected readonly hasData = computed(() => !!this.data() && !this.loading() && !this.error());\n\n private blob(): Blob | null {\n const bytes = this.data();\n return bytes ? new Blob([bytes as BlobPart], { type: 'application/pdf' }) : null;\n }\n\n /** Saves the bytes as a PDF named after `fileName`. */\n protected download(): void {\n const blob = this.blob();\n if (!blob) return;\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = this.fileName() || 'document.pdf';\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(url);\n }\n\n /** Opens the browser's print dialog for the PDF through a hidden frame (a new tab when the browser blocks it). */\n protected print(): void {\n const blob = this.blob();\n if (!blob) return;\n const url = URL.createObjectURL(blob);\n const frame = document.createElement('iframe');\n frame.style.cssText = 'position:fixed;top:-10000px;left:-10000px;width:1px;height:1px;';\n frame.src = url;\n const cleanup = () => { frame.remove(); URL.revokeObjectURL(url); };\n frame.onload = () => {\n try {\n frame.contentWindow?.addEventListener('afterprint', cleanup);\n frame.contentWindow?.print();\n } catch {\n window.open(url, '_blank');\n cleanup();\n }\n };\n document.body.appendChild(frame);\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, effect, input, output, signal, untracked } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n AlertComponent, ButtonComponent, DetailCardComponent, HintComponent, IconComponent, ModalComponent, SectionHeaderComponent, SkeletonComponent,\n StackComponent, TextInputComponent,\n} from '@forgeroute/ui';\nimport { PdfViewerComponent, PdfViewerHotspot } from '../pdf-viewer/pdf-viewer.component';\n\n/** Where a signature goes: PDF points, origin at the page's bottom-left corner, page numbers from 1. */\nexport interface DocumentSignField {\n /** Reported back in the result; defaults to the field's position in the list (\"1\", \"2\", …). */\n id?: string;\n page: number;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface DocumentSignedField {\n id: string;\n page: number;\n x: number;\n y: number;\n width: number;\n height: number;\n /** The signature image (PNG data URL) that was applied to this field. */\n signatureImage: string;\n}\n\nexport interface DocumentSignResult {\n /** The signature image the signer created (the same image is on every field). */\n signatureDataUrl: string;\n signedAt: string;\n fields: DocumentSignedField[];\n}\n\n/** Every text of the dialog; pass only the ones you want to change (`[texts]=\"{ title: 'Sign the contract' }\"`). */\nexport interface DocumentSignTexts {\n title: string;\n loading: string;\n step1: string;\n namePlaceholder: string;\n previewEmpty: string;\n saveSignature: string;\n change: string;\n step2: string;\n signHere: string;\n addSignatureFirst: string;\n allSigned: string;\n cancel: string;\n complete: string;\n submitting: string;\n noFields: string;\n}\n\nconst DEFAULT_TEXTS: DocumentSignTexts = {\n title: 'Sign Document',\n loading: 'Loading document…',\n step1: 'Step 1: Create Your Signature',\n namePlaceholder: 'Type your full name',\n previewEmpty: 'Preview appears here',\n saveSignature: 'Save Signature',\n change: 'Change',\n step2: 'Step 2: Click the buttons below to apply your signature',\n signHere: 'Sign Here',\n addSignatureFirst: 'Add signature above',\n allSigned: 'All signatures completed',\n cancel: 'Cancel',\n complete: 'Complete Signing',\n submitting: 'Submitting...',\n noFields: 'This document has no place to sign.',\n};\n\nconst SIGNATURE_WIDTH = 600;\nconst SIGNATURE_HEIGHT = 60;\n\n/**\n * Shows any PDF and collects a signature on it, wherever the host says the signature goes. Generic on purpose: the HOST fetches the\n * bytes (its own auth, pre-filling, prefilled forms) and says where the signature fields are (`fields`, PDF points, bottom-left origin);\n * the dialog does the rest: the signer types their name, it becomes a handwriting-style image, the signer clicks each field to apply it,\n * and `(signed)` reports the image and every field with its position. The host stores the signature however it needs.\n *\n * <ui-document-sign-modal [open]=\"open()\" [data]=\"bytes()\" [fields]=\"fields()\" [loading]=\"loading()\" [error]=\"error()\"\n * [signerName]=\"user.name\" [submitting]=\"saving()\" (signed)=\"save($event)\" (closed)=\"open.set(false)\" />\n *\n * - `savedSignature` (a data URL, e.g. from the user's profile) skips the typing step; it is scaled down to the standard 600x60 image.\n * `(signatureCreated)` gives the new image so the host can keep it for next time.\n * - Complete is disabled until every field is signed (and while there are no fields); the footer counts \"N of M signed\".\n * - \"Change\" clears the signature and every applied field so the signer can start again.\n * - `(signed)` does not close the dialog: the host closes it (`[open]=\"false\"`) when its own save succeeded, or shows `error`.\n * - The pdf.js worker comes from `provideForgeroutePdf` (or the viewer's `workerSrc`).\n * Test ids: `sign-modal`, `sign-loading`, `sign-error`, `signature-step`, `typed-name-input`, `typed-signature-preview`, `save-typed-signature`,\n * `redraw-signature`, `apply-step`, `sign-progress`, `sign-cancel`, `sign-complete`, `pdf-hotspot-<field id>`.\n */\n@Component({\n selector: 'ui-document-sign-modal',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n FormsModule, AlertComponent, ButtonComponent, DetailCardComponent, HintComponent, IconComponent, ModalComponent, SectionHeaderComponent,\n SkeletonComponent, StackComponent, TextInputComponent, PdfViewerComponent,\n ],\n template: `\n <ui-modal [open]=\"open()\" [title]=\"t().title\" maxWidth=\"56rem\" bodyOverflow=\"auto\" dialogTestId=\"sign-modal\" (close)=\"cancel()\">\n @if (loading()) {\n <ui-stack gap=\"md\" align=\"center\" role=\"status\" aria-live=\"polite\" data-testid=\"sign-loading\">\n <ui-skeleton [height]=\"24\" width=\"60%\" />\n <ui-hint>{{ t().loading }}</ui-hint>\n </ui-stack>\n } @else if (error()) {\n <ui-alert variant=\"danger\" data-testid=\"sign-error\">{{ error() }}</ui-alert>\n } @else {\n <ui-stack gap=\"md\">\n <ui-detail-card [title]=\"t().step1\" [headingLevel]=\"4\" [collapsible]=\"false\" data-testid=\"signature-step\">\n @if (!signature()) {\n <ui-stack gap=\"sm\">\n <ui-text-input data-testid=\"typed-name-input\" [placeholder]=\"t().namePlaceholder\" [ariaLabel]=\"t().namePlaceholder\"\n [ngModel]=\"typedName()\" (ngModelChange)=\"typedName.set($event ?? '')\" />\n <div class=\"ds-preview\">\n @if (typedName()) {\n <span class=\"ds-typed\" data-testid=\"typed-signature-preview\">{{ typedName() }}</span>\n } @else {\n <span class=\"ds-empty\">{{ t().previewEmpty }}</span>\n }\n </div>\n <ui-button variant=\"primary\" [fullWidth]=\"true\" [disabled]=\"!typedName().trim()\" data-testid=\"save-typed-signature\" (click)=\"saveTypedSignature()\">\n <ui-icon name=\"check\" [size]=\"16\" [interactive]=\"false\" /> {{ t().saveSignature }}\n </ui-button>\n </ui-stack>\n } @else {\n <ui-stack gap=\"sm\">\n <div class=\"ds-saved\"><img [src]=\"signature()!\" alt=\"Your saved signature\" class=\"ds-saved-img\"></div>\n <ui-button variant=\"secondary\" [ariaLabel]=\"t().change + ' signature'\" data-testid=\"redraw-signature\" (click)=\"changeSignature()\">\n <ui-icon name=\"edit\" [size]=\"16\" [interactive]=\"false\" /> {{ t().change }}\n </ui-button>\n </ui-stack>\n }\n </ui-detail-card>\n\n @if (signature()) {\n <ui-section-header [level]=\"4\" size=\"sm\" [title]=\"t().step2\" data-testid=\"apply-step\" />\n }\n\n <ui-pdf-viewer [data]=\"data()\" [hotspots]=\"hotspots()\" [ariaLabel]=\"t().title\" [loadingText]=\"t().loading\" (hotspotActivate)=\"apply($event)\" />\n </ui-stack>\n }\n\n <ui-hint modal-footer-left role=\"status\" aria-live=\"polite\" aria-atomic=\"true\" data-testid=\"sign-progress\">\n @if (fields().length === 0) { {{ t().noFields }} } @else if (allSigned()) { {{ t().allSigned }} } @else { {{ signedCount() }} of {{ fields().length }} signed }\n </ui-hint>\n <ui-stack modal-footer-right direction=\"horizontal\" gap=\"sm\" [wrap]=\"true\">\n <ui-button variant=\"secondary\" data-testid=\"sign-cancel\" (click)=\"cancel()\"><ui-icon name=\"close\" [size]=\"16\" [interactive]=\"false\" /> {{ t().cancel }}</ui-button>\n <ui-button variant=\"primary\" data-testid=\"sign-complete\" [disabled]=\"!allSigned() || submitting()\" [loading]=\"submitting()\" (click)=\"complete()\">\n <ui-icon name=\"check\" [size]=\"16\" [interactive]=\"false\" /> {{ submitting() ? t().submitting : t().complete }}\n </ui-button>\n </ui-stack>\n </ui-modal>\n `,\n styles: [`\n .ds-preview { display: flex; align-items: center; min-height: 64px; padding: 4px 12px; border: 1px dashed #cbd5e1; border-radius: 8px; background: #fff; overflow: hidden; }\n .ds-empty { color: #94a3b8; font-size: 0.875rem; }\n .ds-typed { font-family: 'Dancing Script', cursive; font-weight: 700; font-size: 2.25rem; line-height: 1.2; color: #000; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n .ds-saved { display: flex; align-items: center; min-height: 64px; padding: 4px 12px; border: 1px solid #e2e8f0; border-radius: 8px; background: #fff; }\n .ds-saved-img { display: block; max-width: 100%; height: 60px; object-fit: contain; object-position: left; }\n `],\n})\nexport class DocumentSignModalComponent {\n readonly open = input(false);\n /** The PDF bytes (null until the host has them). */\n readonly data = input<ArrayBuffer | Uint8Array | null>(null);\n /** Where the signature goes. */\n readonly fields = input<DocumentSignField[]>([]);\n /** The host is still fetching the document: a skeleton shows. */\n readonly loading = input(false);\n /** The host's error text (empty = none): shown instead of the document. */\n readonly error = input('');\n /** Prefills the typed-name box. */\n readonly signerName = input('');\n /** A signature the signer made before (PNG data URL): used at once, scaled to the standard size. */\n readonly savedSignature = input<string | null>(null);\n /** The host is saving the result: Complete shows a spinner and cannot be pressed twice. */\n readonly submitting = input(false);\n /** Texts to change (the rest stay in English). */\n readonly texts = input<Partial<DocumentSignTexts>>({});\n\n /** The signer finished: the signature image and every field with its position. The dialog stays open for the host to close. */\n readonly signed = output<DocumentSignResult>();\n /** The signer created a new signature (typed name saved): the host may keep it. */\n readonly signatureCreated = output<string>();\n /** The dialog was dismissed (Cancel, Escape, X, backdrop). */\n readonly closed = output<void>();\n\n protected readonly t = computed((): DocumentSignTexts => ({ ...DEFAULT_TEXTS, ...this.texts() }));\n protected readonly typedName = signal('');\n protected readonly signature = signal<string | null>(null);\n /** Field id -> the image applied to it. */\n private readonly applied = signal<Record<string, string>>({});\n\n /** The fields with their ids filled in (\"1\", \"2\", … when the host gave none). */\n private readonly resolved = computed(() => this.fields().map((f, i) => ({ ...f, id: f.id ?? String(i + 1) })));\n protected readonly signedCount = computed(() => this.resolved().filter(f => !!this.applied()[f.id]).length);\n protected readonly allSigned = computed(() => this.resolved().length > 0 && this.signedCount() === this.resolved().length);\n\n protected readonly hotspots = computed((): PdfViewerHotspot[] => {\n const fields = this.resolved();\n const image = this.signature();\n return fields.map((f, i) => {\n const applied = this.applied()[f.id];\n const label = `Sign field ${i + 1} of ${fields.length}`;\n return {\n id: f.id, page: f.page, x: f.x, y: f.y, width: f.width, height: f.height,\n state: applied ? 'signed' : image ? 'ready' : 'blocked',\n label: this.t().signHere, blockedLabel: this.t().addSignatureFirst,\n ariaLabel: image ? label : `${label} - add your signature above first to enable signing`,\n image: applied ?? null, imageAlt: 'Applied signature',\n };\n });\n });\n\n constructor() {\n // Every time the dialog opens: start clean, prefill the name, and use the saved signature when there is one. Only `open` is tracked, so a\n // host echoing the saved signature back does not reset a half-finished signing.\n effect(() => {\n if (!this.open()) return;\n untracked(() => {\n this.applied.set({});\n this.typedName.set(this.signerName());\n this.signature.set(null);\n const saved = this.savedSignature();\n if (saved) void this.normalize(saved).then(image => { if (this.open() && !this.signature()) this.signature.set(image); });\n });\n });\n }\n\n protected cancel(): void {\n this.closed.emit();\n }\n\n /** Turns the typed name into a handwriting-style PNG (600x60, left-aligned) and makes it the signature. */\n protected async saveTypedSignature(): Promise<void> {\n const name = this.typedName().trim();\n if (!name) return;\n try { await document.fonts?.ready; } catch { /* fonts API unavailable: the fallback font is used */ }\n const canvas = document.createElement('canvas');\n canvas.width = SIGNATURE_WIDTH;\n canvas.height = SIGNATURE_HEIGHT;\n const ctx = canvas.getContext('2d')!;\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = '#000';\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n const maxWidth = canvas.width - 10;\n let size = 48;\n ctx.font = `bold ${size}px 'Dancing Script', cursive`;\n while (ctx.measureText(name).width > maxWidth && size > 24) {\n size -= 2;\n ctx.font = `bold ${size}px 'Dancing Script', cursive`;\n }\n ctx.fillText(name, 5, canvas.height / 2);\n const image = canvas.toDataURL('image/png');\n this.signature.set(image);\n this.signatureCreated.emit(image);\n }\n\n /** Clears the signature and everything applied, so the signer starts again. */\n protected changeSignature(): void {\n this.signature.set(null);\n this.typedName.set('');\n this.applied.set({});\n }\n\n /** Puts the signature on one field (only possible once there is a signature). */\n protected apply(fieldId: string): void {\n const image = this.signature();\n if (!image || !this.resolved().some(f => f.id === fieldId)) return;\n this.applied.update(a => ({ ...a, [fieldId]: image }));\n }\n\n protected complete(): void {\n if (!this.allSigned() || this.submitting()) return;\n const fields = this.resolved().map((f): DocumentSignedField => ({ id: f.id, page: f.page, x: f.x, y: f.y, width: f.width, height: f.height, signatureImage: this.applied()[f.id] }));\n this.signed.emit({ signatureDataUrl: fields[0].signatureImage, signedAt: new Date().toISOString(), fields });\n }\n\n /** Scales a saved signature down to fit 600x60 (never up), left-aligned and centred vertically, so old oversized images weigh the same as new ones. */\n private normalize(dataUrl: string): Promise<string> {\n return new Promise(resolve => {\n const img = new Image();\n img.onload = () => {\n const out = document.createElement('canvas');\n out.width = SIGNATURE_WIDTH;\n out.height = SIGNATURE_HEIGHT;\n const scale = Math.min(out.width / img.width, out.height / img.height);\n const w = img.width * scale;\n const h = img.height * scale;\n out.getContext('2d')!.drawImage(img, 0, (out.height - h) / 2, w, h);\n resolve(out.toDataURL('image/png'));\n };\n img.onerror = () => resolve(dataUrl);\n img.src = dataUrl;\n });\n }\n}\n"],"mappings":"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"}
|