@exige/web-scanner-sdk 0.2.40 → 0.2.42
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.
- package/README.md +1 -1
- package/dist/{driver-license-BV2-PjpZ.js → driver-license-D9y7PQn4.js} +504 -546
- package/dist/driver-license-D9y7PQn4.js.map +1 -0
- package/dist/react/parts.d.ts +1 -1
- package/dist/react/types.d.ts +4 -2
- package/dist/react.js +443 -454
- package/dist/react.js.map +1 -1
- package/dist/scanner-sdk.js +3 -3
- package/dist/sdk/driver-license.d.ts +2 -2
- package/dist/sdk/finder.d.ts +13 -6
- package/package.json +1 -1
- package/dist/driver-license-BV2-PjpZ.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"driver-license-D9y7PQn4.js","names":[],"sources":["../src/sdk/errors.ts","../src/sdk/license-codec.ts","../src/sdk/license-public-key.ts","../src/sdk/license-runtime.ts","../src/sdk/license.ts","../src/sdk/model-download.ts","../src/sdk/camera.ts","../src/sdk/ort-cdn.ts","../src/sdk/ort-runtime.ts","../src/sdk/image.ts","../src/sdk/scan.ts","../src/sdk/lighting.ts","../src/sdk/finder.ts","../src/sdk/ocr-runtime.ts","../src/sdk/ocr.ts","../src/sdk/zxing-runtime.ts","../src/sdk/barcode.ts","../src/sdk/aamva.ts","../src/sdk/face-model.ts","../src/sdk/field-model.ts","../src/sdk/redact.ts","../src/sdk/programmatic-redaction.ts","../src/sdk/driver-license.ts"],"sourcesContent":["/**\n * Operational failure from the scanner runtime.\n *\n * Scan quality stays a returned status (`success`, `no-document`,\n * `low-confidence`, or `low-quality`). Redaction match quality stays\n * `success`, `partial`, or `unmatched`. Neither of those outcomes uses this error.\n * A bad image does not throw.\n */\nexport type ScannerErrorCode =\n | 'secure-context'\n | 'worker'\n | 'model-http'\n | 'language'\n | 'canvas'\n\nexport class ScannerError extends Error {\n readonly code: ScannerErrorCode\n\n constructor(code: ScannerErrorCode, message: string) {\n super(message)\n this.name = 'ScannerError'\n this.code = code\n }\n}\n\nexport function canvasUnavailable(): ScannerError {\n return new ScannerError('canvas', 'Canvas 2D is unavailable in this browser.')\n}\n","export function bytesToBase64Url(bytes: Uint8Array): string {\n let binary = ''\n for (const byte of bytes) binary += String.fromCharCode(byte)\n return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/u, '')\n}\n\nexport function base64UrlToBytes(value: string): Uint8Array {\n const normalized = value.replaceAll('-', '+').replaceAll('_', '/')\n const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4)\n const binary = atob(padded)\n const bytes = new Uint8Array(binary.length)\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index)\n }\n return bytes\n}\n","/**\n * Raw Ed25519 public key: 32 bytes, base64url, no padding.\n *\n * The license portal signs with `LICENSE_SIGNING_PRIVATE_KEY`.\n * Generate a pair with `npm run generate-keypair --prefix license-portal`,\n * then replace this constant with the printed public key and rebuild the\n * library. Never commit the private key.\n */\nexport const LICENSE_PUBLIC_KEY_BASE64 = 'uFpA_UXGk-1AjjG31cFP6cqXOQX0w2TnuR7Bl-msZ6I'\n","import { base64UrlToBytes } from './license-codec'\nimport { LICENSE_PUBLIC_KEY_BASE64 } from './license-public-key'\n\n/** Public key embedded in the published package. Tests replace this module. */\nexport function embeddedLicensePublicKey(): Uint8Array {\n return base64UrlToBytes(LICENSE_PUBLIC_KEY_BASE64)\n}\n","import { base64UrlToBytes } from './license-codec'\nimport { embeddedLicensePublicKey } from './license-runtime'\n\n/**\n * Hosts that may use the SDK before a paid license exists.\n * Add a production hostname here when it should run without a signed key.\n *\n * An entry is either an exact hostname or a single `*.suffix` pattern.\n * `*.exige.io` matches `exige.io` and any host that ends with `.exige.io`.\n * It does not match `exige.io.evil.com`. `*.localhost` is the only\n * single-label suffix; `app.localhost` is allowed. Ngrok tunnel suffixes\n * are allowed. Nothing here allows `*.vercel.app`. Paid license `domains`\n * stay exact hostnames.\n */\nexport const DEV_LICENSE_HOSTS = [\n 'localhost',\n '127.0.0.1',\n 'exige-scanner-sdk.vercel.app',\n '*.localhost',\n '*.exige.io',\n '*.ngrok.io',\n '*.ngrok.app',\n '*.ngrok.dev',\n '*.ngrok-free.app',\n '*.ngrok-free.dev',\n] as const\n\nexport const LICENSE_FEATURES = ['scan', 'ocr', 'redaction', 'vehicles', 'torch'] as const\n\nexport type LicenseFeature = (typeof LICENSE_FEATURES)[number]\n\nexport type LicenseErrorCode =\n | 'missing'\n | 'malformed'\n | 'invalid-signature'\n | 'expired'\n | 'domain'\n | 'feature'\n\nexport interface LicenseFeatures {\n scan: boolean\n ocr: boolean\n redaction: boolean\n /**\n * Kept so existing signed keys still verify.\n * No camera, finder, scan, OCR, or redaction entry point asserts this flag.\n */\n vehicles: boolean\n torch: boolean\n}\n\nexport interface LicenseClaims {\n customerId: string\n /** Exact hostnames. `localhost` and `127.0.0.1` are included only when listed. */\n domains: string[]\n /** Unix expiry in seconds. The key is valid while `now < exp`. */\n exp: number\n features: LicenseFeatures\n}\n\n/**\n * Compact signed payload: `base64url(json).base64url(ed25519 signature)`.\n * The signature covers the raw JSON bytes. Public APIs do not accept a\n * caller-supplied public key or hostname.\n */\nexport interface LicenseVerification {\n publicKey: Uint8Array\n /** Injected only for Node tests. Browsers use `location.hostname`. */\n hostname: string\n /** Unix time in seconds. */\n now: number\n}\n\nexport class LicenseError extends Error {\n readonly code: LicenseErrorCode\n\n constructor(code: LicenseErrorCode, message: string) {\n super(message)\n this.name = 'LicenseError'\n this.code = code\n }\n}\n\nlet activeToken: string | undefined\nlet activeClaims: LicenseClaims | undefined\n\nexport function normalizeHostname(value: string): string {\n return value.trim().toLowerCase().replace(/\\.$/u, '')\n}\n\n/**\n * Canonical JSON signed by the license portal. Key order is part of the\n * signature bytes. Keep this in lockstep with `license-portal/lib/license-token.ts`.\n */\nexport function canonicalLicensePayload(claims: LicenseClaims): string {\n const domains = [...new Set(claims.domains.map(normalizeHostname).filter(Boolean))].sort()\n const features = {\n ocr: claims.features.ocr === true,\n redaction: claims.features.redaction === true,\n scan: claims.features.scan === true,\n torch: claims.features.torch === true,\n vehicles: claims.features.vehicles === true,\n }\n return JSON.stringify({\n customerId: claims.customerId,\n domains,\n exp: claims.exp,\n features,\n })\n}\n\nfunction readBrowserHostname(): string | undefined {\n const host = (globalThis as { location?: { hostname?: string } }).location?.hostname\n if (typeof host !== 'string') return undefined\n const normalized = normalizeHostname(host)\n return normalized || undefined\n}\n\nfunction unixNow(): number {\n return Math.floor(Date.now() / 1000)\n}\n\nfunction matchesDevLicenseEntry(hostname: string, entry: string): boolean {\n const pattern = normalizeHostname(entry)\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2)\n const allowSingleLabel = suffix === 'localhost'\n if ((!suffix.includes('.') && !allowSingleLabel) || suffix.startsWith('.') || suffix.includes('*')) {\n return false\n }\n return hostname === suffix || hostname.endsWith(`.${suffix}`)\n }\n return hostname === pattern\n}\n\nfunction isDevLicenseHost(hostname: string): boolean {\n const host = normalizeHostname(hostname)\n if (!host || host.includes('*')) return false\n return DEV_LICENSE_HOSTS.some((entry) => matchesDevLicenseEntry(host, entry))\n}\n\n/** All features, only for the host that matched `DEV_LICENSE_HOSTS`. */\nfunction developmentLicense(hostname: string): LicenseClaims {\n return {\n customerId: 'dev',\n domains: [hostname],\n exp: 4_102_444_800,\n features: {\n scan: true,\n ocr: true,\n redaction: true,\n vehicles: true,\n torch: true,\n },\n }\n}\n\nfunction copyClaims(claims: LicenseClaims): LicenseClaims {\n return {\n customerId: claims.customerId,\n domains: [...claims.domains],\n exp: claims.exp,\n features: { ...claims.features },\n }\n}\n\nfunction malformed(message = 'This license key is not a signed payload.'): LicenseError {\n return new LicenseError('malformed', message)\n}\n\nfunction parseClaims(payload: unknown): LicenseClaims {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw malformed()\n const record = payload as Record<string, unknown>\n if (typeof record.customerId !== 'string' || record.customerId.trim() === '' || record.customerId.length > 200) {\n throw malformed('This license key is missing a customer id.')\n }\n if (!Array.isArray(record.domains) || record.domains.some((domain) => typeof domain !== 'string')) {\n throw malformed('This license key is missing a domain list.')\n }\n if (typeof record.exp !== 'number' || !Number.isSafeInteger(record.exp) || record.exp <= 0) {\n throw malformed('This license key is missing a Unix expiry.')\n }\n if (!record.features || typeof record.features !== 'object' || Array.isArray(record.features)) {\n throw malformed('This license key is missing feature flags.')\n }\n const flags = record.features as Record<string, unknown>\n const domains = [...new Set(record.domains.map((domain) => normalizeHostname(domain)).filter(Boolean))].sort()\n return {\n customerId: record.customerId.trim(),\n domains,\n exp: record.exp,\n features: {\n scan: flags.scan === true,\n ocr: flags.ocr === true,\n redaction: flags.redaction === true,\n vehicles: flags.vehicles === true,\n torch: flags.torch === true,\n },\n }\n}\n\n/**\n * Verifies a compact license. Hostname and public key are explicit so Node\n * tests can inject them. Published entry points call `assertLicense`, which\n * always uses the embedded public key and `location.hostname`.\n */\nexport async function verifyLicenseToken(\n licenseKey: string,\n verification: LicenseVerification,\n): Promise<LicenseClaims> {\n const parts = licenseKey.trim().split('.')\n if (parts.length !== 2 || !parts[0] || !parts[1]) throw malformed()\n let payloadBytes: Uint8Array\n let signature: Uint8Array\n try {\n payloadBytes = base64UrlToBytes(parts[0])\n signature = base64UrlToBytes(parts[1])\n } catch {\n throw malformed()\n }\n let key: CryptoKey\n let valid = false\n try {\n key = await crypto.subtle.importKey(\n 'raw',\n verification.publicKey as BufferSource,\n { name: 'Ed25519' },\n false,\n ['verify'],\n )\n valid = await crypto.subtle.verify('Ed25519', key, signature as BufferSource, payloadBytes as BufferSource)\n } catch {\n valid = false\n }\n if (!valid) {\n throw new LicenseError('invalid-signature', 'This license key signature does not match the SDK public key.')\n }\n let parsed: unknown\n try {\n parsed = JSON.parse(new TextDecoder().decode(payloadBytes))\n } catch {\n throw malformed()\n }\n const claims = parseClaims(parsed)\n if (claims.exp <= verification.now) {\n throw new LicenseError('expired', 'This license key has expired.')\n }\n const hostname = normalizeHostname(verification.hostname)\n if (!hostname || !claims.domains.includes(hostname)) {\n throw new LicenseError('domain', `This license key is not valid for ${hostname || 'this host'}.`)\n }\n return claims\n}\n\n/**\n * Verifies `licenseKey` for the current page and keeps it for later calls.\n *\n * @throws {LicenseError} LicenseError when the key is missing, malformed, badly signed,\n * expired, or not issued for `location.hostname`.\n */\nexport async function setLicenseKey(licenseKey: string): Promise<LicenseClaims> {\n const hostname = readBrowserHostname()\n if (!hostname) {\n throw new LicenseError('domain', 'The license check requires a browser hostname.')\n }\n const claims = await verifyLicenseToken(licenseKey, {\n publicKey: embeddedLicensePublicKey(),\n hostname,\n now: unixNow(),\n })\n activeToken = licenseKey.trim()\n activeClaims = claims\n return copyClaims(claims)\n}\n\n/**\n * Feature flags from the key most recently accepted by `setLicenseKey`.\n * Returns null when no key is loaded, the key is expired, or this hostname\n * is not on the key. Enforcement still re-checks the signature in `assertLicense`.\n */\nexport function getLicense(): LicenseClaims | null {\n const hostname = readBrowserHostname()\n if (activeClaims && hostname && activeClaims.domains.includes(hostname) && activeClaims.exp > unixNow()) {\n return copyClaims(activeClaims)\n }\n if (!activeToken && hostname && isDevLicenseHost(hostname)) return copyClaims(developmentLicense(hostname))\n return null\n}\n\n/**\n * Fail-closed gate used by the public camera, finder, scan, OCR, and\n * redaction entry points.\n *\n * @throws {LicenseError} LicenseError before those APIs do any work, when the key is\n * missing, invalid, or missing `feature`.\n */\nexport async function assertLicense(feature: LicenseFeature): Promise<LicenseClaims> {\n const hostname = readBrowserHostname()\n if (!hostname) {\n throw new LicenseError('domain', 'The license check requires a browser hostname.')\n }\n if (!activeToken) {\n if (isDevLicenseHost(hostname)) return copyClaims(developmentLicense(hostname))\n throw new LicenseError(\n 'missing',\n 'A license key is required. Call setLicenseKey() before using the scanner SDK.',\n )\n }\n const claims = await verifyLicenseToken(activeToken, {\n publicKey: embeddedLicensePublicKey(),\n hostname,\n now: unixNow(),\n })\n activeClaims = claims\n if (!claims.features[feature]) {\n throw new LicenseError('feature', `This license key does not include the ${feature} feature.`)\n }\n return copyClaims(claims)\n}\n\n/** Drops the in-memory key. Not part of the published package entry. */\nexport function clearLicenseForTests(): void {\n activeToken = undefined\n activeClaims = undefined\n}\n","import { ScannerError } from './errors'\n\nexport interface ModelDownloadProgress {\n loaded: number\n /** Byte length from Content-Length. Null when that header is absent. */\n total: number | null\n done?: boolean\n}\n\nexport interface ModelDownloadSnapshot {\n downloading: boolean\n loaded: number\n /** Sum of Content-Length values when every active transfer has one. */\n total: number | null\n}\n\nconst STORE = Symbol.for('exige.web-scanner-sdk.model-downloads')\n\ninterface ModelDownloadStore {\n active: Map<string, { loaded: number; total: number | null }>\n listeners: Set<(snapshot: ModelDownloadSnapshot) => void>\n cache: Map<string, ArrayBuffer>\n inflight: Map<string, Promise<ArrayBuffer>>\n}\n\n/** One store across the package entry and the React entry. */\nfunction store(): ModelDownloadStore {\n const scope = globalThis as typeof globalThis & { [STORE]?: ModelDownloadStore }\n if (!scope[STORE]) {\n scope[STORE] = {\n active: new Map(),\n listeners: new Set(),\n cache: new Map(),\n inflight: new Map(),\n }\n }\n return scope[STORE]\n}\n\nconst active = store().active\nconst listeners = store().listeners\nconst cache = store().cache\nconst inflight = store().inflight\n\nfunction contentLength(response: Response) {\n const parsed = Number(response.headers.get('content-length'))\n return Number.isFinite(parsed) && parsed > 0 ? parsed : null\n}\n\nexport function modelDownloadSnapshot(): ModelDownloadSnapshot {\n if (active.size === 0) return { downloading: false, loaded: 0, total: null }\n let loaded = 0\n let total = 0\n let known = true\n for (const item of active.values()) {\n loaded += item.loaded\n if (item.total == null) known = false\n else total += item.total\n }\n return { downloading: true, loaded, total: known ? total : null }\n}\n\nfunction emit() {\n const snapshot = modelDownloadSnapshot()\n for (const listener of listeners) listener(snapshot)\n}\n\n/** Subscribing does not fetch. The document worker and `downloadModelBytes` report into the same snapshot. */\nexport function subscribeModelDownloads(listener: (snapshot: ModelDownloadSnapshot) => void) {\n listener(modelDownloadSnapshot())\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n}\n\n/** Progress reported from another realm, such as the document worker. */\nexport function reportModelDownload(url: string, progress: ModelDownloadProgress) {\n if (progress.done) active.delete(url)\n else active.set(url, { loaded: progress.loaded, total: progress.total })\n emit()\n}\n\nexport async function readResponseBytes(\n response: Response,\n onProgress?: (progress: ModelDownloadProgress) => void,\n) {\n const total = contentLength(response)\n if (!response.body) {\n const bytes = await response.arrayBuffer()\n onProgress?.({ loaded: bytes.byteLength, total: total ?? bytes.byteLength })\n return bytes\n }\n const reader = response.body.getReader()\n const chunks: Uint8Array[] = []\n let loaded = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n if (!value?.byteLength) continue\n chunks.push(value)\n loaded += value.byteLength\n onProgress?.({ loaded, total })\n }\n const bytes = new Uint8Array(loaded)\n let offset = 0\n for (const chunk of chunks) {\n bytes.set(chunk, offset)\n offset += chunk.byteLength\n }\n if (loaded === 0) onProgress?.({ loaded: 0, total })\n return bytes.buffer\n}\n\n/**\n * Fetch a model file and report loaded/total while the body transfers.\n * A second call for the same URL reuses the cached bytes and does not fetch.\n */\nexport async function downloadModelBytes(url: string, init?: { cache?: RequestCache }) {\n const cached = cache.get(url)\n if (cached) return cached.slice(0)\n const existing = inflight.get(url)\n if (existing) return existing.then((bytes) => bytes.slice(0))\n const request = (async () => {\n reportModelDownload(url, { loaded: 0, total: null })\n const response = await fetch(url, { cache: init?.cache })\n if (!response.ok) {\n throw new ScannerError('model-http', `Model request failed with HTTP ${response.status}.`)\n }\n return readResponseBytes(response, (progress) => reportModelDownload(url, progress))\n })()\n inflight.set(url, request)\n try {\n const bytes = await request\n cache.set(url, bytes)\n return bytes.slice(0)\n } finally {\n inflight.delete(url)\n if (active.has(url)) {\n active.delete(url)\n emit()\n }\n }\n}\n","import { canvasUnavailable, ScannerError } from './errors'\nimport { assertLicense } from './license'\n\nexport interface CameraOptions {\n facingMode?: 'environment' | 'user'\n idealWidth?: number\n idealHeight?: number\n /** Host opt-in. The SDK does not apply an Android-only width cap. */\n maxWidth?: number\n /** Host opt-in. The SDK does not apply an Android-only 1920 height cap. */\n maxHeight?: number\n maxFrameRate?: number\n /** Rear-camera torch policy. Defaults to `alwaysOff`. */\n torch?: TorchMode\n}\n\nexport type TorchMode =\n | 'defaultOn'\n | 'defaultOff'\n | 'alwaysOn'\n | 'alwaysOff'\n | 'auto'\n\nexport interface TorchStatus {\n mode: TorchMode\n supported: boolean\n on: boolean\n /** Whether the camera UI should show a torch control. */\n visible: boolean\n /** Whether an end user can change the control with `setTorch()`. */\n mutable: boolean\n /** True when luminance hysteresis, rather than an end user, owns the state. */\n automatic: boolean\n}\n\nexport interface CameraSession {\n stream: MediaStream\n readonly torchMode: TorchMode\n readonly torchSupported: boolean\n readonly torchOn: boolean\n capture: (maxDimension?: number) => HTMLCanvasElement\n /**\n * Best live pixels for PDF417: `ImageCapture.grabFrame()` when it is not\n * black, else the video, else the last non-black mirror copy.\n */\n captureLiveFrame: (maxDimension?: number) => Promise<HTMLCanvasElement | undefined>\n /**\n * Full-resolution still. Outside WebKit this is `ImageCapture.takePhoto()`\n * at the camera's max native still (one axis only — both maxima ask Chrome\n * for a square and it scales). Map finder corners onto it with\n * `mapDocumentCornersToFrame`. A still longer than `STILL_MAX_EDGE` (4096)\n * is scaled down with high-quality smoothing so a 12MP+ JPEG does not\n * exhaust mobile memory. WebKit still copies the live preview, because\n * `takePhoto()` returns a black JPEG there.\n */\n capturePhoto: () => Promise<CameraPhoto>\n getSettings: () => MediaTrackSettings\n getDiagnostics: () => CameraDiagnostics\n getTorchStatus: () => TorchStatus\n /**\n * Changes torch state only for `defaultOn` and `defaultOff`.\n * Returns false, without throwing, for unsupported or immutable modes.\n */\n setTorch: (enabled: boolean) => Promise<boolean>\n stop: () => void\n}\n\nexport type CapturePath = 'video-frame' | 'live-mirror' | 'image-capture' | 'system-camera'\n\n/**\n * How `ScannerProvider` captures.\n *\n * - `auto` (default): Android opens the system Camera app as soon as\n * `open` becomes true from a user gesture. iOS and desktop stay on\n * the live finder overlay.\n * - `live`: always use the live preview + finder, including on Android.\n * - `system-camera`: always use `pickNativeCameraStill()`, including on iOS.\n */\nexport type CaptureMode = 'auto' | 'live' | 'system-camera'\n\nexport interface CameraPhoto {\n /** Still pixels. Long edge is at most `STILL_MAX_EDGE` (4096). */\n frame: HTMLCanvasElement\n method: CapturePath\n settings: MediaTrackSettings\n fallbackReason?:\n | 'video-frame-empty'\n | 'image-capture-empty'\n | 'image-capture-failed'\n | 'image-capture-aspect'\n | 'image-capture-preview-size'\n luminance: number\n diagnostics: CameraDiagnostics\n}\n\nexport interface CameraDiagnostics {\n videoWidth: number\n videoHeight: number\n readyState: number\n paused: boolean\n connected: boolean\n rendered: boolean\n trackReadyState: MediaStreamTrackState | 'missing'\n trackEnabled: boolean\n trackMuted: boolean\n liveLuminance: number\n mirrorLuminance: number\n mirrorAgeMs: number\n /**\n * Whether the browser exposes `ImageCapture`. Stills use `takePhoto()`\n * outside WebKit. WebKit exposes the constructor and returns a black JPEG.\n */\n hasImageCapture: boolean\n torchMode: TorchMode\n torchSupported: boolean\n torchOn: boolean\n torch: TorchStatus\n engine: BrowserEngine\n}\n\nexport interface CaptureDiagnosticsReport {\n capturedAt?: string\n error?: string\n captureMethod?: CapturePath\n luminance?: number\n fallbackReason?: CameraPhoto['fallbackReason']\n shutter?: CameraDiagnostics\n live?: CameraDiagnostics\n finder?: {\n state?: string\n confidence?: number\n stableFrames?: number\n requiredStableFrames?: number\n detector?: string\n modelStatus?: string\n modelError?: string\n modelProvider?: string\n modelNote?: string\n inferenceMs?: number\n sourceWidth?: number\n sourceHeight?: number\n }\n}\n\nfunction formatDiagnosticsBlock(label: string, diagnostics: CameraDiagnostics) {\n const { engine } = diagnostics\n return [\n `${label}:`,\n ` engine: ${engine.engine}/${engine.browser}${engine.iosWebKit ? ' ios-webkit' : ''}`,\n ` ua: ${engine.userAgent}`,\n ` video: ${diagnostics.videoWidth}x${diagnostics.videoHeight} readyState=${diagnostics.readyState}${diagnostics.paused ? ' paused' : ''}${diagnostics.rendered ? '' : ' hidden'}${diagnostics.connected ? '' : ' disconnected'}`,\n ` track: ${diagnostics.trackReadyState}${diagnostics.trackEnabled ? '' : ' disabled'}${diagnostics.trackMuted ? ' muted' : ''}`,\n ` liveLuma: ${diagnostics.liveLuminance.toFixed(1)}`,\n ` mirrorLuma: ${diagnostics.mirrorLuminance.toFixed(1)}`,\n ` mirrorAgeMs: ${Number.isFinite(diagnostics.mirrorAgeMs) ? Math.round(diagnostics.mirrorAgeMs) : 'none'}`,\n ` hasImageCapture: ${diagnostics.hasImageCapture}${diagnostics.engine.iosWebKit ? ' (skipped on webkit)' : ' (still capture)'}`,\n ` torch: ${diagnostics.torch.mode} supported=${diagnostics.torch.supported} on=${diagnostics.torch.on}`,\n ].join('\\n')\n}\n\n/** Plain-text dump safe to paste into chat or a ticket. */\nexport function formatCaptureDiagnosticsReport(report: CaptureDiagnosticsReport) {\n const lines = [\n 'Exige scanner capture report',\n `time: ${report.capturedAt ?? new Date().toISOString()}`,\n ]\n if (report.error) lines.push(`error: ${report.error}`)\n if (report.captureMethod) lines.push(`method: ${report.captureMethod}`)\n if (report.luminance !== undefined) lines.push(`luminance: ${report.luminance.toFixed(1)}`)\n if (report.fallbackReason) lines.push(`fallback: ${report.fallbackReason}`)\n if (report.shutter) lines.push(formatDiagnosticsBlock('shutter', report.shutter))\n if (report.live) lines.push(formatDiagnosticsBlock('live', report.live))\n if (report.finder) {\n const finder = report.finder\n lines.push(\n [\n 'finder:',\n ` state: ${finder.state ?? 'unknown'}`,\n ` confidence: ${finder.confidence === undefined ? '—' : finder.confidence.toFixed(3)}`,\n ` stable: ${finder.stableFrames ?? '—'}/${finder.requiredStableFrames ?? '—'}`,\n ` detector: ${finder.detector ?? '—'} ${finder.modelStatus ?? ''}`.trim(),\n ` modelError: ${finder.modelError ?? '—'}`,\n ` provider: ${finder.modelProvider ?? '—'}`,\n ` note: ${finder.modelNote ?? '—'}`,\n ` inferenceMs: ${finder.inferenceMs === undefined ? '—' : Math.round(finder.inferenceMs)}`,\n ` source: ${finder.sourceWidth ?? '—'}x${finder.sourceHeight ?? '—'}`,\n ].join('\\n'),\n )\n }\n return `${lines.join('\\n')}\\n`\n}\n\nexport interface BrowserEngine {\n /** Engine actually executing, not the vendor shell around it. */\n engine: 'webkit' | 'blink' | 'gecko' | 'unknown'\n /** Vendor shell, which on iOS is only a wrapper around WebKit. */\n browser: string\n iosWebKit: boolean\n userAgent: string\n}\n\n/**\n * Identifies the rendering engine by capability plus the vendor shell by name.\n * Every iOS browser is WebKit, so behavior must never be gated on Safari alone.\n */\nexport function detectBrowserEngine(\n userAgent = typeof navigator === 'undefined' ? '' : navigator.userAgent,\n maxTouchPoints = typeof navigator === 'undefined' ? 0 : navigator.maxTouchPoints,\n): BrowserEngine {\n const browser = /CriOS|Chrome/i.test(userAgent)\n ? 'chrome'\n : /EdgiOS|Edg/i.test(userAgent)\n ? 'edge'\n : /FxiOS|Firefox/i.test(userAgent)\n ? 'firefox'\n : /Safari/i.test(userAgent)\n ? 'safari'\n : 'unknown'\n const iosDevice = /iPhone|iPad|iPod/i.test(userAgent) ||\n (/Macintosh/i.test(userAgent) && maxTouchPoints > 1)\n const iosShell = /CriOS|EdgiOS|FxiOS|OPiOS|Mercury/i.test(userAgent)\n const iosWebKit = iosDevice || iosShell\n const engine = iosWebKit || (/AppleWebKit/i.test(userAgent) && !/Chrome|Chromium|Edg/i.test(userAgent))\n ? 'webkit'\n : /Chrome|Chromium|Edg/i.test(userAgent)\n ? 'blink'\n : /Gecko\\//i.test(userAgent)\n ? 'gecko'\n : 'unknown'\n return { engine, browser, iosWebKit, userAgent }\n}\n\n/** Mean luminance below this is treated as an empty/black frame. */\n\n/** Android browser, not an iOS shell that happens to mention Android. */\nexport function isAndroidBrowser(engine: BrowserEngine) {\n return /Android/i.test(engine.userAgent) && !engine.iosWebKit\n}\n\n/**\n * DocAligner may use WebGPU on WebKit (iOS, including Chrome/Firefox shells).\n * Android Chrome can open a WebGPU session whose corner heatmaps never peak,\n * so Android and other Blink engines stay on WASM.\n */\nexport function shouldPreferWebGpuForDocumentModel(\n engine: BrowserEngine = detectBrowserEngine(),\n) {\n return engine.engine === 'webkit' && !isAndroidBrowser(engine)\n}\n\n/**\n * Android `auto` uses the system Camera app. Chrome `takePhoto()` cannot\n * match Camera-app quality, and there is no live document guide on that path.\n * Hosts opt out with `live` and force the picker with `system-camera`.\n */\nexport function shouldUseSystemCameraCapture(\n mode: CaptureMode = 'auto',\n engine: BrowserEngine = detectBrowserEngine(),\n) {\n if (mode === 'system-camera') return true\n if (mode === 'live') return false\n return isAndroidBrowser(engine)\n}\n\n/** WebKit's ImageCapture.takePhoto() resolves with a black JPEG. Other engines can use it for a sharp still. */\nexport function shouldUseImageCapture(engine: BrowserEngine, hasImageCapture: boolean) {\n return hasImageCapture && !engine.iosWebKit\n}\n\nexport interface PhotoSizeRange {\n min?: number\n max?: number\n step?: number\n}\n\n/** Snap a requested still edge onto the camera's min/max/step grid. */\nexport function snapPhotoDimension(preferred: number, range?: PhotoSizeRange) {\n if (!preferred || !Number.isFinite(preferred)) return undefined\n const max = range?.max\n if (!max || !Number.isFinite(max)) return Math.round(preferred)\n const min = range?.min && Number.isFinite(range.min) ? range.min : 1\n const step = range?.step && range.step > 0 ? range.step : 1\n const clamped = Math.min(max, Math.max(min, preferred))\n const snapped = min + Math.round((clamped - min) / step) * step\n return Math.round(Math.min(max, Math.max(min, snapped)))\n}\n\n/**\n * Largest still the camera reports. Requests only one axis.\n *\n * Chrome's photo sample sets `imageWidth` alone and lets the UA pick a\n * native pair. Passing both `imageWidth.max` and `imageHeight.max` repeats\n * the 0.2.14 square trap: phones that advertise the same max on both axes\n * (portrait + landscape stills) get a max×max square, and Chrome scales\n * the sensor image into a large soft JPEG.\n */\nexport function maxPhotoSettings(capabilities?: {\n imageWidth?: PhotoSizeRange\n imageHeight?: PhotoSizeRange\n}) {\n const imageWidth = snapPhotoDimension(\n capabilities?.imageWidth?.max ?? 0,\n capabilities?.imageWidth,\n )\n const imageHeight = snapPhotoDimension(\n capabilities?.imageHeight?.max ?? 0,\n capabilities?.imageHeight,\n )\n if (imageWidth && imageHeight) {\n return imageWidth >= imageHeight\n ? { imageWidth }\n : { imageHeight }\n }\n if (imageWidth) return { imageWidth }\n if (imageHeight) return { imageHeight }\n return undefined\n}\n\n/**\n * True when the still has meaningfully more pixels than the live preview.\n * A same-size `takePhoto()` JPEG is often a recompress of the video frame\n * and looks softer than copying the `<video>` pixels.\n */\nexport function stillBeatsPreview(\n still: { width: number; height: number },\n preview: { width: number; height: number },\n) {\n if (!still.width || !still.height || !preview.width || !preview.height) return false\n return still.width * still.height > preview.width * preview.height * 1.15\n}\n\n/**\n * Whether a still and the preview share an aspect ratio. Capture still uses\n * a visible `takePhoto()` still when this is false; finder corners are mapped\n * onto that full-res still with `mapDocumentCornersToFrame`.\n */\nexport function stillMatchesPreview(\n still: { width: number; height: number },\n preview: { width: number; height: number },\n) {\n if (!still.width || !still.height || !preview.width || !preview.height) return false\n const ratio = (still.width / still.height) / (preview.width / preview.height)\n return ratio > 0.97 && ratio < 1.03\n}\nexport const NEAR_BLACK_LUMINANCE = 10\n\nexport interface AutoTorchOptions {\n /** Luminance at or below which dark samples accumulate. */\n turnOnLuminance?: number\n /** Luminance at or above which bright samples accumulate. */\n turnOffLuminance?: number\n /** Consecutive dark samples required before enabling the torch. */\n darkSamples?: number\n /** Consecutive bright samples required before disabling the torch. */\n brightSamples?: number\n}\n\n/**\n * Stateful automatic torch policy. The wide threshold gap and consecutive\n * sample counts prevent exposure changes and hand movement from flickering the\n * LED. `update()` returns a new requested state only when the state changes.\n */\nexport class AutoTorchController {\n private on = false\n private darkCount = 0\n private brightCount = 0\n private readonly turnOnLuminance: number\n private readonly turnOffLuminance: number\n private readonly darkSamples: number\n private readonly brightSamples: number\n\n constructor(options: AutoTorchOptions = {}) {\n this.turnOnLuminance = options.turnOnLuminance ?? 62\n this.turnOffLuminance = options.turnOffLuminance ?? 155\n this.darkSamples = options.darkSamples ?? 3\n this.brightSamples = options.brightSamples ?? 8\n }\n\n update(luminance: number) {\n if (!Number.isFinite(luminance) || luminance <= NEAR_BLACK_LUMINANCE) {\n this.darkCount = 0\n this.brightCount = 0\n return undefined\n }\n if (!this.on) {\n this.brightCount = 0\n this.darkCount = luminance <= this.turnOnLuminance ? this.darkCount + 1 : 0\n if (this.darkCount < this.darkSamples) return undefined\n this.darkCount = 0\n this.on = true\n return true\n }\n this.darkCount = 0\n this.brightCount = luminance >= this.turnOffLuminance ? this.brightCount + 1 : 0\n if (this.brightCount < this.brightSamples) return undefined\n this.brightCount = 0\n this.on = false\n return false\n }\n\n getState() {\n return this.on\n }\n}\n\nexport function torchControl(\n mode: TorchMode,\n supported: boolean,\n): TorchStatus {\n if (!supported) {\n return {\n mode,\n supported: false,\n on: false,\n visible: false,\n mutable: false,\n automatic: mode === 'auto',\n }\n }\n if (mode === 'alwaysOn' || mode === 'alwaysOff') {\n return {\n mode,\n supported: true,\n on: mode === 'alwaysOn',\n visible: false,\n mutable: false,\n automatic: false,\n }\n }\n return {\n mode,\n supported: true,\n on: mode === 'defaultOn',\n visible: true,\n mutable: mode !== 'auto',\n automatic: mode === 'auto',\n }\n}\n\nexport function meanFrameLuminance(pixels: Uint8ClampedArray) {\n const pixelCount = Math.floor(pixels.length / 4)\n if (!pixelCount) return 0\n let luminance = 0\n for (let offset = 0; offset < pixelCount * 4; offset += 4) {\n luminance += pixels[offset] * 0.299 +\n pixels[offset + 1] * 0.587 +\n pixels[offset + 2] * 0.114\n }\n return luminance / pixelCount\n}\n\nexport function isNearBlackCanvas(canvas: HTMLCanvasElement) {\n if (!canvas.width || !canvas.height) return true\n return sampleCanvasLuminance(canvas, canvas.width, canvas.height) <= NEAR_BLACK_LUMINANCE\n}\n\nexport function isNearBlackFrame(\n pixels: Uint8ClampedArray,\n blackThreshold = NEAR_BLACK_LUMINANCE,\n) {\n if (pixels.length < 4) return true\n const pixelCount = Math.floor(pixels.length / 4)\n let visiblePixels = 0\n for (let offset = 0; offset < pixelCount * 4; offset += 4) {\n const value = pixels[offset] * 0.299 +\n pixels[offset + 1] * 0.587 +\n pixels[offset + 2] * 0.114\n if (value > blackThreshold * 2) visiblePixels += 1\n }\n return meanFrameLuminance(pixels) <= blackThreshold &&\n visiblePixels / pixelCount < 0.01\n}\n\nfunction sampleCanvasLuminance(source: CanvasImageSource, width: number, height: number) {\n if (!width || !height) return 0\n const sample = document.createElement('canvas')\n sample.width = Math.min(32, width)\n sample.height = Math.min(32, height)\n const context = sample.getContext('2d', { willReadFrequently: true })\n if (!context) return 0\n context.drawImage(source, 0, 0, sample.width, sample.height)\n return meanFrameLuminance(context.getImageData(0, 0, sample.width, sample.height).data)\n}\n\nfunction canvasLuminance(canvas: HTMLCanvasElement) {\n return sampleCanvasLuminance(canvas, canvas.width, canvas.height)\n}\n\ntype VideoFrameCallbackElement = HTMLVideoElement & Partial<{\n requestVideoFrameCallback: (callback: () => void) => number\n cancelVideoFrameCallback: (handle: number) => void\n}>\n\n/**\n * Keeps an offscreen copy of the most recent non-black video frame. WebKit can\n * blank a `<video>` at the moment of a tap, teardown, or compositing change, so\n * capture must not depend on the pixels available at that exact instant.\n */\nfunction startFrameMirror(video: VideoFrameCallbackElement, minIntervalMs = 120) {\n const canvas = document.createElement('canvas')\n const context = canvas.getContext('2d')\n const supportsFrameCallback = typeof video.requestVideoFrameCallback === 'function'\n let running = true\n let handle = 0\n let lastCopyAt = 0\n let lastGoodAt = 0\n let liveLuminance = 0\n let mirrorLuminance = 0\n\n const schedule = () => {\n if (!running) return\n handle = supportsFrameCallback\n ? video.requestVideoFrameCallback!(grab)\n : requestAnimationFrame(grab)\n }\n\n function grab() {\n if (!running) return\n const { videoWidth, videoHeight } = video\n const now = performance.now()\n if (context && videoWidth && videoHeight && now - lastCopyAt >= minIntervalMs) {\n lastCopyAt = now\n liveLuminance = sampleCanvasLuminance(video, videoWidth, videoHeight)\n if (liveLuminance > NEAR_BLACK_LUMINANCE) {\n if (canvas.width !== videoWidth || canvas.height !== videoHeight) {\n canvas.width = videoWidth\n canvas.height = videoHeight\n }\n context.drawImage(video, 0, 0, videoWidth, videoHeight)\n mirrorLuminance = liveLuminance\n lastGoodAt = now\n }\n }\n schedule()\n }\n\n schedule()\n return {\n stop() {\n running = false\n if (supportsFrameCallback) video.cancelVideoFrameCallback?.(handle)\n else cancelAnimationFrame(handle)\n },\n getFrame: () => lastGoodAt ? canvas : undefined,\n getLiveLuminance: () => liveLuminance,\n getMirrorLuminance: () => mirrorLuminance,\n getMirrorAgeMs: () => lastGoodAt ? performance.now() - lastGoodAt : Infinity,\n }\n}\n\nfunction cloneFrame(source: HTMLCanvasElement) {\n const copy = document.createElement('canvas')\n copy.width = source.width\n copy.height = source.height\n copy.getContext('2d')?.drawImage(source, 0, 0)\n return copy\n}\n\nfunction scaleCanvasToMax(source: HTMLCanvasElement, maxDimension?: number) {\n const size = scaledFrameSize(source.width, source.height, maxDimension)\n if (size.width === source.width && size.height === source.height) return cloneFrame(source)\n const canvas = document.createElement('canvas')\n canvas.width = size.width\n canvas.height = size.height\n const context = canvas.getContext('2d')\n if (!context) throw canvasUnavailable()\n context.imageSmoothingEnabled = true\n if ('imageSmoothingQuality' in context) context.imageSmoothingQuality = 'high'\n context.drawImage(source, 0, 0, canvas.width, canvas.height)\n return canvas\n}\n\nfunction captureFromVideoOrMirror(\n video: HTMLVideoElement,\n mirror: { getFrame: () => HTMLCanvasElement | undefined },\n maxDimension?: number,\n) {\n try {\n const live = captureVideoFrame(video, maxDimension)\n if (!isNearBlackCanvas(live)) return live\n } catch {\n // Video not ready; try the last good mirror copy.\n }\n const mirrored = mirror.getFrame()\n if (!mirrored) throw new Error('The camera is not ready to capture a frame.')\n const frame = scaleCanvasToMax(mirrored, maxDimension)\n if (isNearBlackCanvas(frame)) {\n throw new Error('The camera preview produced no visible pixels.')\n }\n return frame\n}\n\n/** Blink may software-scale the preview unless `resizeMode` is `none`. */\nexport function preferNativeCameraResize(engine: BrowserEngine) {\n return engine.engine === 'blink'\n}\n\nexport function mobileCameraConstraints(\n options: CameraOptions = {},\n engine = detectBrowserEngine(),\n): MediaStreamConstraints {\n const width: ConstrainULongRange = { ideal: options.idealWidth ?? 3840 }\n const height: ConstrainULongRange = { ideal: options.idealHeight ?? 2160 }\n if (options.maxWidth) width.max = options.maxWidth\n if (options.maxHeight) height.max = options.maxHeight\n const video: MediaTrackConstraints & { resizeMode?: ConstrainDOMString } = {\n facingMode: { ideal: options.facingMode ?? 'environment' },\n width,\n height,\n frameRate: { ideal: options.maxFrameRate ?? 30, max: options.maxFrameRate ?? 30 },\n }\n if (preferNativeCameraResize(engine)) video.resizeMode = { ideal: 'none' }\n return {\n audio: false,\n video,\n }\n}\n\n/** Long-edge cap that keeps the source aspect. Never upscales. */\nexport function scaledFrameSize(\n width: number,\n height: number,\n maxDimension?: number,\n) {\n const scale = maxDimension\n ? Math.min(1, maxDimension / Math.max(width, height))\n : 1\n return {\n width: Math.max(1, Math.round(width * scale)),\n height: Math.max(1, Math.round(height * scale)),\n }\n}\n\n/**\n * Long-edge memory cap for `capturePhoto()` stills. Preview and analysis\n * are independent: the live stream stays at the negotiated 3840×2160-class\n * size, and DocAligner sees a working copy (720 on iOS, 520–1600\n * elsewhere). Only a still whose\n * long edge exceeds this is scaled, with high-quality smoothing.\n */\nexport const STILL_MAX_EDGE = 4096\n\n/**\n * Bump a granted preview toward the largest same-aspect size the camera\n * advertises. Chrome often opens at 1080p even after a 4K ideal.\n */\nexport function highestPracticalTrackConstraints(\n settings: { width?: number; height?: number },\n capabilities?: { width?: { max?: number }; height?: { max?: number } },\n): MediaTrackConstraints | undefined {\n const width = settings.width\n const height = settings.height\n const maxWidth = capabilities?.width?.max\n const maxHeight = capabilities?.height?.max\n if (!width || !height || !maxWidth || !maxHeight) return undefined\n const scale = Math.min(maxWidth / width, maxHeight / height)\n if (scale <= 1.05) return undefined\n return {\n width: { ideal: Math.round(width * scale) },\n height: { ideal: Math.round(height * scale) },\n }\n}\n\nasync function applyHighestPracticalResolution(track: MediaStreamTrack) {\n const next = highestPracticalTrackConstraints(\n track.getSettings(),\n typeof track.getCapabilities === 'function' ? track.getCapabilities() : undefined,\n )\n if (!next) return\n try {\n await track.applyConstraints(next)\n } catch {\n // Keep the granted stream if the camera rejects the bump.\n }\n}\n\ntype ImageCaptureLike = {\n takePhoto: (settings?: { imageWidth?: number; imageHeight?: number }) => Promise<Blob>\n grabFrame?: () => Promise<ImageBitmap>\n getPhotoCapabilities?: () => Promise<{\n imageWidth?: PhotoSizeRange\n imageHeight?: PhotoSizeRange\n }>\n getPhotoSettings?: () => Promise<{ imageWidth?: number; imageHeight?: number }>\n}\n\nasync function decodeStillBitmap(blob: Blob) {\n try {\n return await createImageBitmap(blob, {\n imageOrientation: 'from-image',\n colorSpaceConversion: 'none',\n })\n } catch {\n return await createImageBitmap(blob)\n }\n}\n\nasync function aimMeteringAtCenter(track: MediaStreamTrack) {\n try {\n await track.applyConstraints({\n advanced: [{ pointsOfInterest: [{ x: 0.5, y: 0.5 }] } as MediaTrackConstraintSet],\n })\n await new Promise((resolve) => window.setTimeout(resolve, 180))\n } catch {\n // Chrome Camera2 focuses continuously; some devices reject POI.\n }\n}\n\nasync function blobToStillCanvas(blob: Blob) {\n const bitmap = await decodeStillBitmap(blob)\n const size = scaledFrameSize(bitmap.width, bitmap.height, STILL_MAX_EDGE)\n const canvas = document.createElement('canvas')\n canvas.width = size.width\n canvas.height = size.height\n const context = canvas.getContext('2d')\n if (!context) {\n bitmap.close?.()\n throw canvasUnavailable()\n }\n const scaled = size.width !== bitmap.width || size.height !== bitmap.height\n context.imageSmoothingEnabled = scaled\n context.imageSmoothingQuality = 'high'\n context.drawImage(bitmap, 0, 0, canvas.width, canvas.height)\n bitmap.close?.()\n return canvas\n}\n\nasync function captureTrackStill(track: MediaStreamTrack) {\n const ImageCaptureApi = (globalThis as typeof globalThis & {\n ImageCapture?: new (track: MediaStreamTrack) => ImageCaptureLike\n }).ImageCapture\n if (!ImageCaptureApi) return undefined\n await aimMeteringAtCenter(track)\n const capture = new ImageCaptureApi(track)\n const capabilities = capture.getPhotoCapabilities\n ? await capture.getPhotoCapabilities()\n : undefined\n const settings = maxPhotoSettings(capabilities)\n let blob: Blob\n try {\n blob = await capture.takePhoto(settings)\n } catch {\n // No PhotoSettings: Chrome's current size defaults to the video format.\n blob = await capture.takePhoto()\n }\n const frame = await blobToStillCanvas(blob)\n return {\n frame,\n requested: settings,\n photoMax: {\n width: capabilities?.imageWidth?.max,\n height: capabilities?.imageHeight?.max,\n },\n }\n}\n\nexport function captureVideoFrame(video: HTMLVideoElement, maxDimension?: number) {\n if (!video.videoWidth || !video.videoHeight) {\n throw new Error('The camera is not ready to capture a frame.')\n }\n const size = scaledFrameSize(video.videoWidth, video.videoHeight, maxDimension)\n const canvas = document.createElement('canvas')\n canvas.width = size.width\n canvas.height = size.height\n const context = canvas.getContext('2d')\n if (!context) throw canvasUnavailable()\n context.drawImage(video, 0, 0, canvas.width, canvas.height)\n return canvas\n}\n\nfunction cameraConstraintCandidates(options: CameraOptions) {\n const engine = detectBrowserEngine()\n if (options.idealWidth || options.idealHeight || options.maxWidth || options.maxHeight) {\n return [mobileCameraConstraints(options, engine)]\n }\n return [\n mobileCameraConstraints({ ...options, idealWidth: 3840, idealHeight: 2160 }, engine),\n mobileCameraConstraints({ ...options, idealWidth: 2560, idealHeight: 1440 }, engine),\n mobileCameraConstraints({ ...options, idealWidth: 1920, idealHeight: 1080 }, engine),\n ]\n}\n\n/**\n * Opens the rear camera on `video`.\n *\n * @throws {LicenseError} LicenseError when scan is not licensed. Torch mode other than\n * `alwaysOff` also requires the torch feature. The camera does not start.\n * @throws {ScannerError} ScannerError `secure-context` when the page is not HTTPS.\n * `localhost` is allowed. `canvas` when a captured frame cannot get canvas 2D.\n */\nexport async function startCamera(\n video: HTMLVideoElement,\n options: CameraOptions = {},\n): Promise<CameraSession> {\n await assertLicense('scan')\n const torchMode = options.torch ?? 'alwaysOff'\n if (torchMode !== 'alwaysOff') await assertLicense('torch')\n if (!window.isSecureContext && location.hostname !== 'localhost') {\n throw new ScannerError('secure-context', 'Camera access requires HTTPS.')\n }\n if (!navigator.mediaDevices?.getUserMedia) {\n throw new Error('Live camera access is unavailable. Use the native camera picker instead.')\n }\n video.muted = true\n video.autoplay = true\n video.playsInline = true\n video.setAttribute('playsinline', '')\n let stream: MediaStream | undefined\n let resolutionError: unknown\n for (const constraints of cameraConstraintCandidates(options)) {\n try {\n stream = await navigator.mediaDevices.getUserMedia(constraints)\n break\n } catch (cause) {\n resolutionError = cause\n if (!(cause instanceof DOMException) || cause.name !== 'OverconstrainedError') throw cause\n }\n }\n if (!stream) throw resolutionError ?? new Error('No rear camera resolution is available.')\n video.srcObject = stream\n await video.play()\n const track = stream.getVideoTracks()[0]\n if (!track) {\n for (const streamTrack of stream.getTracks()) streamTrack.stop()\n throw new Error('The camera did not provide a video track.')\n }\n await applyHighestPracticalResolution(track)\n const mirror = startFrameMirror(video)\n const hasImageCapture = 'ImageCapture' in globalThis\n const capabilities = typeof track.getCapabilities === 'function'\n ? track.getCapabilities() as MediaTrackCapabilities & { torch?: boolean }\n : undefined\n let torchStatus = torchControl(torchMode, capabilities?.torch === true)\n let torchUpdatePending = false\n const applyTorch = async (enabled: boolean) => {\n if (!torchStatus.supported || track.readyState !== 'live') return false\n try {\n await track.applyConstraints({\n advanced: [{ torch: enabled } as MediaTrackConstraintSet],\n })\n torchStatus = { ...torchStatus, on: enabled }\n return true\n } catch {\n // Some browsers report a torch capability but reject its constraint.\n // Fail closed and hide the control without disturbing the live track.\n torchStatus = {\n ...torchStatus,\n supported: false,\n on: false,\n visible: false,\n mutable: false,\n }\n return false\n }\n }\n\n if (torchStatus.on) await applyTorch(true)\n\n const autoTorch = torchMode === 'auto' && torchStatus.supported\n ? new AutoTorchController()\n : undefined\n const autoTorchTimer = autoTorch\n ? window.setInterval(() => {\n if (torchUpdatePending || !torchStatus.supported) return\n const requested = autoTorch.update(mirror.getLiveLuminance())\n if (requested === undefined || requested === torchStatus.on) return\n torchUpdatePending = true\n void applyTorch(requested).finally(() => {\n torchUpdatePending = false\n })\n }, 350)\n : 0\n\n const diagnostics = (): CameraDiagnostics => {\n const bounds = video.getBoundingClientRect()\n return {\n videoWidth: video.videoWidth,\n videoHeight: video.videoHeight,\n readyState: video.readyState,\n paused: video.paused,\n connected: video.isConnected,\n rendered: bounds.width > 0 && bounds.height > 0,\n trackReadyState: track.readyState ?? 'missing',\n trackEnabled: track.enabled,\n trackMuted: track.muted,\n liveLuminance: mirror.getLiveLuminance(),\n mirrorLuminance: mirror.getMirrorLuminance(),\n mirrorAgeMs: mirror.getMirrorAgeMs(),\n hasImageCapture,\n torchMode: torchStatus.mode,\n torchSupported: torchStatus.supported,\n torchOn: torchStatus.on,\n torch: { ...torchStatus },\n engine: detectBrowserEngine(),\n }\n }\n\n return {\n stream,\n get torchMode() {\n return torchStatus.mode\n },\n get torchSupported() {\n return torchStatus.supported\n },\n get torchOn() {\n return torchStatus.on\n },\n capture: (maxDimension) => captureFromVideoOrMirror(video, mirror, maxDimension),\n async captureLiveFrame(maxDimension) {\n const ImageCaptureApi = (globalThis as typeof globalThis & {\n ImageCapture?: new (track: MediaStreamTrack) => ImageCaptureLike\n }).ImageCapture\n if (ImageCaptureApi) {\n try {\n const bitmap = await new ImageCaptureApi(track).grabFrame?.()\n if (bitmap) {\n const size = scaledFrameSize(bitmap.width, bitmap.height, maxDimension)\n const canvas = document.createElement('canvas')\n canvas.width = size.width\n canvas.height = size.height\n const context = canvas.getContext('2d')\n if (context) {\n context.imageSmoothingEnabled = true\n if ('imageSmoothingQuality' in context) context.imageSmoothingQuality = 'high'\n context.drawImage(bitmap, 0, 0, canvas.width, canvas.height)\n }\n bitmap.close?.()\n if (canvas.width && !isNearBlackCanvas(canvas)) return canvas\n }\n } catch {\n // takePhoto is black on WebKit; grabFrame can fail the same way.\n }\n }\n try {\n return captureFromVideoOrMirror(video, mirror, maxDimension)\n } catch {\n return undefined\n }\n },\n // Copy the preview before any await. WebKit can blank the element during\n // the tap that triggers capture. Outside WebKit, prefer ImageCapture's\n // max-size still even when its aspect differs from the preview. The\n // <video> frame is only the live view and is too soft for OCR.\n async capturePhoto() {\n let liveFrame: HTMLCanvasElement | undefined\n let liveLuminance = 0\n try {\n liveFrame = captureVideoFrame(video)\n liveLuminance = canvasLuminance(liveFrame)\n } catch {\n liveFrame = undefined\n }\n const mirrorFrame = mirror.getFrame()\n let stillFrame: HTMLCanvasElement | undefined\n let stillFailed = false\n let stillPreviewSized = false\n let photoRequest: { imageWidth?: number; imageHeight?: number } | undefined\n let photoMax: { width?: number; height?: number } | undefined\n if (shouldUseImageCapture(detectBrowserEngine(), hasImageCapture)) {\n try {\n const still = await captureTrackStill(track)\n stillFrame = still?.frame\n photoRequest = still?.requested\n photoMax = still?.photoMax\n } catch {\n stillFailed = true\n stillFrame = undefined\n }\n }\n const previewSize = { width: video.videoWidth, height: video.videoHeight }\n const stillLuminance = stillFrame ? canvasLuminance(stillFrame) : 0\n const stillIsBetter = Boolean(\n stillFrame &&\n stillLuminance > NEAR_BLACK_LUMINANCE &&\n stillBeatsPreview(stillFrame, previewSize),\n )\n if (stillFrame && stillLuminance > NEAR_BLACK_LUMINANCE && !stillIsBetter) {\n stillPreviewSized = true\n }\n if (stillIsBetter && stillFrame) {\n console.info('[exige-scanner] capture', {\n method: 'image-capture',\n frame: `${stillFrame.width}×${stillFrame.height}`,\n video: `${video.videoWidth}×${video.videoHeight}`,\n photoMax: photoMax ? `${photoMax.width ?? '—'}×${photoMax.height ?? '—'}` : undefined,\n photoRequest,\n aspectMatch: stillMatchesPreview(stillFrame, previewSize),\n })\n return {\n frame: stillFrame,\n method: 'image-capture' as const,\n settings: track.getSettings(),\n luminance: stillLuminance,\n diagnostics: diagnostics(),\n }\n }\n const usableLive = liveFrame && liveLuminance > NEAR_BLACK_LUMINANCE\n ? liveFrame\n : undefined\n const snapshot = usableLive ?? (mirrorFrame ? cloneFrame(mirrorFrame) : undefined)\n if (!snapshot) {\n throw new Error(\n 'The camera preview produced no visible pixels. Keep the scanner on screen, wait for the preview, and retake.',\n )\n }\n const fallbackReason = stillPreviewSized\n ? 'image-capture-preview-size' as const\n : stillFrame\n ? 'image-capture-empty' as const\n : stillFailed\n ? 'image-capture-failed' as const\n : usableLive\n ? undefined\n : 'video-frame-empty' as const\n console.info('[exige-scanner] capture', {\n method: usableLive ? 'video-frame' : 'live-mirror',\n frame: `${snapshot.width}×${snapshot.height}`,\n video: `${video.videoWidth}×${video.videoHeight}`,\n photoMax: photoMax ? `${photoMax.width ?? '—'}×${photoMax.height ?? '—'}` : undefined,\n photoRequest,\n fallbackReason,\n })\n return {\n frame: snapshot,\n method: usableLive ? 'video-frame' as const : 'live-mirror' as const,\n settings: track.getSettings(),\n fallbackReason,\n luminance: usableLive ? liveLuminance : mirror.getMirrorLuminance(),\n diagnostics: diagnostics(),\n }\n },\n getSettings: () => track.getSettings(),\n getDiagnostics: diagnostics,\n getTorchStatus: () => ({ ...torchStatus }),\n async setTorch(enabled) {\n if (!torchStatus.mutable) return false\n return applyTorch(enabled)\n },\n stop() {\n if (autoTorchTimer) window.clearInterval(autoTorchTimer)\n mirror.stop()\n for (const track of stream.getTracks()) track.stop()\n if (video.srcObject === stream) video.srcObject = null\n },\n }\n}\n\n/** True when `input.click()` can still open the system camera. */\nexport function hasTransientUserActivation() {\n const activation = typeof navigator === 'undefined' ? undefined : navigator.userActivation\n if (!activation) return true\n return activation.isActive\n}\n\n/**\n * Opens the phone Camera app via `<input capture=\"environment\">` and returns\n * that JPEG. Chrome `ImageCapture.takePhoto()` cannot match Camera2/CameraX\n * quality: it is a single `TEMPLATE_STILL_CAPTURE` frame with no HDR+, Night\n * Sight, or OEM ISP, and PhotoSettings cannot set JPEG quality. Must run\n * from a user gesture (`useEffect` after `setState` loses it; a layout\n * effect in the same click does not).\n */\nexport async function pickNativeCameraStill(): Promise<HTMLCanvasElement> {\n const input = document.createElement('input')\n input.type = 'file'\n input.accept = 'image/*'\n input.capture = 'environment'\n input.setAttribute('capture', 'environment')\n const file = await new Promise<File>((resolve, reject) => {\n input.addEventListener('change', () => {\n const chosen = input.files?.[0]\n if (chosen) resolve(chosen)\n else reject(new Error('No photo was selected.'))\n }, { once: true })\n input.addEventListener('cancel', () => {\n reject(new Error('No photo was selected.'))\n }, { once: true })\n input.click()\n })\n return blobToStillCanvas(file)\n}\n","export interface PackagedOrtWasmPaths {\n wasm: string\n mjs: string\n}\n\n/**\n * Override for the packaged ONNX Runtime files.\n * A string is a directory that contains `ort-wasm-simd-threaded.jsep.wasm`\n * and `ort-wasm-simd-threaded.jsep.mjs`. An object sets each file URL.\n * Omit the option to use the copies shipped with the SDK.\n */\nexport type OrtWasmPaths = string | { wasm: string; mjs: string }\n\n/** Pinned onnxruntime-web version. Do not float `latest`. */\nexport const PINNED_ONNXRUNTIME_WEB_VERSION = '1.30.0' as const\n\nexport const PREFERRED_ORT_CDN_BASE =\n `https://exige-scanner-sdk.vercel.app/ort/${PINNED_ONNXRUNTIME_WEB_VERSION}/`\n\nexport const LAST_RESORT_ORT_CDN_BASE =\n `https://cdn.jsdelivr.net/npm/onnxruntime-web@${PINNED_ONNXRUNTIME_WEB_VERSION}/dist/`\n\nexport const SIBLING_ORT_MODULE = 'onnxruntime-web'\nexport const ORT_JS_FILENAME = 'ort.min.mjs'\nexport const ORT_WASM_FILENAME = 'ort-wasm-simd-threaded.jsep.wasm'\nexport const ORT_WASM_MJS_FILENAME = 'ort-wasm-simd-threaded.jsep.mjs'\n\nexport type OrtModule = typeof import('onnxruntime-web')\nexport type OrtLoadSource = 'sibling' | 'preferred-cdn' | 'last-resort-cdn'\nexport type OrtImporter = (specifier: string) => Promise<OrtModule>\n\nexport function joinOrtCdnUrl(base: string, fileName: string): string {\n const root = base.endsWith('/') ? base : `${base}/`\n return `${root}${fileName}`\n}\n\nexport function siblingOrtUrl(workerHref: string): string {\n return new URL('./ort.min.js', workerHref).href\n}\n\nexport function pinnedOrtJsUrls(): readonly [string, string] {\n return [\n joinOrtCdnUrl(PREFERRED_ORT_CDN_BASE, ORT_JS_FILENAME),\n joinOrtCdnUrl(LAST_RESORT_ORT_CDN_BASE, ORT_JS_FILENAME),\n ]\n}\n\nexport function pinnedOrtWasmPaths(base: string = PREFERRED_ORT_CDN_BASE): PackagedOrtWasmPaths {\n return {\n wasm: joinOrtCdnUrl(base, ORT_WASM_FILENAME),\n mjs: joinOrtCdnUrl(base, ORT_WASM_MJS_FILENAME),\n }\n}\n\nexport function expandOrtWasmPaths(value: OrtWasmPaths): PackagedOrtWasmPaths {\n if (typeof value === 'string') {\n return {\n wasm: joinOrtCdnUrl(value, ORT_WASM_FILENAME),\n mjs: joinOrtCdnUrl(value, ORT_WASM_MJS_FILENAME),\n }\n }\n return { wasm: value.wasm, mjs: value.mjs }\n}\n\nfunction hasOrtWasmValue(value?: OrtWasmPaths): value is OrtWasmPaths {\n if (typeof value === 'string') return value.trim().length > 0\n return Boolean(value?.wasm.trim() && value.mjs.trim())\n}\n\nfunction isSuccessfulAssetResponse(response: Response): boolean {\n if (!response.ok && response.status !== 206) return false\n const type = response.headers.get('content-type') ?? ''\n return !type.toLowerCase().includes('text/html')\n}\n\n/** HEAD, then GET. Treat HTML (SPA fallback) and 404 as missing. */\nexport async function probeHttpUrl(\n url: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<boolean> {\n if (!url) return false\n try {\n const head = await fetchImpl(url, { method: 'HEAD' })\n if (isSuccessfulAssetResponse(head)) return true\n if (head.status === 404 || head.status === 410) return false\n } catch {\n // Some hosts reject HEAD; try GET.\n }\n try {\n const get = await fetchImpl(url, {\n method: 'GET',\n headers: { Range: 'bytes=0-63' },\n })\n return isSuccessfulAssetResponse(get)\n } catch {\n return false\n }\n}\n\nexport async function probeOrtWasmPaths(\n value: OrtWasmPaths,\n fetchImpl: typeof fetch = fetch,\n): Promise<boolean> {\n const { wasm, mjs } = expandOrtWasmPaths(value)\n const [wasmOk, mjsOk] = await Promise.all([\n probeHttpUrl(wasm, fetchImpl),\n probeHttpUrl(mjs, fetchImpl),\n ])\n return wasmOk && mjsOk\n}\n\n/**\n * Prefer same-origin / caller wasm when those URLs exist.\n * A 404 override still falls back to the pinned CDN pair.\n */\nexport async function resolveUsableOrtWasmPaths(\n value?: OrtWasmPaths,\n fetchImpl: typeof fetch = fetch,\n): Promise<OrtWasmPaths> {\n if (hasOrtWasmValue(value) && await probeOrtWasmPaths(value, fetchImpl)) {\n if (typeof value === 'string') return value.trim()\n return { wasm: value.wasm.trim(), mjs: value.mjs.trim() }\n }\n const preferred = pinnedOrtWasmPaths(PREFERRED_ORT_CDN_BASE)\n if (await probeOrtWasmPaths(preferred, fetchImpl)) return preferred\n return pinnedOrtWasmPaths(LAST_RESORT_ORT_CDN_BASE)\n}\n\nexport async function importOrtSpecifier(specifier: string): Promise<OrtModule> {\n return import(specifier) as Promise<OrtModule>\n}\n\nexport async function loadOrtJsFromPinnedCdn(\n importModule: OrtImporter = importOrtSpecifier,\n): Promise<OrtModule> {\n const errors: unknown[] = []\n for (const url of pinnedOrtJsUrls()) {\n try {\n return await importModule(url)\n } catch (error) {\n errors.push(error)\n }\n }\n throw new Error(\n `Failed to load onnxruntime-web@${PINNED_ONNXRUNTIME_WEB_VERSION} from the pinned CDN.`,\n { cause: errors.at(-1) },\n )\n}\n\n/**\n * Sibling `onnxruntime-web` first (webpack copies `./ort.min.js`).\n * Next 15 turbo leaves that sibling missing, so use the pinned CDN.\n */\nexport async function loadOrtRuntime(\n importModule: OrtImporter = importOrtSpecifier,\n): Promise<{ ort: OrtModule; source: OrtLoadSource }> {\n try {\n return { ort: await importModule(SIBLING_ORT_MODULE), source: 'sibling' }\n } catch {\n // Next turbo emits the worker without a sibling ort.min.js.\n }\n const [preferred, lastResort] = pinnedOrtJsUrls()\n try {\n return { ort: await importModule(preferred), source: 'preferred-cdn' }\n } catch {\n return { ort: await importModule(lastResort), source: 'last-resort-cdn' }\n }\n}\n","/**\n * ONNX Runtime Web 1.30 extern-wasm JSEP build. Document finding uses\n * WebGPU on WebKit when exposed, otherwise WASM. Android stays WASM.\n * Literal `new URL` calls are what Webpack and Turbopack copy.\n */\nexport const packagedOrtWasmFile = new URL(\n './runtime/ort-wasm-simd-threaded.jsep.wasm',\n import.meta.url,\n)\nexport const packagedOrtWasmGlue = new URL(\n './runtime/ort-wasm-simd-threaded.jsep.mjs',\n import.meta.url,\n)\n\nexport type {\n OrtLoadSource,\n OrtImporter,\n OrtModule,\n OrtWasmPaths,\n PackagedOrtWasmPaths,\n} from './ort-cdn'\nexport {\n LAST_RESORT_ORT_CDN_BASE,\n ORT_JS_FILENAME,\n ORT_WASM_FILENAME,\n ORT_WASM_MJS_FILENAME,\n PINNED_ONNXRUNTIME_WEB_VERSION,\n PREFERRED_ORT_CDN_BASE,\n SIBLING_ORT_MODULE,\n expandOrtWasmPaths,\n importOrtSpecifier,\n joinOrtCdnUrl,\n loadOrtJsFromPinnedCdn,\n loadOrtRuntime,\n pinnedOrtJsUrls,\n pinnedOrtWasmPaths,\n probeHttpUrl,\n probeOrtWasmPaths,\n resolveUsableOrtWasmPaths,\n siblingOrtUrl,\n} from './ort-cdn'\n\nimport { type OrtWasmPaths, type PackagedOrtWasmPaths } from './ort-cdn'\n\nexport function packagedOrtWasmPaths(): PackagedOrtWasmPaths {\n return {\n wasm: packagedOrtWasmFile.href,\n mjs: packagedOrtWasmGlue.href,\n }\n}\n\n/** Uses the packaged wasm pair when the override is missing or blank. */\nexport function resolveOrtWasmPaths(value?: OrtWasmPaths): OrtWasmPaths {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed) return trimmed\n return packagedOrtWasmPaths()\n }\n if (value?.wasm.trim() && value.mjs.trim()) {\n return { wasm: value.wasm.trim(), mjs: value.mjs.trim() }\n }\n return packagedOrtWasmPaths()\n}\n","import { canvasUnavailable } from './errors'\nimport type { ImageSource } from './types'\n\nexport interface CanvasSourceOptions {\n cloneCanvas?: boolean\n maxDimension?: number\n}\n\nfunction scaledSize(width: number, height: number, maxDimension?: number) {\n const scale = maxDimension ? Math.min(1, maxDimension / Math.max(width, height)) : 1\n return {\n width: Math.max(1, Math.round(width * scale)),\n height: Math.max(1, Math.round(height * scale)),\n }\n}\n\nasync function blobToImage(blob: Blob) {\n const url = URL.createObjectURL(blob)\n try {\n const image = new Image()\n image.src = url\n await image.decode()\n return image\n } finally {\n URL.revokeObjectURL(url)\n }\n}\n\nexport async function sourceToCanvas(\n source: ImageSource,\n options: CanvasSourceOptions = {},\n): Promise<HTMLCanvasElement> {\n if (source instanceof HTMLCanvasElement && options.cloneCanvas === false && !options.maxDimension) {\n return source\n }\n const canvas = document.createElement('canvas')\n const context = canvas.getContext('2d', { willReadFrequently: true })\n if (!context) throw canvasUnavailable()\n\n if (source instanceof Blob) {\n if ('createImageBitmap' in window) {\n try {\n const bitmap = await createImageBitmap(source)\n const size = scaledSize(bitmap.width, bitmap.height, options.maxDimension)\n canvas.width = size.width\n canvas.height = size.height\n context.drawImage(bitmap, 0, 0, size.width, size.height)\n bitmap.close()\n return canvas\n } catch {\n // Fall through to HTMLImageElement decoding for iOS/WebKit formats.\n }\n }\n source = await blobToImage(source)\n }\n\n const width = source instanceof HTMLImageElement ? source.naturalWidth : source.width\n const height = source instanceof HTMLImageElement ? source.naturalHeight : source.height\n if (!width || !height) throw new Error('The image has no readable dimensions.')\n const size = scaledSize(width, height, options.maxDimension)\n canvas.width = size.width\n canvas.height = size.height\n context.drawImage(source, 0, 0, size.width, size.height)\n return canvas\n}\n\nexport function canvasToBlob(\n canvas: HTMLCanvasElement,\n type = 'image/png',\n quality = 0.92,\n): Promise<Blob> {\n return new Promise((resolve, reject) =>\n canvas.toBlob(\n (blob) => (blob ? resolve(blob) : reject(new Error('Could not encode the image.'))),\n type,\n quality,\n ),\n )\n}\n\nexport function clampBox(\n box: { x: number; y: number; width: number; height: number },\n width: number,\n height: number,\n) {\n const x = Math.max(0, Math.min(width, box.x))\n const y = Math.max(0, Math.min(height, box.y))\n return {\n x,\n y,\n width: Math.max(0, Math.min(width - x, box.width)),\n height: Math.max(0, Math.min(height - y, box.height)),\n }\n}\n","import { shouldPreferWebGpuForDocumentModel } from './camera'\nimport { canvasUnavailable, ScannerError } from './errors'\nimport { assertLicense } from './license'\nimport { reportModelDownload } from './model-download'\nimport { resolveOrtWasmPaths, type OrtWasmPaths } from './ort-runtime'\nimport { sourceToCanvas } from './image'\nimport type {\n DocumentCorners,\n DocumentDetectionResult,\n DocumentModelStatus,\n DocumentQuad,\n ImageSource,\n Point,\n ScanResult,\n} from './types'\n\nexport interface ScanOptions {\n minDimension?: number\n minConfidence?: number\n maxAnalysisDimension?: number\n maxOutputDimension?: number\n useWorker?: boolean\n /**\n * DocAligner checkpoint URL. Omit this. The default is the Exige CDN\n * DocAligner model (`DEFAULT_DOCUMENT_MODEL_URL`).\n */\n modelUrl?: string\n /** Heatmap threshold used before largest-component corner centroids. */\n heatmapThreshold?: number\n /** Reuse model bytes through the browser HTTP cache. */\n cacheModel?: boolean\n /**\n * ONNX Runtime wasm override. Omit this. The default is the packaged\n * `ort-wasm-simd-threaded.jsep.wasm` and `.mjs` files.\n * A string is a directory that contains both files. An object sets each URL.\n */\n ortWasmPaths?: OrtWasmPaths\n /**\n * Try WebGPU for DocAligner. Default is WebKit only. Android Chrome stays\n * on WASM because empty heatmaps made WebGPU unusable there.\n */\n preferWebGpu?: boolean\n}\n\nexport const DEFAULT_DOCUMENT_MODEL_URL =\n 'https://exige-scanner-sdk.vercel.app/models/docaligner-lcnet100-f4117b78.onnx'\nexport const TALL_PAGE_ASPECT = 1.6\nexport const TALL_PAGE_ANALYSIS_DIMENSION = 1280\n/** Matches `STILL_MAX_EDGE`. Mobile no longer crushes crops to 2048. */\nexport const DEFAULT_OUTPUT_DIMENSION = 4096\n\nexport function analysisDimensionForSource(\n width: number,\n height: number,\n requested?: number,\n) {\n if (requested) return requested\n const longEdge = Math.max(width, height)\n const shortEdge = Math.max(1, Math.min(width, height))\n const aspect = longEdge / shortEdge\n const base = isMobileClassDevice() ? 560 : 900\n if (aspect >= TALL_PAGE_ASPECT) {\n return Math.min(longEdge, Math.max(base, TALL_PAGE_ANALYSIS_DIMENSION))\n }\n return base\n}\n\nexport function resolveDocumentModelUrl(modelUrl?: string) {\n return modelUrl?.trim() || DEFAULT_DOCUMENT_MODEL_URL\n}\n\ninterface WorkerAnalysis {\n sharpness: number\n candidate?: { corners: DocumentCorners; confidence: number }\n detector?: 'ml'\n modelStatus?: DocumentModelStatus\n modelProvider?: 'webgpu' | 'wasm'\n modelError?: string\n modelNote?: string\n inferenceMs?: number\n}\n\nconst MIN_DOCUMENT_SHARPNESS = 3\n\nlet analysisWorker: Worker | undefined\nlet analysisRequestId = 0\nconst analysisRequests = new Map<number, {\n resolve: (analysis: WorkerAnalysis) => void\n reject: (error: Error) => void\n}>()\n\nfunction distance(a: Point, b: Point) {\n return Math.hypot(a.x - b.x, a.y - b.y)\n}\n\nfunction toQuad(corners: DocumentCorners): DocumentQuad {\n return {\n ...corners,\n edges: {\n top: { start: corners.topLeft, end: corners.topRight },\n right: { start: corners.topRight, end: corners.bottomRight },\n bottom: { start: corners.bottomRight, end: corners.bottomLeft },\n left: { start: corners.bottomLeft, end: corners.topLeft },\n },\n }\n}\n\nfunction scaleCorners(corners: DocumentCorners, scale: number): DocumentCorners {\n const resize = (point: Point) => ({ x: point.x / scale, y: point.y / scale })\n return {\n topLeft: resize(corners.topLeft),\n topRight: resize(corners.topRight),\n bottomRight: resize(corners.bottomRight),\n bottomLeft: resize(corners.bottomLeft),\n }\n}\n\nfunction isMobileClassDevice() {\n if (typeof navigator === 'undefined') return false\n return navigator.maxTouchPoints > 0 || navigator.hardwareConcurrency <= 4 ||\n (typeof matchMedia === 'function' && matchMedia('(pointer: coarse)').matches)\n}\n\nfunction analyzeInWorker(\n pixels: Uint8ClampedArray,\n width: number,\n height: number,\n options: ScanOptions,\n): Promise<WorkerAnalysis> {\n return new Promise((resolve, reject) => {\n if (!analysisWorker) {\n analysisWorker = new Worker(new URL('./document-worker.ts', import.meta.url), { type: 'module' })\n analysisWorker.onmessage = (event: MessageEvent<{\n id: number\n result?: WorkerAnalysis\n error?: string\n errorCode?: ScannerError['code']\n download?: { url: string; loaded: number; total: number | null; done?: boolean }\n }>) => {\n if (event.data.download?.url) {\n reportModelDownload(event.data.download.url, event.data.download)\n return\n }\n const pending = analysisRequests.get(event.data.id)\n if (!pending) return\n analysisRequests.delete(event.data.id)\n if (event.data.error) {\n pending.reject(event.data.errorCode\n ? new ScannerError(event.data.errorCode, event.data.error)\n : new Error(event.data.error))\n }\n else if (event.data.result) pending.resolve(event.data.result)\n else pending.reject(new Error('The document worker returned no result.'))\n }\n analysisWorker.onerror = () => {\n const error = new ScannerError('worker', 'The document worker could not start.')\n for (const pending of analysisRequests.values()) pending.reject(error)\n analysisRequests.clear()\n analysisWorker?.terminate()\n analysisWorker = undefined\n }\n }\n const id = ++analysisRequestId\n analysisRequests.set(id, { resolve, reject })\n const transferable = pixels.slice().buffer\n analysisWorker.postMessage({\n id,\n pixels: transferable,\n width,\n height,\n modelUrl: resolveDocumentModelUrl(options.modelUrl),\n heatmapThreshold: options.heatmapThreshold,\n cacheModel: options.cacheModel,\n ortWasmPaths: resolveOrtWasmPaths(options.ortWasmPaths),\n preferWebGpu: options.preferWebGpu ?? shouldPreferWebGpuForDocumentModel(),\n }, [transferable])\n })\n}\n\nasync function analyzeCanvas(\n canvas: HTMLCanvasElement,\n options: ScanOptions,\n): Promise<DocumentDetectionResult> {\n const { width, height } = canvas\n const minDimension = options.minDimension ?? 360\n if (Math.min(width, height) < minDimension) {\n return {\n status: 'low-quality',\n confidence: 0,\n message: `Use an image at least ${minDimension}px on its shortest side.`,\n sourceWidth: width,\n sourceHeight: height,\n }\n }\n\n const analysisDimension = analysisDimensionForSource(\n width,\n height,\n options.maxAnalysisDimension,\n )\n const scale = Math.min(1, analysisDimension / Math.max(width, height))\n const sampleWidth = Math.max(1, Math.round(width * scale))\n const sampleHeight = Math.max(1, Math.round(height * scale))\n let pixels: Uint8ClampedArray\n if (scale === 1) {\n const context = canvas.getContext('2d', { willReadFrequently: true })\n if (!context) throw canvasUnavailable()\n pixels = context.getImageData(0, 0, sampleWidth, sampleHeight).data\n } else {\n const sample = document.createElement('canvas')\n sample.width = sampleWidth\n sample.height = sampleHeight\n const context = sample.getContext('2d', { willReadFrequently: true })\n if (!context) throw canvasUnavailable()\n context.drawImage(canvas, 0, 0, sample.width, sample.height)\n pixels = context.getImageData(0, 0, sample.width, sample.height).data\n }\n let analysis: WorkerAnalysis\n if (options.useWorker !== false && typeof Worker !== 'undefined') {\n try {\n analysis = await analyzeInWorker(\n pixels,\n sampleWidth,\n sampleHeight,\n options,\n )\n } catch (error) {\n if (\n error instanceof ScannerError &&\n (error.code === 'worker' || error.code === 'model-http')\n ) {\n throw error\n }\n const message = error instanceof Error ? error.message : 'Document worker failed.'\n analysis = {\n sharpness: 20,\n detector: 'ml',\n modelStatus: 'failed',\n modelError: message,\n }\n }\n } else {\n analysis = {\n sharpness: 20,\n detector: 'ml',\n modelStatus: 'failed',\n modelError: 'ML detection requires a Web Worker.',\n }\n }\n const {\n sharpness,\n candidate,\n detector,\n modelStatus,\n modelProvider,\n modelError,\n modelNote,\n inferenceMs,\n } = analysis\n const detectorMetadata = {\n detector,\n modelStatus,\n modelProvider,\n modelError,\n modelNote,\n inferenceMs,\n }\n if (sharpness < MIN_DOCUMENT_SHARPNESS) {\n return {\n status: 'low-quality',\n confidence: Math.min(0.3, sharpness / 18),\n message: 'The image is too soft or dark to resolve four document edges.',\n sourceWidth: width,\n sourceHeight: height,\n ...detectorMetadata,\n }\n }\n\n if (!candidate) {\n return {\n status: 'no-document',\n confidence: 0,\n message: 'No document corners found. Place all four edges on a contrasting surface.',\n sourceWidth: width,\n sourceHeight: height,\n ...detectorMetadata,\n }\n }\n\n const corners = scaleCorners(candidate.corners, scale)\n const quad = toQuad(corners)\n const minimumConfidence = options.minConfidence ?? 0.58\n if (candidate.confidence < minimumConfidence) {\n return {\n status: 'low-confidence',\n quad,\n corners,\n confidence: candidate.confidence,\n message: 'Four possible corners were found, but the geometry is not reliable enough to crop.',\n sourceWidth: width,\n sourceHeight: height,\n ...detectorMetadata,\n }\n }\n return {\n status: 'success',\n quad,\n corners,\n confidence: candidate.confidence,\n message: 'Four document edges detected.',\n sourceWidth: width,\n sourceHeight: height,\n ...detectorMetadata,\n }\n}\n\nexport function perspectiveOutputSize(corners: DocumentCorners, maxDimension: number) {\n const naturalWidth = Math.max(\n 1,\n Math.round(Math.max(\n distance(corners.topLeft, corners.topRight),\n distance(corners.bottomLeft, corners.bottomRight),\n )),\n )\n const naturalHeight = Math.max(\n 1,\n Math.round(Math.max(\n distance(corners.topLeft, corners.bottomLeft),\n distance(corners.topRight, corners.bottomRight),\n )),\n )\n const outputScale = Math.min(1, maxDimension / Math.max(naturalWidth, naturalHeight))\n return {\n width: Math.max(1, Math.round(naturalWidth * outputScale)),\n height: Math.max(1, Math.round(naturalHeight * outputScale)),\n }\n}\n\nexport function sampleBilinear(\n pixels: Uint8ClampedArray,\n width: number,\n height: number,\n x: number,\n y: number,\n) {\n const clampedX = Math.max(0, Math.min(width - 1, x))\n const clampedY = Math.max(0, Math.min(height - 1, y))\n const x0 = Math.floor(clampedX)\n const y0 = Math.floor(clampedY)\n const x1 = Math.min(width - 1, x0 + 1)\n const y1 = Math.min(height - 1, y0 + 1)\n const tx = clampedX - x0\n const ty = clampedY - y0\n const row0 = y0 * width\n const row1 = y1 * width\n const sample = (channel: number) => {\n const top = pixels[(row0 + x0) * 4 + channel] * (1 - tx) +\n pixels[(row0 + x1) * 4 + channel] * tx\n const bottom = pixels[(row1 + x0) * 4 + channel] * (1 - tx) +\n pixels[(row1 + x1) * 4 + channel] * tx\n return Math.round(top * (1 - ty) + bottom * ty)\n }\n return [sample(0), sample(1), sample(2), 255]\n}\n\nfunction perspectiveWarp(source: HTMLCanvasElement, corners: DocumentCorners, maxDimension: number) {\n const { width, height } = perspectiveOutputSize(corners, maxDimension)\n const sourceContext = source.getContext('2d', { willReadFrequently: true })\n if (!sourceContext) throw canvasUnavailable()\n const sourcePixels = sourceContext.getImageData(0, 0, source.width, source.height)\n const output = document.createElement('canvas')\n output.width = width\n output.height = height\n const outputContext = output.getContext('2d')\n if (!outputContext) throw canvasUnavailable()\n const outputPixels = outputContext.createImageData(width, height)\n const { topLeft: tl, topRight: tr, bottomRight: br, bottomLeft: bl } = corners\n const dx1 = tr.x - br.x\n const dx2 = bl.x - br.x\n const dy1 = tr.y - br.y\n const dy2 = bl.y - br.y\n const sx = tl.x - tr.x + br.x - bl.x\n const sy = tl.y - tr.y + br.y - bl.y\n const denominator = dx1 * dy2 - dx2 * dy1\n const projectiveX = Math.abs(denominator) < 0.0001 ? 0 : (sx * dy2 - dx2 * sy) / denominator\n const projectiveY = Math.abs(denominator) < 0.0001 ? 0 : (dx1 * sy - sx * dy1) / denominator\n const a = tr.x - tl.x + projectiveX * tr.x\n const b = bl.x - tl.x + projectiveY * bl.x\n const d = tr.y - tl.y + projectiveX * tr.y\n const e = bl.y - tl.y + projectiveY * bl.y\n\n for (let y = 0; y < height; y += 1) {\n const v = y / Math.max(1, height - 1)\n for (let x = 0; x < width; x += 1) {\n const u = x / Math.max(1, width - 1)\n const divisor = projectiveX * u + projectiveY * v + 1\n const sourceX = (a * u + b * v + tl.x) / divisor\n const sourceY = (d * u + e * v + tl.y) / divisor\n const [red, green, blue, alpha] = sampleBilinear(\n sourcePixels.data,\n source.width,\n source.height,\n sourceX,\n sourceY,\n )\n const outputOffset = (y * width + x) * 4\n outputPixels.data[outputOffset] = red\n outputPixels.data[outputOffset + 1] = green\n outputPixels.data[outputOffset + 2] = blue\n outputPixels.data[outputOffset + 3] = alpha\n }\n }\n outputContext.putImageData(outputPixels, 0, 0)\n return output\n}\n\n/**\n * Finds document corners. Returns `success`, `no-document`, `low-confidence`,\n * or `low-quality`. A bad image is one of those statuses and does not throw.\n * Inference or geometry failure is `modelStatus: 'failed'`, not a throw.\n *\n * @throws {LicenseError} LicenseError when the scan feature is not licensed. Detection does not start.\n * @throws {ScannerError} ScannerError `canvas` when canvas 2D is unavailable.\n * `worker` when the document worker cannot start.\n * `model-http` when the DocAligner file returns a failed HTTP response.\n */\nexport async function detectDocument(\n source: ImageSource,\n options: ScanOptions = {},\n): Promise<DocumentDetectionResult> {\n await assertLicense('scan')\n return analyzeCanvas(await sourceToCanvas(source, { cloneCanvas: false }), options)\n}\n\n/**\n * Perspective-crops `corners` onto a new canvas.\n * Resolves with `status: 'success'`. It does not score image quality.\n *\n * @throws {LicenseError} LicenseError when the scan feature is not licensed.\n * @throws {ScannerError} ScannerError `canvas` when canvas 2D is unavailable.\n */\nexport async function cropDocument(\n source: ImageSource,\n corners: DocumentCorners,\n options: Pick<ScanOptions, 'maxOutputDimension'> = {},\n): Promise<ScanResult> {\n await assertLicense('scan')\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const maxOutputDimension = options.maxOutputDimension ?? DEFAULT_OUTPUT_DIMENSION\n return {\n status: 'success',\n corners,\n quad: toQuad(corners),\n confidence: 1,\n message: 'Captured with the document edges shown in the finder.',\n sourceWidth: canvas.width,\n sourceHeight: canvas.height,\n canvas: perspectiveWarp(canvas, corners, maxOutputDimension),\n }\n}\n\n/**\n * Detects a document and perspective-crops it.\n * Returns `success`, `no-document`, `low-confidence`, or `low-quality`.\n * A bad image is one of those statuses and does not throw.\n * Inference or geometry failure is `modelStatus: 'failed'`, not a throw.\n *\n * @throws {LicenseError} LicenseError when the scan feature is not licensed. Detection does not start.\n * @throws {ScannerError} ScannerError `canvas` when canvas 2D is unavailable.\n * `worker` when the document worker cannot start.\n * `model-http` when the DocAligner file returns a failed HTTP response.\n */\nexport async function scanDocument(\n source: ImageSource,\n options: ScanOptions = {},\n): Promise<ScanResult> {\n await assertLicense('scan')\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const detection = await analyzeCanvas(canvas, options)\n if (detection.status !== 'success' || !detection.corners) return detection\n const maxOutputDimension = options.maxOutputDimension ?? DEFAULT_OUTPUT_DIMENSION\n return {\n ...detection,\n canvas: perspectiveWarp(canvas, detection.corners, maxOutputDimension),\n message: 'Four document edges detected and perspective-corrected.',\n }\n}\n\n/** User-visible copy when a system-camera still has no cropable document. */\nexport function systemCameraFindMessage(result: Pick<DocumentDetectionResult, 'status' | 'message' | 'modelStatus' | 'modelError'>) {\n if (result.status === 'success') return result.message\n if (result.status === 'low-quality') {\n return result.message.includes('shortest side')\n ? `${result.message} Take another photo.`\n : 'That photo is too soft or dark to find a document. Take another photo in better light.'\n }\n if (result.status === 'low-confidence') {\n return 'The page edges were not clear enough to crop. Take another photo with the whole page in frame.'\n }\n const modelFail = result.modelStatus === 'failed' && result.modelError\n ? ` (${result.modelError})`\n : ''\n return `No document was found in that photo. Take another photo with the whole page in frame.${modelFail}`\n}\n\n/**\n * Runs DocAligner on a system-camera JPEG and crops the most prominent\n * document (the single largest-component quad the model returns). A miss\n * is a status plus a retry-oriented message. It does not throw.\n *\n * @throws {LicenseError} LicenseError when the scan feature is not licensed.\n */\nexport async function scanSystemCameraStill(\n source: ImageSource,\n options: ScanOptions = {},\n): Promise<ScanResult> {\n const result = await scanDocument(source, options)\n if (result.status === 'success') return result\n return {\n ...result,\n message: systemCameraFindMessage(result),\n }\n}\n","import type { DocumentQuad } from './types'\n\nexport interface CastShadowRegion {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface CastShadowAnalysis {\n strong: boolean\n strength: number\n darkness: number\n areaRatio: number\n region?: CastShadowRegion\n}\n\nfunction percentile(values: number[], fraction: number) {\n if (!values.length) return 0\n const sorted = [...values].sort((first, second) => first - second)\n return sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * fraction))]\n}\n\n/**\n * Finds a connected, page-sized dark blob in any direction. A component must\n * cover meaningful area and contain local contrast, distinguishing cast\n * shadows over printed paper from a dark border or isolated illustration.\n */\nexport function analyzeCastShadowLuminance(\n luminance: Uint8Array,\n width: number,\n height: number,\n gridSize = 12,\n): CastShadowAnalysis {\n if (!width || !height || luminance.length < width * height) {\n return { strong: false, strength: 0, darkness: 0, areaRatio: 0 }\n }\n const columns = Math.min(gridSize, width)\n const rows = Math.min(gridSize, height)\n const cells: Array<{ mean: number; deviation: number }> = []\n for (let row = 0; row < rows; row += 1) {\n const top = Math.floor(row * height / rows)\n const bottom = Math.max(top + 1, Math.floor((row + 1) * height / rows))\n for (let column = 0; column < columns; column += 1) {\n const left = Math.floor(column * width / columns)\n const right = Math.max(left + 1, Math.floor((column + 1) * width / columns))\n let sum = 0\n let squared = 0\n let count = 0\n for (let y = top; y < bottom; y += 1) {\n for (let x = left; x < right; x += 1) {\n const value = luminance[y * width + x]\n sum += value\n squared += value * value\n count += 1\n }\n }\n const mean = sum / count\n cells.push({\n mean,\n deviation: Math.sqrt(Math.max(0, squared / count - mean * mean)),\n })\n }\n }\n\n const brightPaper = percentile(cells.map((cell) => cell.mean), 0.8)\n const dark = cells.map((cell) =>\n brightPaper - cell.mean >= 24 &&\n cell.mean >= 35,\n )\n const visited = new Uint8Array(cells.length)\n const components: number[][] = []\n for (let start = 0; start < cells.length; start += 1) {\n if (!dark[start] || visited[start]) continue\n const component: number[] = []\n const queue = [start]\n visited[start] = 1\n while (queue.length) {\n const index = queue.shift()!\n component.push(index)\n const row = Math.floor(index / columns)\n const column = index % columns\n for (const [nextColumn, nextRow] of [\n [column - 1, row],\n [column + 1, row],\n [column, row - 1],\n [column, row + 1],\n ]) {\n if (\n nextColumn < 0 ||\n nextColumn >= columns ||\n nextRow < 0 ||\n nextRow >= rows\n ) continue\n const next = nextRow * columns + nextColumn\n if (dark[next] && !visited[next]) {\n visited[next] = 1\n queue.push(next)\n }\n }\n }\n components.push(component)\n }\n\n let best: CastShadowAnalysis = {\n strong: false,\n strength: 0,\n darkness: 0,\n areaRatio: 0,\n }\n for (const component of components) {\n const areaRatio = component.length / cells.length\n if (areaRatio < 0.06 || areaRatio > 0.75) continue\n const darkness = component.reduce(\n (total, index) => total + brightPaper - cells[index].mean,\n 0,\n ) / component.length\n const printedCells = component.filter((index) => cells[index].deviation >= 9).length\n const printRatio = printedCells / component.length\n if (printRatio < 0.08 && darkness < 42) continue\n const columnsInComponent = component.map((index) => index % columns)\n const rowsInComponent = component.map((index) => Math.floor(index / columns))\n const minColumn = Math.min(...columnsInComponent)\n const maxColumn = Math.max(...columnsInComponent)\n const minRow = Math.min(...rowsInComponent)\n const maxRow = Math.max(...rowsInComponent)\n const padding = 1\n const left = Math.max(0, minColumn - padding) / columns\n const top = Math.max(0, minRow - padding) / rows\n const right = Math.min(columns, maxColumn + padding + 1) / columns\n const bottom = Math.min(rows, maxRow + padding + 1) / rows\n const strength = darkness * Math.sqrt(areaRatio) * (0.75 + Math.min(0.25, printRatio))\n if (strength > best.strength) {\n best = {\n strong: strength >= 10,\n strength,\n darkness,\n areaRatio,\n region: {\n x: left,\n y: top,\n width: right - left,\n height: bottom - top,\n },\n }\n }\n }\n return best\n}\n\nfunction localMeanAt(\n integral: Uint32Array,\n stride: number,\n width: number,\n height: number,\n x: number,\n y: number,\n radius: number,\n) {\n const top = Math.max(0, y - radius)\n const bottom = Math.min(height, y + radius + 1)\n const left = Math.max(0, x - radius)\n const right = Math.min(width, x + radius + 1)\n const area = Math.max(1, (right - left) * (bottom - top))\n return (\n integral[bottom * stride + right] -\n integral[top * stride + right] -\n integral[bottom * stride + left] +\n integral[top * stride + left]\n ) / area\n}\n\nfunction integralImage(luminance: Uint8Array, width: number, height: number) {\n const stride = width + 1\n const integral = new Uint32Array((width + 1) * (height + 1))\n for (let y = 1; y <= height; y += 1) {\n let row = 0\n for (let x = 1; x <= width; x += 1) {\n row += luminance[(y - 1) * width + x - 1]\n integral[y * stride + x] = integral[(y - 1) * stride + x] + row\n }\n }\n return { integral, stride }\n}\n\nexport function flattenIlluminationLuminance(\n luminance: Uint8Array,\n width: number,\n height: number,\n radius = Math.max(12, Math.round(Math.max(width, height) / 32)),\n) {\n const { integral, stride } = integralImage(luminance, width, height)\n const output = new Uint8Array(luminance.length)\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const background = localMeanAt(\n integral,\n stride,\n width,\n height,\n x,\n y,\n radius,\n )\n output[y * width + x] = Math.max(\n 0,\n Math.min(255, Math.round(luminance[y * width + x] * 235 / Math.max(24, background))),\n )\n }\n }\n return output\n}\n\n/**\n * Bradley–Roth local-mean threshold. A pixel is ink when it is darker\n * than the neighborhood mean. One page-wide Otsu cut cannot keep both\n * lit paper and a soft shade as paper.\n */\nexport function adaptiveThresholdLuminance(\n luminance: Uint8Array,\n width: number,\n height: number,\n radius = Math.max(8, Math.round(Math.max(width, height) / 64)),\n sensitivity = 0.12,\n) {\n const { integral, stride } = integralImage(luminance, width, height)\n const output = new Uint8Array(luminance.length)\n const gain = 1 - sensitivity\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const mean = localMeanAt(integral, stride, width, height, x, y, radius)\n output[y * width + x] = luminance[y * width + x] < mean * gain ? 0 : 255\n }\n }\n return output\n}\n\nfunction quadBounds(quad: DocumentQuad, width: number, height: number) {\n const points = [quad.topLeft, quad.topRight, quad.bottomRight, quad.bottomLeft]\n const left = Math.max(0, Math.min(...points.map((point) => point.x)))\n const top = Math.max(0, Math.min(...points.map((point) => point.y)))\n const right = Math.min(width, Math.max(...points.map((point) => point.x)))\n const bottom = Math.min(height, Math.max(...points.map((point) => point.y)))\n return { left, top, width: right - left, height: bottom - top }\n}\n\nexport function analyzeCanvasCastShadow(\n canvas: HTMLCanvasElement,\n quad: DocumentQuad,\n): CastShadowAnalysis {\n const bounds = quadBounds(quad, canvas.width, canvas.height)\n if (bounds.width <= 0 || bounds.height <= 0) {\n return { strong: false, strength: 0, darkness: 0, areaRatio: 0 }\n }\n const sample = document.createElement('canvas')\n sample.width = 60\n sample.height = 60\n const context = sample.getContext('2d', { willReadFrequently: true })\n if (!context) return { strong: false, strength: 0, darkness: 0, areaRatio: 0 }\n context.drawImage(\n canvas,\n bounds.left,\n bounds.top,\n bounds.width,\n bounds.height,\n 0,\n 0,\n sample.width,\n sample.height,\n )\n const pixels = context.getImageData(0, 0, sample.width, sample.height).data\n const luminance = new Uint8Array(sample.width * sample.height)\n for (let pixel = 0, offset = 0; pixel < luminance.length; pixel += 1, offset += 4) {\n luminance[pixel] = Math.round(\n pixels[offset] * 0.299 +\n pixels[offset + 1] * 0.587 +\n pixels[offset + 2] * 0.114,\n )\n }\n return analyzeCastShadowLuminance(luminance, sample.width, sample.height)\n}\n","import { captureVideoFrame, detectBrowserEngine, type BrowserEngine } from './camera'\nimport { canvasUnavailable, ScannerError } from './errors'\nimport { assertLicense } from './license'\nimport { detectDocument, type ScanOptions } from './scan'\nimport { analyzeCanvasCastShadow } from './lighting'\nimport type {\n DocumentDetectionResult,\n DocumentModelStatus,\n DocumentQuad,\n Point,\n} from './types'\n\nexport type AutoCaptureMode = 'defaultOn' | 'defaultOff' | 'alwaysOn' | 'alwaysOff' | 'only'\nexport type FinderSource = HTMLVideoElement | MediaStream | HTMLCanvasElement\nexport type FinderState = 'seeking' | 'detecting' | 'unstable' | 'ready' | 'captured'\nexport type FinderGuidanceReason =\n | 'place-document'\n | 'move-closer'\n | 'move-farther-away'\n | 'straighten-camera'\n | 'hold-still'\n | 'ready'\n\nexport interface QuadFill {\n /** Raw quad bounds width divided by the analysis frame width. */\n widthFill: number\n /** Raw quad bounds height divided by the analysis frame height. */\n heightFill: number\n /** Fill on the limiting axis: `max(widthFill, heightFill)`. */\n limitingFill: number\n /** Shoelace polygon area divided by the analysis frame area. Diagnostic only. */\n areaFill: number\n /** Long side divided by short side of the raw quad. */\n aspectRatio: number\n}\n\nexport interface FinderGuidance {\n hint: string\n reason: FinderGuidanceReason\n /**\n * Fill on the limiting axis of the raw quad: the larger of its width and\n * height fill. A tall receipt that fills the frame height is fully framed\n * even though its bounding box covers little frame area.\n */\n fillRatio: number\n /** Component fills, polygon area fill, and aspect ratio behind `fillRatio`. */\n fill: QuadFill\n readyForAutoCapture: boolean\n}\n\nexport interface FinderLighting {\n castShadow: boolean\n strength: number\n hint?: string\n}\n\nexport function selectFinderHint(\n guidance: FinderGuidance,\n lighting: FinderLighting,\n) {\n return lighting.castShadow &&\n (guidance.reason === 'hold-still' || guidance.reason === 'ready')\n ? lighting.hint ?? guidance.hint\n : guidance.hint\n}\n\n/** Wall-clock seconds remaining in the auto-capture hold-still. `0` means fire. */\nexport function holdStillSecondsRemaining(\n startedAt: number,\n now: number,\n holdStillMs: number,\n) {\n if (holdStillMs <= 0) return 0\n const remaining = holdStillMs - (now - startedAt)\n if (remaining <= 0) return 0\n return Math.max(1, Math.ceil(remaining / 1000))\n}\n\n/** Chip copy during the hold-still. No 3–2–1 countdown. */\nexport function holdStillCountdownLabel(_seconds?: number) {\n return 'Hold still'\n}\n\nexport interface AutoCaptureControl {\n visible: boolean\n checked: boolean\n mutable: boolean\n}\n\nexport interface FinderStatus {\n state: FinderState\n confidence: number\n quad?: DocumentQuad\n guidance: FinderGuidance\n lighting: FinderLighting\n stableFrames: number\n requiredStableFrames: number\n autoCapture: AutoCaptureControl\n /**\n * Seconds left in the auto-capture hold-still (1 while waiting).\n * Absent when the document is not ready, auto-capture is off, or the\n * hold finished and capture is starting.\n */\n holdStillSeconds?: number\n sourceWidth?: number\n sourceHeight?: number\n detector?: DocumentDetectionResult['detector']\n modelStatus?: DocumentModelStatus\n modelProvider?: DocumentDetectionResult['modelProvider']\n modelError?: string\n modelNote?: string\n inferenceMs?: number\n}\n\nexport interface FinderCapture {\n frame: HTMLCanvasElement\n detection?: DocumentDetectionResult\n reason: 'auto' | 'manual'\n}\n\nexport interface FinderOptions {\n source: FinderSource\n autoCapture: AutoCaptureMode\n /** Supplies the final full-resolution frame while finder analysis stays bounded. */\n captureFrame?: () => HTMLCanvasElement | Promise<HTMLCanvasElement>\n onStatus?: (status: FinderStatus) => void\n onCapture?: (capture: FinderCapture) => void | Promise<void>\n /** Reports capture failures, including automatic captures, instead of swallowing them. */\n onCaptureError?: (error: Error) => void\n /**\n * After the document is ready, wait this long before auto-capture so the\n * end user can settle. Defaults to 1000ms. The overlay says **Hold still**\n * (no 3–2–1). Pass `0` to skip. Manual `capture()` does not wait.\n * Lost or unstable quads reset the timer.\n */\n holdStillMs?: number\n /**\n * Extra gate for the hold-still timer. When this returns false, the\n * document can be ready but the 1s hold does not start (license back\n * waits for a PDF417 quad).\n */\n holdStillReady?: () => boolean\n /** Consecutive stable raw detections required. Defaults to 4. */\n stableFrames?: number\n /** Minimum detector confidence on the normalized 0–1 scale. Defaults to 0.72. */\n confidenceThreshold?: number\n /** Maximum normalized corner movement while entering ready. Defaults to 0.024. */\n cornerTolerance?: number\n /** Movement allowed after ready before dropping out. Defaults to 0.036. */\n exitCornerTolerance?: number\n /**\n * Pause after each detection before the next tick. Defaults to 70ms on iOS\n * and 140ms elsewhere. The 260ms demo value was an Android 4K live-finder\n * accommodation and is no longer applied automatically.\n */\n intervalMs?: number\n /**\n * Maximum analysis-frame long edge. Defaults to 720 on iOS (one working\n * copy) and 1600 elsewhere, then a 520px working image on that non-iOS path.\n */\n maxFrameDimension?: number\n guidance?: FinderGuidanceOptions\n detection?: ScanOptions\n playShutterSound?: boolean\n}\n\nexport interface FinderSession {\n stop: () => void\n capture: () => Promise<FinderCapture>\n setAutoCapture: (enabled: boolean) => boolean\n getStatus: () => FinderStatus\n}\n\nexport async function snapshotBeforeCaptureFeedback<T>(\n snapshot: () => T | Promise<T>,\n feedback: () => void,\n) {\n const captured = await snapshot()\n feedback()\n return captured\n}\n\nexport function autoCaptureControl(mode: AutoCaptureMode): AutoCaptureControl {\n if (mode === 'alwaysOn' || mode === 'only') return { visible: false, checked: true, mutable: false }\n if (mode === 'alwaysOff') return { visible: false, checked: false, mutable: false }\n return {\n visible: true,\n checked: mode === 'defaultOn',\n mutable: true,\n }\n}\n\nfunction cornerList(quad: DocumentQuad): Point[] {\n return [quad.topLeft, quad.topRight, quad.bottomRight, quad.bottomLeft]\n}\n\nexport interface StabilityOptions {\n stableFrames: number\n confidenceThreshold: number\n cornerTolerance: number\n exitCornerTolerance?: number\n}\n\nexport interface FinderGuidanceOptions {\n confidenceThreshold?: number\n /** Limiting-axis fill required to enter ready. Defaults to 0.72. */\n minFillRatio?: number\n /** Limiting-axis fill allowed after ready before exiting. Defaults to 0.64. */\n exitFillRatio?: number\n /** Long-to-short side ratio that makes a document high-aspect. Defaults to 2.2. */\n highAspectRatio?: number\n /** Long-side fill required to enter ready on a high-aspect document. Defaults to 0.8. */\n highAspectFillRatio?: number\n /** Long-side fill allowed after ready on a high-aspect document. Defaults to 0.72. */\n highAspectExitFillRatio?: number\n /** Edge clearance required to enter ready. Defaults to 0.015. */\n edgeMarginRatio?: number\n /** Edge clearance allowed after ready. Defaults to 0.005. */\n exitEdgeMarginRatio?: number\n /** Opposite-edge ratio required to enter ready. Defaults to 0.48. */\n minPerspectiveRatio?: number\n /** Opposite-edge ratio allowed after ready. Defaults to 0.4. */\n exitMinPerspectiveRatio?: number\n}\n\nexport const DEFAULT_HOLD_STILL_MS = 1000\nexport const DEFAULT_FINDER_STABLE_FRAMES = 4\nexport const DEFAULT_FINDER_CONFIDENCE_THRESHOLD = 0.72\nexport const DEFAULT_FINDER_CORNER_TOLERANCE = 0.024\nexport const DEFAULT_FINDER_EXIT_CORNER_TOLERANCE = 0.036\nexport const DEFAULT_FINDER_MIN_FILL_RATIO = 0.72\nexport const DEFAULT_FINDER_EXIT_FILL_RATIO = 0.64\nexport const DEFAULT_FINDER_HIGH_ASPECT_RATIO = 2.2\n/** Extra long-side fill asked of a high-aspect document, above the base target. */\nexport const DEFAULT_FINDER_HIGH_ASPECT_FILL_MARGIN = 0.08\nexport const DEFAULT_FINDER_EDGE_MARGIN_RATIO = 0.015\nexport const DEFAULT_FINDER_EXIT_EDGE_MARGIN_RATIO = 0.005\nexport const DEFAULT_FINDER_MIN_PERSPECTIVE_RATIO = 0.48\nexport const DEFAULT_FINDER_EXIT_MIN_PERSPECTIVE_RATIO = 0.40\nexport const DEFAULT_FINDER_MAX_FRAME_DIMENSION = 1600\nexport const DEFAULT_FINDER_ANALYSIS_DIMENSION = 520\nexport const DEFAULT_FINDER_INTERVAL_MS = 140\n/** iOS live finder: one working copy, not the 1600→520 Android 4K path. */\nexport const IOS_FINDER_MAX_FRAME_DIMENSION = 720\nexport const IOS_FINDER_ANALYSIS_DIMENSION = 720\nexport const IOS_FINDER_INTERVAL_MS = 70\n\nexport function liveFinderDefaults(engine: BrowserEngine = detectBrowserEngine()) {\n if (engine.iosWebKit) {\n return {\n intervalMs: IOS_FINDER_INTERVAL_MS,\n maxFrameDimension: IOS_FINDER_MAX_FRAME_DIMENSION,\n maxAnalysisDimension: IOS_FINDER_ANALYSIS_DIMENSION,\n }\n }\n return {\n intervalMs: DEFAULT_FINDER_INTERVAL_MS,\n maxFrameDimension: DEFAULT_FINDER_MAX_FRAME_DIMENSION,\n maxAnalysisDimension: DEFAULT_FINDER_ANALYSIS_DIMENSION,\n }\n}\n\nexport interface FinderDetectionLog {\n level: 'info' | 'warn'\n /** Stable identity used to throttle repeated samples of the same failure. */\n key: string\n summary: string\n detail: Record<string, string | number>\n}\n\n/** One console record for a finder sample. Failures are warnings; a found page is info. */\nexport function finderDetectionLog(\n detection: Pick<\n DocumentDetectionResult,\n | 'status'\n | 'message'\n | 'confidence'\n | 'modelStatus'\n | 'modelError'\n | 'modelProvider'\n | 'modelNote'\n | 'inferenceMs'\n | 'sourceWidth'\n | 'sourceHeight'\n >,\n frame?: { width: number; height: number },\n): FinderDetectionLog {\n const found = detection.status === 'success' && detection.modelStatus !== 'failed'\n const detail: Record<string, string | number> = {\n status: detection.status,\n confidence: Number(detection.confidence.toFixed(3)),\n source: `${detection.sourceWidth}×${detection.sourceHeight}`,\n }\n if (frame?.width && frame?.height) detail.video = `${frame.width}×${frame.height}`\n if (detection.modelStatus) detail.modelStatus = detection.modelStatus\n if (detection.modelProvider) detail.provider = detection.modelProvider\n if (detection.modelError) detail.modelError = detection.modelError\n if (detection.modelNote) detail.modelNote = detection.modelNote\n if (detection.inferenceMs !== undefined) detail.inferenceMs = Math.round(detection.inferenceMs)\n if (!found && detection.message) detail.message = detection.message\n const summary = found\n ? 'document found'\n : detection.modelStatus === 'failed'\n ? 'document not found (ML failed)'\n : `document not found (${detection.status})`\n return {\n level: found ? 'info' : 'warn',\n key: [\n summary,\n detail.modelStatus ?? '',\n detail.modelError ?? '',\n detail.provider ?? '',\n detail.modelNote ?? '',\n detail.source,\n detail.video ?? '',\n ].join('|'),\n summary,\n detail,\n }\n}\n\nfunction polygonArea(points: Point[]) {\n let area = 0\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index]\n const next = points[(index + 1) % points.length]\n area += current.x * next.y - next.x * current.y\n }\n return Math.abs(area) / 2\n}\n\nfunction distance(first: Point, second: Point) {\n return Math.hypot(first.x - second.x, first.y - second.y)\n}\n\nconst EMPTY_FILL: QuadFill = {\n widthFill: 0,\n heightFill: 0,\n limitingFill: 0,\n areaFill: 0,\n aspectRatio: 1,\n}\n\n/**\n * Measures how much of each frame axis the raw quad occupies. A document can\n * only grow until its limiting axis reaches a frame edge, so the larger of the\n * two axis fills — not the bounding-box area — describes how well it is framed.\n */\nexport function measureQuadFill(\n quad: DocumentQuad,\n frameWidth: number,\n frameHeight: number,\n): QuadFill {\n const points = cornerList(quad)\n const xs = points.map(({ x }) => x)\n const ys = points.map(({ y }) => y)\n const widthFill = (Math.max(...xs) - Math.min(...xs)) / Math.max(1, frameWidth)\n const heightFill = (Math.max(...ys) - Math.min(...ys)) / Math.max(1, frameHeight)\n const [topLeft, topRight, bottomRight, bottomLeft] = points\n const horizontal = Math.max(\n distance(topLeft, topRight),\n distance(bottomLeft, bottomRight),\n )\n const vertical = Math.max(\n distance(topLeft, bottomLeft),\n distance(topRight, bottomRight),\n )\n return {\n widthFill,\n heightFill,\n limitingFill: Math.max(widthFill, heightFill),\n areaFill: polygonArea(points) / Math.max(1, frameWidth * frameHeight),\n aspectRatio: Math.max(horizontal, vertical) /\n Math.max(1, Math.min(horizontal, vertical)),\n }\n}\n\n/**\n * Fill the limiting axis must reach. High-aspect documents are asked for a\n * little more of the long side, because their short side can never fill and\n * the long side is the only axis carrying resolution. The target never exceeds\n * what the edge margin physically allows.\n */\nexport function limitingFillTarget(\n fill: QuadFill,\n previouslyReady: boolean,\n edgeMarginRatio: number,\n options: FinderGuidanceOptions = {},\n) {\n const base = previouslyReady\n ? options.exitFillRatio ?? DEFAULT_FINDER_EXIT_FILL_RATIO\n : options.minFillRatio ?? DEFAULT_FINDER_MIN_FILL_RATIO\n const highAspectRatio = options.highAspectRatio ?? DEFAULT_FINDER_HIGH_ASPECT_RATIO\n if (fill.aspectRatio < highAspectRatio) return base\n const configured = previouslyReady\n ? options.highAspectExitFillRatio\n : options.highAspectFillRatio\n return Math.min(\n configured ?? base + DEFAULT_FINDER_HIGH_ASPECT_FILL_MARGIN,\n 1 - edgeMarginRatio * 2,\n )\n}\n\nexport function selectFinderGuidance(\n detection: DocumentDetectionResult,\n stability: { ready: boolean; previouslyReady?: boolean },\n options: FinderGuidanceOptions = {},\n): FinderGuidance {\n const confidenceThreshold =\n options.confidenceThreshold ?? DEFAULT_FINDER_CONFIDENCE_THRESHOLD\n if (\n detection.status !== 'success' ||\n !detection.quad ||\n detection.confidence < confidenceThreshold ||\n !detection.sourceWidth ||\n !detection.sourceHeight\n ) {\n return {\n hint: 'Place the document in the frame',\n reason: 'place-document',\n fillRatio: 0,\n fill: EMPTY_FILL,\n readyForAutoCapture: false,\n }\n }\n\n const points = cornerList(detection.quad)\n const fill = measureQuadFill(\n detection.quad,\n detection.sourceWidth,\n detection.sourceHeight,\n )\n const fillRatio = fill.limitingFill\n const edgeMarginRatio = stability.previouslyReady\n ? options.exitEdgeMarginRatio ?? DEFAULT_FINDER_EXIT_EDGE_MARGIN_RATIO\n : options.edgeMarginRatio ?? DEFAULT_FINDER_EDGE_MARGIN_RATIO\n const marginX = detection.sourceWidth * edgeMarginRatio\n const marginY = detection.sourceHeight * edgeMarginRatio\n const clippingRisk = points.some(({ x, y }) =>\n x <= marginX ||\n x >= detection.sourceWidth - marginX ||\n y <= marginY ||\n y >= detection.sourceHeight - marginY,\n )\n if (clippingRisk) {\n return {\n hint: 'Move farther away',\n reason: 'move-farther-away',\n fillRatio,\n fill,\n readyForAutoCapture: false,\n }\n }\n\n const [topLeft, topRight, bottomRight, bottomLeft] = points\n const top = distance(topLeft, topRight)\n const right = distance(topRight, bottomRight)\n const bottom = distance(bottomRight, bottomLeft)\n const left = distance(bottomLeft, topLeft)\n const oppositeRatio = Math.min(\n Math.min(top, bottom) / Math.max(1, Math.max(top, bottom)),\n Math.min(left, right) / Math.max(1, Math.max(left, right)),\n )\n const minPerspectiveRatio = stability.previouslyReady\n ? options.exitMinPerspectiveRatio ?? DEFAULT_FINDER_EXIT_MIN_PERSPECTIVE_RATIO\n : options.minPerspectiveRatio ?? DEFAULT_FINDER_MIN_PERSPECTIVE_RATIO\n if (oppositeRatio < minPerspectiveRatio) {\n return {\n hint: 'Hold the camera straight',\n reason: 'straighten-camera',\n fillRatio,\n fill,\n readyForAutoCapture: false,\n }\n }\n\n // Both axes have slack only when the limiting one does, so a skinny document\n // that already fills its long axis is never told to move closer.\n const fillTarget = limitingFillTarget(\n fill,\n stability.previouslyReady === true,\n edgeMarginRatio,\n options,\n )\n if (fillRatio < fillTarget) {\n return {\n hint: 'Move closer',\n reason: 'move-closer',\n fillRatio,\n fill,\n readyForAutoCapture: false,\n }\n }\n\n return {\n hint: 'Hold still',\n reason: stability.ready ? 'ready' : 'hold-still',\n fillRatio,\n fill,\n readyForAutoCapture: stability.ready,\n }\n}\n\nexport class QuadStabilityTracker {\n private previous?: DocumentQuad\n private count = 0\n private ready = false\n private readonly options: StabilityOptions\n\n constructor(options: StabilityOptions) {\n this.options = options\n }\n\n reset() {\n this.previous = undefined\n this.count = 0\n this.ready = false\n }\n\n update(detection: DocumentDetectionResult) {\n if (\n detection.status !== 'success' ||\n !detection.quad ||\n detection.confidence < this.options.confidenceThreshold\n ) {\n this.reset()\n return { stableFrames: 0, ready: false, movement: Infinity }\n }\n const diagonal = Math.hypot(detection.sourceWidth, detection.sourceHeight)\n const movement = this.previous\n ? Math.max(...cornerList(detection.quad).map((point, index) => {\n const previous = cornerList(this.previous!)[index]\n return Math.hypot(point.x - previous.x, point.y - previous.y) / Math.max(1, diagonal)\n }))\n : Infinity\n const exitTolerance = this.options.exitCornerTolerance ??\n Math.max(this.options.cornerTolerance, DEFAULT_FINDER_EXIT_CORNER_TOLERANCE)\n if (this.ready && this.previous && movement <= exitTolerance) {\n this.count = Math.max(this.options.stableFrames, this.count + 1)\n } else {\n this.ready = false\n this.count = this.previous && movement <= this.options.cornerTolerance\n ? this.count + 1\n : 1\n if (this.count >= this.options.stableFrames) this.ready = true\n }\n this.previous = detection.quad\n return {\n stableFrames: this.count,\n ready: this.ready,\n movement,\n }\n }\n}\n\nfunction cloneCanvas(source: HTMLCanvasElement, maxDimension: number) {\n const scale = Math.min(1, maxDimension / Math.max(source.width, source.height))\n const output = document.createElement('canvas')\n output.width = Math.max(1, Math.round(source.width * scale))\n output.height = Math.max(1, Math.round(source.height * scale))\n const context = output.getContext('2d')\n if (!context) throw canvasUnavailable()\n context.drawImage(source, 0, 0, output.width, output.height)\n return output\n}\n\nfunction playShutter() {\n const AudioContextClass = window.AudioContext ??\n (window as typeof window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext\n if (!AudioContextClass) return\n const context = new AudioContextClass()\n const length = Math.round(context.sampleRate * 0.09)\n const buffer = context.createBuffer(1, length, context.sampleRate)\n const channel = buffer.getChannelData(0)\n for (let index = 0; index < length; index += 1) {\n const envelope = Math.exp(-index / (context.sampleRate * 0.018))\n channel[index] = (Math.random() * 2 - 1) * envelope\n }\n const source = context.createBufferSource()\n const filter = context.createBiquadFilter()\n const gain = context.createGain()\n filter.type = 'bandpass'\n filter.frequency.value = 1400\n gain.gain.value = 0.42\n source.buffer = buffer\n source.connect(filter).connect(gain).connect(context.destination)\n source.start()\n source.onended = () => void context.close()\n}\n\nasync function videoForStream(stream: MediaStream) {\n const video = document.createElement('video')\n video.muted = true\n video.autoplay = true\n video.playsInline = true\n video.srcObject = stream\n await video.play()\n return video\n}\n\n/**\n * Watches a camera or canvas for a stable document.\n * Detection quality is reported on `FinderStatus`, not thrown.\n * Worker startup and DocAligner HTTP failures are caught on each tick and\n * reported as `modelStatus: 'failed'` with `modelError`. The camera stays up.\n *\n * @throws {LicenseError} LicenseError when the scan feature is not licensed. The finder does not start.\n */\nexport async function startFinder(options: FinderOptions): Promise<FinderSession> {\n await assertLicense('scan')\n const control = autoCaptureControl(options.autoCapture)\n const holdStillMs = options.holdStillMs ?? DEFAULT_HOLD_STILL_MS\n const tracker = new QuadStabilityTracker({\n stableFrames: options.stableFrames ?? DEFAULT_FINDER_STABLE_FRAMES,\n confidenceThreshold:\n options.confidenceThreshold ?? DEFAULT_FINDER_CONFIDENCE_THRESHOLD,\n cornerTolerance: options.cornerTolerance ?? DEFAULT_FINDER_CORNER_TOLERANCE,\n exitCornerTolerance:\n options.exitCornerTolerance ?? DEFAULT_FINDER_EXIT_CORNER_TOLERANCE,\n })\n const defaults = liveFinderDefaults()\n const maxDimension = options.maxFrameDimension ?? defaults.maxFrameDimension\n const interval = options.intervalMs ?? defaults.intervalMs\n const detectionOptions: ScanOptions = {\n minDimension: 220,\n maxAnalysisDimension: defaults.maxAnalysisDimension,\n ...options.detection,\n }\n const source: HTMLVideoElement | HTMLCanvasElement =\n typeof MediaStream !== 'undefined' && options.source instanceof MediaStream\n ? await videoForStream(options.source)\n : options.source as HTMLVideoElement | HTMLCanvasElement\n let running = true\n let captureStarted = false\n let guidanceReady = false\n let holdStillStartedAt: number | undefined\n let timer = 0\n let latestDetection: DocumentDetectionResult | undefined\n let lastDetectionLogKey = ''\n let lastDetectionLogAt = 0\n let status: FinderStatus = {\n state: 'detecting',\n confidence: 0,\n guidance: {\n hint: 'Place the document in the frame',\n reason: 'place-document',\n fillRatio: 0,\n fill: EMPTY_FILL,\n readyForAutoCapture: false,\n },\n lighting: { castShadow: false, strength: 0 },\n stableFrames: 0,\n requiredStableFrames: options.stableFrames ?? DEFAULT_FINDER_STABLE_FRAMES,\n autoCapture: { ...control },\n }\n\n const frame = () => {\n if (source instanceof HTMLCanvasElement) return cloneCanvas(source, maxDimension)\n return captureVideoFrame(source, maxDimension)\n }\n const emit = (next: FinderStatus) => {\n status = next\n options.onStatus?.(next)\n }\n const logDetection = (detection: DocumentDetectionResult) => {\n const frame = source instanceof HTMLVideoElement\n ? { width: source.videoWidth, height: source.videoHeight }\n : { width: source.width, height: source.height }\n const entry = finderDetectionLog(detection, frame)\n const now = performance.now()\n if (entry.key === lastDetectionLogKey && now - lastDetectionLogAt < 2000) return\n lastDetectionLogKey = entry.key\n lastDetectionLogAt = now\n console[entry.level](`[exige-scanner] ${entry.summary}`, entry.detail)\n }\n const stop = () => {\n running = false\n window.clearTimeout(timer)\n if (source instanceof HTMLVideoElement && source !== options.source) source.srcObject = null\n }\n const capture = async (reason: FinderCapture['reason'] = 'manual') => {\n if (captureStarted) throw new Error('A finder capture is already in progress.')\n captureStarted = true\n let capturedFrame: HTMLCanvasElement\n try {\n capturedFrame = await snapshotBeforeCaptureFeedback(\n options.captureFrame ?? frame,\n () => {\n emit({ ...status, state: 'captured' })\n if (options.playShutterSound !== false) playShutter()\n stop()\n },\n )\n } catch (error) {\n // Leave the finder usable so the end user can retake after a blank frame.\n captureStarted = false\n throw error instanceof Error ? error : new Error('The camera frame could not be captured.')\n }\n const result: FinderCapture = { frame: capturedFrame, detection: latestDetection, reason }\n await options.onCapture?.(result)\n return result\n }\n const tick = async () => {\n if (!running || captureStarted) return\n try {\n const current = frame()\n const detection = await detectDocument(current, detectionOptions)\n latestDetection = detection\n logDetection(detection)\n const shadow = detection.quad\n ? analyzeCanvasCastShadow(current, detection.quad)\n : { strong: false, strength: 0 }\n const lighting: FinderLighting = shadow.strong\n ? {\n castShadow: true,\n strength: shadow.strength,\n hint: 'Tilt to move the shadow off the page',\n }\n : { castShadow: false, strength: shadow.strength }\n const stability = tracker.update(detection)\n const guidance = selectFinderGuidance(detection, {\n ...stability,\n previouslyReady: guidanceReady,\n }, {\n confidenceThreshold:\n options.confidenceThreshold ?? DEFAULT_FINDER_CONFIDENCE_THRESHOLD,\n ...options.guidance,\n })\n guidanceReady = guidance.readyForAutoCapture\n const now = performance.now()\n const holdStillAllowed = options.holdStillReady?.() ?? true\n if (guidance.readyForAutoCapture && control.checked && holdStillAllowed) {\n holdStillStartedAt ??= now\n } else {\n holdStillStartedAt = undefined\n }\n const remaining = holdStillStartedAt === undefined\n ? undefined\n : holdStillSecondsRemaining(holdStillStartedAt, now, holdStillMs)\n const nextState: FinderState =\n detection.status !== 'success' || guidance.reason === 'place-document'\n ? 'seeking'\n : guidance.readyForAutoCapture\n ? 'ready'\n : 'unstable'\n emit({\n state: nextState,\n confidence: detection.confidence,\n quad: detection.quad,\n guidance,\n lighting,\n stableFrames: stability.stableFrames,\n requiredStableFrames: status.requiredStableFrames,\n autoCapture: { ...control },\n holdStillSeconds: remaining && remaining > 0 ? remaining : undefined,\n sourceWidth: detection.sourceWidth,\n sourceHeight: detection.sourceHeight,\n detector: detection.detector,\n modelStatus: detection.modelStatus,\n modelProvider: detection.modelProvider,\n modelError: detection.modelError,\n modelNote: detection.modelNote,\n inferenceMs: detection.inferenceMs,\n })\n if (guidance.readyForAutoCapture && control.checked && remaining === 0) {\n try {\n await capture('auto')\n return\n } catch (error) {\n tracker.reset()\n guidanceReady = false\n holdStillStartedAt = undefined\n options.onCaptureError?.(\n error instanceof Error ? error : new Error('Automatic capture failed.'),\n )\n }\n }\n } catch (error) {\n tracker.reset()\n guidanceReady = false\n holdStillStartedAt = undefined\n const failure = error instanceof ScannerError\n ? {\n detector: 'ml' as const,\n modelStatus: 'failed' as const,\n modelError: error.message,\n }\n : {\n modelError: error instanceof Error ? error.message : 'Finder tick failed.',\n }\n logDetection({\n status: 'no-document',\n confidence: 0,\n message: failure.modelError ?? 'Finder tick failed.',\n sourceWidth: 0,\n sourceHeight: 0,\n ...failure,\n })\n emit({\n state: 'seeking',\n confidence: 0,\n guidance: {\n hint: 'Place the document in the frame',\n reason: 'place-document',\n fillRatio: 0,\n fill: EMPTY_FILL,\n readyForAutoCapture: false,\n },\n lighting: { castShadow: false, strength: 0 },\n stableFrames: 0,\n requiredStableFrames: status.requiredStableFrames,\n autoCapture: { ...control },\n ...failure,\n })\n }\n if (running) timer = window.setTimeout(() => void tick(), interval)\n }\n\n options.onStatus?.(status)\n void tick()\n return {\n stop,\n capture: () => capture('manual'),\n setAutoCapture(enabled) {\n if (!control.mutable || captureStarted) return false\n control.checked = enabled\n if (!enabled) holdStillStartedAt = undefined\n emit({\n ...status,\n autoCapture: { ...control },\n holdStillSeconds: enabled ? status.holdStillSeconds : undefined,\n })\n return true\n },\n getStatus: () => status,\n }\n}\n","import { ScannerError } from './errors'\n\n/**\n * Browser files copied from tesseract.js 7, tesseract.js-core 7.0.0 LSTM builds,\n * and @tesseract.js-data/eng 4.0.0_best_int. Literal `new URL` keeps them visible\n * to Webpack and Turbopack.\n */\nexport const tesseractWorkerUrl = new URL("./assets/tesseract/worker.min.js", import.meta.url)\nexport const tesseractCoreLstmUrl = new URL("./assets/tesseract/tesseract-core-lstm.wasm.js", import.meta.url)\nexport const tesseractCoreSimdLstmUrl = new URL("./assets/tesseract/tesseract-core-simd-lstm.wasm.js", import.meta.url)\nexport const tesseractCoreRelaxedSimdLstmUrl = new URL(\n './runtime/tesseract-core-relaxedsimd-lstm.wasm.js',\n import.meta.url,\n)\nexport const bundledEnglishTraineddataUrl = new URL("./assets/tesseract/eng.traineddata.gz", import.meta.url)\n\nconst RELAXED_SIMD = Uint8Array.of(\n 0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 15, 1, 13, 0,\n 65, 1, 253, 15, 65, 2, 253, 15, 253, 128, 2, 11,\n)\nconst SIMD = Uint8Array.of(\n 0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, 1, 8, 0,\n 65, 0, 253, 15, 253, 98, 11,\n)\n\nexport interface LstmCoreSupport {\n relaxedSimd: boolean\n simd: boolean\n}\n\nfunction wasmValid(bytes: Uint8Array) {\n try {\n return typeof WebAssembly !== 'undefined' &&\n WebAssembly.validate(bytes as unknown as BufferSource)\n } catch {\n return false\n }\n}\n\n/** Same relaxed-SIMD then SIMD order as tesseract.js `getCore` for an LSTM core. */\nexport function detectLstmCoreSupport(): LstmCoreSupport {\n return {\n relaxedSimd: wasmValid(RELAXED_SIMD),\n simd: wasmValid(SIMD),\n }\n}\n\nexport function selectLstmCoreUrl(support: LstmCoreSupport) {\n if (support.relaxedSimd) return tesseractCoreRelaxedSimdLstmUrl\n if (support.simd) return tesseractCoreSimdLstmUrl\n return tesseractCoreLstmUrl\n}\n\nexport type OcrLanguagePlan =\n | { kind: 'bundled-eng'; language: 'eng' }\n | { kind: 'lang-path'; language: string; langPath: string }\n\nexport function resolveOcrLanguage(language?: string, langPath?: string): OcrLanguagePlan {\n const resolved = language?.trim() || 'eng'\n const override = langPath?.trim() || ''\n if (override) return { kind: 'lang-path', language: resolved, langPath: override }\n if (resolved === 'eng') return { kind: 'bundled-eng', language: 'eng' }\n throw new ScannerError(\n 'language',\n `OCR language \"${resolved}\" is not packaged. Pass langPath, a directory URL that contains ${resolved}.traineddata.gz.`,\n )\n}\n\n/** Directory that contains `eng.traineddata.gz` next to the packaged worker and cores. */\nexport function bundledEnglishLangPath(traineddataUrl = bundledEnglishTraineddataUrl) {\n const href = traineddataUrl.href\n const slash = href.lastIndexOf('/')\n return slash === -1 ? href : href.slice(0, slash)\n}\n\nexport function tesseractLanguagePath(plan: OcrLanguagePlan) {\n return plan.kind === 'bundled-eng' ? bundledEnglishLangPath() : plan.langPath\n}\n\nexport async function loadBundledEnglishTraineddata() {\n const response = await fetch(bundledEnglishTraineddataUrl)\n if (!response.ok) {\n throw new ScannerError(\n 'model-http',\n `Packaged English OCR data failed to load (HTTP ${response.status}).`,\n )\n }\n return new Uint8Array(await response.arrayBuffer())\n}\n\n/**\n * tesseract.js 7 `createWorker` swallows initialize failures (`.catch(() => {})`)\n * unless `errorHandler` rejects. Passing `{ code, data }` is also unsafe: initialize\n * uses `Lang.data` as the language name, so a Uint8Array becomes\n * `\"24,0,0,0,…\"`. Always pass a language string and `langPath`.\n */\nexport async function createTesseractWorker<T>(\n createWorker: (\n langs: string,\n oem: undefined,\n options: {\n workerPath: string\n corePath: string\n langPath: string\n gzip: boolean\n logger?: (message: { progress?: number; status?: string }) => void\n errorHandler?: (error: unknown) => void\n },\n ) => Promise<T>,\n language: string,\n options: {\n workerPath: string\n corePath: string\n langPath: string\n logger?: (message: { progress?: number; status?: string }) => void\n },\n): Promise<T> {\n return new Promise((resolve, reject) => {\n let settled = false\n const fail = (error: unknown) => {\n if (settled) return\n settled = true\n const message = error instanceof Error ? error.message : String(error)\n reject(new ScannerError('worker', `OCR worker failed to start (${message}).`))\n }\n void createWorker(language, undefined, {\n workerPath: options.workerPath,\n corePath: options.corePath,\n langPath: options.langPath,\n gzip: true,\n logger: options.logger,\n errorHandler: fail,\n }).then((worker) => {\n if (settled) {\n const close = worker as { terminate?: () => unknown }\n void close.terminate?.()\n return\n }\n settled = true\n resolve(worker)\n }, fail)\n })\n}\n","import { canvasUnavailable } from './errors'\nimport { sourceToCanvas } from './image'\nimport { assertLicense } from './license'\nimport {\n createTesseractWorker,\n detectLstmCoreSupport,\n resolveOcrLanguage,\n selectLstmCoreUrl,\n tesseractLanguagePath,\n tesseractWorkerUrl,\n} from './ocr-runtime'\nimport {\n adaptiveThresholdLuminance,\n analyzeCastShadowLuminance,\n flattenIlluminationLuminance,\n type CastShadowAnalysis,\n type CastShadowRegion,\n} from './lighting'\nimport type { ImageSource, OcrBox, OcrResult } from './types'\n\nexport interface OcrOptions {\n /**\n * Tesseract language code. Defaults to `eng`, which loads the packaged\n * English LSTM traineddata. Any other code needs `langPath`.\n */\n language?: string\n /**\n * Directory URL that contains `${language}.traineddata.gz`.\n * Required when `language` is not `eng`. For `eng`, set this only to\n * replace the packaged English model.\n */\n langPath?: string\n /**\n * Minimum normalized confidence accepted for OCR tokens, from 0 to 1.\n * Defaults to 0.5. Words and characters are filtered independently.\n * A value outside 0–1 throws `RangeError`.\n */\n minConfidence?: number\n /**\n * Re-reads one phone/hand cast-shadow tile after full-page\n * illumination flattening when that region's lighting or token\n * quality is poor. Defaults to true. Does not use accepted-word\n * count as the skip condition.\n */\n shadowPass?: boolean\n /**\n * Include word boxes on the result. Defaults to false.\n * Redaction that matches words needs `true`.\n */\n includeWords?: boolean\n /**\n * Include character boxes on the result. Defaults to false.\n */\n includeCharacters?: boolean\n onProgress?: (progress: number, status: string) => void\n}\n\ninterface TesseractBbox {\n x0: number\n y0: number\n x1: number\n y1: number\n}\n\ninterface TesseractSymbol {\n text: string\n confidence: number\n bbox: TesseractBbox\n}\n\ninterface TesseractWord {\n text: string\n confidence: number\n bbox: TesseractBbox\n symbols: TesseractSymbol[]\n}\n\ninterface TesseractBlock {\n paragraphs: Array<{\n lines: Array<{\n words: TesseractWord[]\n }>\n }>\n}\n\nexport interface OcrPass {\n blocks: TesseractBlock[] | null\n /** Maps pass coordinates back into the original input image. */\n scaleX?: number\n scaleY?: number\n offsetX?: number\n offsetY?: number\n}\n\nfunction toBox(\n item: { text: string; confidence: number; bbox: TesseractBbox },\n scaleX = 1,\n scaleY = 1,\n offsetX = 0,\n offsetY = 0,\n): OcrBox {\n return {\n text: item.text,\n confidence: Math.max(0, Math.min(1, item.confidence / 100)),\n bbox: {\n x: item.bbox.x0 * scaleX + offsetX,\n y: item.bbox.y0 * scaleY + offsetY,\n width: Math.max(0, item.bbox.x1 - item.bbox.x0) * scaleX,\n height: Math.max(0, item.bbox.y1 - item.bbox.y0) * scaleY,\n },\n }\n}\n\nfunction resolveMinConfidence(value = 0.5) {\n if (!Number.isFinite(value) || value < 0 || value > 1) {\n throw new RangeError('OCR minConfidence must be between 0 and 1.')\n }\n return value\n}\n\nfunction overlapOverSmaller(first: OcrBox, second: OcrBox) {\n const left = Math.max(first.bbox.x, second.bbox.x)\n const top = Math.max(first.bbox.y, second.bbox.y)\n const right = Math.min(\n first.bbox.x + first.bbox.width,\n second.bbox.x + second.bbox.width,\n )\n const bottom = Math.min(\n first.bbox.y + first.bbox.height,\n second.bbox.y + second.bbox.height,\n )\n const intersection = Math.max(0, right - left) * Math.max(0, bottom - top)\n const smaller = Math.min(\n first.bbox.width * first.bbox.height,\n second.bbox.width * second.bbox.height,\n )\n return smaller > 0 ? intersection / smaller : 0\n}\n\nfunction dedupeBoxes(boxes: OcrBox[]) {\n const ranked = [...boxes].sort((first, second) =>\n second.confidence - first.confidence ||\n first.bbox.y - second.bbox.y ||\n first.bbox.x - second.bbox.x ||\n first.text.localeCompare(second.text),\n )\n const retained: OcrBox[] = []\n for (const candidate of ranked) {\n if (!retained.some((existing) => overlapOverSmaller(existing, candidate) >= 0.65)) {\n retained.push(candidate)\n }\n }\n return retained\n}\n\nfunction isLikelyText(\n item: OcrBox,\n kind: 'word' | 'character',\n imageWidth: number,\n imageHeight: number,\n inkDensity?: (box: OcrBox['bbox']) => number,\n) {\n const text = item.text.trim()\n if (!text || !/[\\p{L}\\p{N}]/u.test(text)) return false\n const area = item.bbox.width * item.bbox.height\n const areaRatio = area / Math.max(1, imageWidth * imageHeight)\n const aspect = item.bbox.width / Math.max(1, item.bbox.height)\n const minimumArea = kind === 'word' ? 0.000015 : 0.0000015\n const maximumAspect = kind === 'word' ? 50 : 8\n if (areaRatio < minimumArea || aspect < 0.05 || aspect > maximumAspect) return false\n if (inkDensity) {\n const density = inkDensity(item.bbox)\n if (density < 0.01 || density > 0.90) return false\n }\n return true\n}\n\nfunction rebuildText(words: OcrBox[]) {\n const sorted = [...words].sort((first, second) =>\n first.bbox.y + first.bbox.height / 2 - (second.bbox.y + second.bbox.height / 2) ||\n first.bbox.x - second.bbox.x,\n )\n const lines: Array<{ centerY: number; height: number; words: OcrBox[] }> = []\n for (const word of sorted) {\n const centerY = word.bbox.y + word.bbox.height / 2\n const line = lines.find((candidate) =>\n Math.abs(candidate.centerY - centerY) <=\n Math.max(candidate.height, word.bbox.height) * 0.6,\n )\n if (line) {\n line.words.push(word)\n const count = line.words.length\n line.centerY = (line.centerY * (count - 1) + centerY) / count\n line.height = Math.max(line.height, word.bbox.height)\n } else {\n lines.push({ centerY, height: word.bbox.height, words: [word] })\n }\n }\n return lines\n .sort((first, second) => first.centerY - second.centerY)\n .map((line) => line.words\n .sort((first, second) => first.bbox.x - second.bbox.x)\n .map((word) => word.text.trim())\n .join(' '))\n .join('\\n')\n}\n\nexport function mergeOcrPasses(\n passes: OcrPass[],\n options: Pick<OcrOptions, 'minConfidence' | 'includeWords' | 'includeCharacters'>,\n imageSize?: { width: number; height: number },\n inkDensity?: (box: OcrBox['bbox']) => number,\n) {\n const minConfidence = resolveMinConfidence(options.minConfidence)\n const allWords: OcrBox[] = []\n const allCharacters: OcrBox[] = []\n let inferredWidth = 1\n let inferredHeight = 1\n\n for (const pass of passes) {\n const scaleX = pass.scaleX ?? 1\n const scaleY = pass.scaleY ?? 1\n const offsetX = pass.offsetX ?? 0\n const offsetY = pass.offsetY ?? 0\n for (const block of pass.blocks ?? []) {\n for (const paragraph of block.paragraphs) {\n for (const line of paragraph.lines) {\n for (const word of line.words) {\n const wordBox = toBox(word, scaleX, scaleY, offsetX, offsetY)\n inferredWidth = Math.max(inferredWidth, wordBox.bbox.x + wordBox.bbox.width)\n inferredHeight = Math.max(inferredHeight, wordBox.bbox.y + wordBox.bbox.height)\n if (wordBox.confidence >= minConfidence) allWords.push(wordBox)\n for (const symbol of word.symbols) {\n const character = toBox(symbol, scaleX, scaleY, offsetX, offsetY)\n if (character.confidence >= minConfidence) allCharacters.push(character)\n }\n }\n }\n }\n }\n }\n\n const width = imageSize?.width ?? inferredWidth\n const height = imageSize?.height ?? inferredHeight\n const words = dedupeBoxes(allWords)\n .filter((word) => isLikelyText(word, 'word', width, height, inkDensity))\n const characters = dedupeBoxes(allCharacters)\n .filter((character) => isLikelyText(character, 'character', width, height, inkDensity))\n const text = rebuildText(words)\n\n return {\n text,\n confidence: words.length\n ? words.reduce((total, word) => total + word.confidence, 0) / words.length\n : 0,\n words: options.includeWords ? words : [],\n characters: options.includeCharacters ? characters : [],\n }\n}\n\nexport function extractOcrLayout(\n blocks: TesseractBlock[] | null,\n options: Pick<OcrOptions, 'minConfidence' | 'includeWords' | 'includeCharacters'>,\n) {\n return mergeOcrPasses([{ blocks }], options)\n}\n\nexport function extractOcrBoxes(\n blocks: TesseractBlock[] | null,\n options: Pick<OcrOptions, 'minConfidence' | 'includeWords' | 'includeCharacters'>,\n) {\n const { words, characters } = extractOcrLayout(blocks, options)\n return { words, characters }\n}\n\nfunction canvasLuminance(canvas: HTMLCanvasElement) {\n const context = canvas.getContext('2d', { willReadFrequently: true })\n if (!context) throw canvasUnavailable()\n const image = context.getImageData(0, 0, canvas.width, canvas.height)\n const luminance = new Uint8Array(canvas.width * canvas.height)\n for (let pixel = 0, offset = 0; pixel < luminance.length; pixel += 1, offset += 4) {\n luminance[pixel] = Math.round(\n image.data[offset] * 0.299 +\n image.data[offset + 1] * 0.587 +\n image.data[offset + 2] * 0.114,\n )\n }\n return { context, image, luminance }\n}\n\nfunction sampleCastShadow(source: HTMLCanvasElement) {\n const sample = document.createElement('canvas')\n sample.width = 72\n sample.height = 72\n const context = sample.getContext('2d', { willReadFrequently: true })\n if (!context) return { strong: false, strength: 0, darkness: 0, areaRatio: 0 }\n context.drawImage(source, 0, 0, sample.width, sample.height)\n const { luminance } = canvasLuminance(sample)\n return analyzeCastShadowLuminance(luminance, sample.width, sample.height)\n}\n\nfunction wordCenterInRegion(\n word: OcrBox,\n region: CastShadowRegion,\n width: number,\n height: number,\n) {\n const x = (word.bbox.x + word.bbox.width / 2) / width\n const y = (word.bbox.y + word.bbox.height / 2) / height\n return (\n x >= region.x &&\n x <= region.x + region.width &&\n y >= region.y &&\n y <= region.y + region.height\n )\n}\n\nconst SHADOW_REGION_QUALITY_MIN = 0.58\n\nexport function shadowTokenQuality(text: string) {\n const token = text.trim()\n if (!token || !/[\\p{L}\\p{N}]/u.test(token)) return 0\n if (/^\\$?\\d{1,5}([.,]\\d{2})?$/.test(token)) return 1\n const letters = token.replace(/[^\\p{L}]/gu, '')\n if (letters.length >= 3 && /[aeiouy]/i.test(letters)) return 0.9\n if (letters.length >= 4) return 0.7\n if (token.length <= 2) return 0.35\n if (/[^\\p{L}\\p{N}.,'’$/-]/u.test(token)) return 0.25\n return 0.5\n}\n\nexport function shadowRegionQuality(\n analysis: CastShadowAnalysis,\n words: OcrBox[],\n inkDensity?: (box: OcrBox['bbox']) => number,\n) {\n const lighting = Math.max(\n 0,\n Math.min(1, (1 - analysis.darkness / 80) * (1 - analysis.strength / 24)),\n )\n if (!words.length) return lighting * 0.35\n let total = 0\n for (const word of words) {\n let score = shadowTokenQuality(word.text)\n if (inkDensity) {\n const ink = inkDensity(word.bbox)\n if (ink < 0.04 || ink > 0.62) score *= 0.4\n }\n total += score\n }\n // Lighting dominates. Many accepted words cannot clear a deep shade —\n // that is how 92% garbage skipped the flatten tile.\n return lighting * 0.7 + (total / words.length) * 0.3\n}\n\nexport function shadowRegionNeedsOcr(\n analysis: CastShadowAnalysis,\n words: OcrBox[],\n imageSize: { width: number; height: number },\n inkDensity?: (box: OcrBox['bbox']) => number,\n) {\n if (!analysis.strong || !analysis.region) return false\n const inside = words.filter((word) =>\n wordCenterInRegion(word, analysis.region!, imageSize.width, imageSize.height)\n )\n return shadowRegionQuality(analysis, inside, inkDensity) < SHADOW_REGION_QUALITY_MIN\n}\n\nfunction cropShadowTile(\n adaptive: HTMLCanvasElement,\n region: CastShadowRegion,\n originalWidth: number,\n originalHeight: number,\n) {\n const left = Math.max(0, Math.floor(region.x * adaptive.width))\n const top = Math.max(0, Math.floor(region.y * adaptive.height))\n const right = Math.min(\n adaptive.width,\n Math.ceil((region.x + region.width) * adaptive.width),\n )\n const bottom = Math.min(\n adaptive.height,\n Math.ceil((region.y + region.height) * adaptive.height),\n )\n const tile = document.createElement('canvas')\n tile.width = Math.max(1, right - left)\n tile.height = Math.max(1, bottom - top)\n tile.getContext('2d')?.drawImage(\n adaptive,\n left,\n top,\n tile.width,\n tile.height,\n 0,\n 0,\n tile.width,\n tile.height,\n )\n const scaleX = originalWidth / adaptive.width\n const scaleY = originalHeight / adaptive.height\n return {\n tile,\n passMapping: {\n scaleX,\n scaleY,\n offsetX: left * scaleX,\n offsetY: top * scaleY,\n },\n }\n}\n\nfunction writeLuminance(\n image: ImageData,\n values: Uint8Array,\n) {\n for (let pixel = 0, offset = 0; pixel < values.length; pixel += 1, offset += 4) {\n image.data[offset] = values[pixel]\n image.data[offset + 1] = values[pixel]\n image.data[offset + 2] = values[pixel]\n image.data[offset + 3] = 255\n }\n}\n\nfunction prepareOcrCanvases(source: HTMLCanvasElement) {\n const maxDimension = 2200\n const sourceMax = Math.max(source.width, source.height)\n const scale = sourceMax > maxDimension\n ? maxDimension / sourceMax\n : Math.min(1.5, maxDimension / Math.max(1, sourceMax))\n const sparseCanvas = document.createElement('canvas')\n sparseCanvas.width = Math.max(1, Math.round(source.width * scale))\n sparseCanvas.height = Math.max(1, Math.round(source.height * scale))\n const context = sparseCanvas.getContext('2d', { willReadFrequently: true })\n if (!context) throw canvasUnavailable()\n context.drawImage(source, 0, 0, sparseCanvas.width, sparseCanvas.height)\n const { image, luminance } = canvasLuminance(sparseCanvas)\n const flattened = flattenIlluminationLuminance(\n luminance,\n sparseCanvas.width,\n sparseCanvas.height,\n )\n const binary = adaptiveThresholdLuminance(\n flattened,\n sparseCanvas.width,\n sparseCanvas.height,\n )\n const flattenedCanvas = document.createElement('canvas')\n flattenedCanvas.width = sparseCanvas.width\n flattenedCanvas.height = sparseCanvas.height\n const flattenedContext = flattenedCanvas.getContext('2d', { willReadFrequently: true })\n if (!flattenedContext) throw canvasUnavailable()\n const flattenedImage = flattenedContext.createImageData(\n flattenedCanvas.width,\n flattenedCanvas.height,\n )\n writeLuminance(flattenedImage, flattened)\n flattenedContext.putImageData(flattenedImage, 0, 0)\n writeLuminance(image, binary)\n context.putImageData(image, 0, 0)\n return { sparseCanvas, flattenedCanvas }\n}\n\nfunction createInkDensitySampler(source: HTMLCanvasElement) {\n const sample = document.createElement('canvas')\n sample.width = 24\n sample.height = 24\n const context = sample.getContext('2d', { willReadFrequently: true })\n return (box: OcrBox['bbox']) => {\n if (!context || box.width <= 0 || box.height <= 0) return 0\n context.clearRect(0, 0, sample.width, sample.height)\n context.drawImage(\n source,\n box.x,\n box.y,\n box.width,\n box.height,\n 0,\n 0,\n sample.width,\n sample.height,\n )\n const pixels = context.getImageData(0, 0, sample.width, sample.height).data\n let minimum = 255\n let maximum = 0\n const values = new Uint8Array(sample.width * sample.height)\n for (let pixel = 0, offset = 0; pixel < values.length; pixel += 1, offset += 4) {\n const value = Math.round(\n pixels[offset] * 0.299 +\n pixels[offset + 1] * 0.587 +\n pixels[offset + 2] * 0.114,\n )\n values[pixel] = value\n minimum = Math.min(minimum, value)\n maximum = Math.max(maximum, value)\n }\n if (maximum - minimum < 12) return 0\n const threshold = minimum + (maximum - minimum) * 0.55\n let ink = 0\n for (const value of values) if (value < threshold) ink += 1\n return ink / values.length\n }\n}\n\n/**\n * Reads text from a browser image. English uses the packaged traineddata.\n * `includeWords` and `includeCharacters` default to false.\n * `minConfidence` defaults to 0.5.\n *\n * @throws {LicenseError} LicenseError when the OCR feature is not licensed. Recognition does not start.\n * @throws {RangeError} RangeError when `minConfidence` is outside 0–1.\n * @throws {ScannerError} ScannerError `language` when `language` is not `eng` and `langPath` is omitted.\n * `canvas` when canvas 2D is unavailable.\n * `worker` when the Tesseract worker fails to start or language data cannot load.\n */\nexport async function recognizeText(\n source: ImageSource,\n options: OcrOptions = {},\n): Promise<OcrResult> {\n await assertLicense('ocr')\n const languagePlan = resolveOcrLanguage(options.language, options.langPath)\n const language = languagePlan.language\n resolveMinConfidence(options.minConfidence)\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const { sparseCanvas, flattenedCanvas } = prepareOcrCanvases(canvas)\n const shadow: CastShadowAnalysis = options.shadowPass === false\n ? { strong: false, strength: 0, darkness: 0, areaRatio: 0 }\n : sampleCastShadow(canvas)\n const { createWorker, PSM } = await import('tesseract.js')\n let passIndex = 0\n const passCount = shadow.strong ? 3 : 2\n // passCount is 3 when a cast shadow is present, else 2. Logger events use\n // (passIndex + tesseractProgress) / passCount, so worker init at\n // progress=1 is 33% or 50% before the first recognize() call.\n const worker = await createTesseractWorker(createWorker, language, {\n workerPath: tesseractWorkerUrl.href,\n corePath: selectLstmCoreUrl(detectLstmCoreSupport()).href,\n langPath: tesseractLanguagePath(languagePlan),\n logger(message) {\n if (typeof message.progress === 'number') {\n options.onProgress?.(\n Math.min(1, (passIndex + message.progress) / passCount),\n message.status ?? 'recognizing text',\n )\n }\n },\n })\n try {\n await worker.setParameters({ tessedit_pageseg_mode: PSM.AUTO })\n const automatic = await worker.recognize(\n canvas,\n {},\n { text: true, blocks: true },\n )\n passIndex = 1\n await worker.setParameters({\n tessedit_pageseg_mode: PSM.SPARSE_TEXT,\n preserve_interword_spaces: '1',\n })\n const sparse = await worker.recognize(\n sparseCanvas,\n {},\n { text: true, blocks: true },\n )\n const passes: OcrPass[] = [\n { blocks: automatic.data.blocks as TesseractBlock[] | null },\n {\n blocks: sparse.data.blocks as TesseractBlock[] | null,\n scaleX: canvas.width / sparseCanvas.width,\n scaleY: canvas.height / sparseCanvas.height,\n },\n ]\n const mergeOptions = { ...options, includeWords: true }\n const inkDensity = createInkDensitySampler(canvas)\n const baseLayout = mergeOcrPasses(\n passes,\n mergeOptions,\n { width: canvas.width, height: canvas.height },\n inkDensity,\n )\n const useShadowPass =\n options.shadowPass !== false &&\n shadowRegionNeedsOcr(\n shadow,\n baseLayout.words,\n { width: canvas.width, height: canvas.height },\n inkDensity,\n )\n if (useShadowPass && shadow.region) {\n passIndex = 2\n const { tile, passMapping } = cropShadowTile(\n flattenedCanvas,\n shadow.region,\n canvas.width,\n canvas.height,\n )\n await worker.setParameters({\n tessedit_pageseg_mode: PSM.SPARSE_TEXT,\n preserve_interword_spaces: '1',\n })\n const adaptive = await worker.recognize(\n tile,\n {},\n { text: true, blocks: true },\n )\n passes.push({\n blocks: adaptive.data.blocks as TesseractBlock[] | null,\n ...passMapping,\n })\n }\n const layout = passes.length === 2\n ? baseLayout\n : mergeOcrPasses(\n passes,\n mergeOptions,\n { width: canvas.width, height: canvas.height },\n inkDensity,\n )\n options.onProgress?.(1, 'recognizing text')\n return {\n text: layout.text,\n confidence: layout.confidence,\n language,\n imageWidth: canvas.width,\n imageHeight: canvas.height,\n diagnostics: {\n shadowPassUsed: useShadowPass,\n castShadowStrength: shadow.strength,\n shadowRegion: shadow.region\n ? {\n x: shadow.region.x * canvas.width,\n y: shadow.region.y * canvas.height,\n width: shadow.region.width * canvas.width,\n height: shadow.region.height * canvas.height,\n }\n : undefined,\n },\n ...(options.includeWords ? { words: layout.words } : {}),\n ...(options.includeCharacters ? { characters: layout.characters } : {}),\n }\n } finally {\n await worker.terminate()\n }\n}\n","import { probeHttpUrl } from './ort-cdn'\n\n/**\n * zxing-wasm reader 3.1.6. License-back PDF417 decode uses this, not\n * BarcodeDetector. Literal `new URL` is what Webpack and Turbopack copy.\n */\nexport const packagedZxingReaderWasm = new URL(\n './runtime/zxing_reader.wasm',\n import.meta.url,\n)\n\nexport const PINNED_ZXING_WASM_VERSION = '3.1.6' as const\nexport const ZXING_READER_WASM_FILENAME = 'zxing_reader.wasm'\n\nexport const PREFERRED_ZXING_CDN_BASE =\n `https://exige-scanner-sdk.vercel.app/zxing/${PINNED_ZXING_WASM_VERSION}/`\n\nexport const LAST_RESORT_ZXING_CDN_BASE =\n `https://cdn.jsdelivr.net/npm/zxing-wasm@${PINNED_ZXING_WASM_VERSION}/dist/reader/`\n\nexport function joinZxingCdnUrl(base: string, fileName: string) {\n const root = base.endsWith('/') ? base : `${base}/`\n return `${root}${fileName}`\n}\n\nexport function pinnedZxingReaderWasmUrl(base: string = PREFERRED_ZXING_CDN_BASE) {\n return joinZxingCdnUrl(base, ZXING_READER_WASM_FILENAME)\n}\n\nexport function packagedZxingReaderWasmUrl() {\n return packagedZxingReaderWasm.href\n}\n\nlet testWasmBinary: ArrayBuffer | undefined\nlet testWasmUrl: string | undefined\n\n/** Injected only for Node tests so jsDelivr is not required. */\nexport function setZxingReaderWasmForTests(value?: { binary?: ArrayBuffer; url?: string }) {\n testWasmBinary = value?.binary\n testWasmUrl = value?.url\n}\n\nexport function zxingReaderWasmForTests() {\n return { binary: testWasmBinary, url: testWasmUrl }\n}\n\nexport async function resolveUsableZxingReaderWasmUrl(\n fetchImpl: typeof fetch = fetch,\n): Promise<string> {\n if (testWasmUrl?.trim()) return testWasmUrl.trim()\n const packaged = packagedZxingReaderWasmUrl()\n if (await probeHttpUrl(packaged, fetchImpl)) return packaged\n const preferred = pinnedZxingReaderWasmUrl(PREFERRED_ZXING_CDN_BASE)\n if (await probeHttpUrl(preferred, fetchImpl)) return preferred\n return pinnedZxingReaderWasmUrl(LAST_RESORT_ZXING_CDN_BASE)\n}\n\n/**\n * Fetch the reader wasm as bytes. Safari often fails `instantiateStreaming`\n * when `locateFile` hits a wrong MIME type; `wasmBinary` avoids that.\n */\nexport async function loadZxingReaderWasmBinary(\n fetchImpl: typeof fetch = fetch,\n): Promise<ArrayBuffer> {\n const injected = zxingReaderWasmForTests()\n if (injected.binary) return injected.binary.slice(0)\n const url = await resolveUsableZxingReaderWasmUrl(fetchImpl)\n const response = await fetchImpl(url)\n if (!response.ok) {\n throw new Error(`Could not download zxing-wasm (${response.status}).`)\n }\n return await response.arrayBuffer()\n}\n","import { canvasToBlob, sourceToCanvas } from './image'\nimport type { DocumentCorners, DocumentQuad, ImageSource, Point } from './types'\nimport { loadZxingReaderWasmBinary } from './zxing-runtime'\n\n/**\n * Never feed zxing a 4K original. A 2160×3840 portrait still is ~8MP;\n * the PDF417 is a wide center band and reads at ~1600 long-edge (the\n * distance that already worked when the code was ~1/3 of the preview).\n */\nexport const PDF417_LIVE_MAX_EDGE = 1600\nexport const PDF417_DECODE_EDGES = [1600, 1280, 960, 720, 480] as const\n\nconst PORTRAIT_BANDS = [\n [0.20, 0.80],\n [0.32, 0.72],\n [0.40, 0.86],\n] as const\n\n/**\n * Decode scales. Caps at `PDF417_LIVE_MAX_EDGE` so a 2160×3840 live\n * frame is not sent to zxing at full size.\n */\nexport function pdf417DecodeEdges(width: number, height: number): number[] {\n const long = Math.max(width, height)\n if (!Number.isFinite(long) || long < 1) return []\n const start = Math.min(long, PDF417_LIVE_MAX_EDGE)\n const edges = [start]\n for (const edge of PDF417_DECODE_EDGES) {\n if (edge <= start - 16) edges.push(edge)\n }\n return edges\n}\n\nexport type Pdf417DecodeSource = 'barcode-detector' | 'zxing-wasm'\nexport type Pdf417DecoderState = 'idle' | 'loading' | 'ready' | 'failed'\n\nexport interface Pdf417DecodeResult {\n text: string\n source: Pdf417DecodeSource\n /** ZXing barcode corners in the source image’s pixel space. */\n quad?: DocumentQuad\n}\n\nexport interface Pdf417Variant {\n canvas: HTMLCanvasElement\n originX: number\n originY: number\n /** Variant pixels / source (or crop) pixels. */\n scale: number\n}\n\nexport class Pdf417DecoderError extends Error {\n readonly code = 'zxing-wasm-load' as const\n constructor(cause?: unknown) {\n super('The barcode decoder failed to load.')\n this.name = 'Pdf417DecoderError'\n if (cause !== undefined) this.cause = cause\n }\n}\n\nlet decoderState: Pdf417DecoderState = 'idle'\nlet decoderError: string | undefined\n\nexport function getPdf417DecoderStatus() {\n return { state: decoderState, error: decoderError }\n}\n\ninterface BarcodeDetectorCtor {\n getSupportedFormats(): Promise<string[]>\n}\n\nfunction barcodeDetectorCtor(): BarcodeDetectorCtor | undefined {\n const ctor = (globalThis as { BarcodeDetector?: BarcodeDetectorCtor }).BarcodeDetector\n return typeof ctor === 'function' ? ctor : undefined\n}\n\nexport async function pdf417BarcodeDetectorSupported(): Promise<boolean> {\n const Detector = barcodeDetectorCtor()\n if (!Detector?.getSupportedFormats) return false\n try {\n const formats = await Detector.getSupportedFormats()\n return formats.some((format) => format.toLowerCase() === 'pdf417')\n } catch {\n return false\n }\n}\n\nfunction canvasImageData(canvas: HTMLCanvasElement) {\n try {\n return canvas.getContext('2d', { willReadFrequently: true })\n ?.getImageData(0, 0, canvas.width, canvas.height)\n } catch {\n return undefined\n }\n}\n\nasync function zxingInputs(source: ImageSource): Promise<Array<Blob | ImageData>> {\n const inputs: Array<Blob | ImageData> = []\n if (source instanceof Blob) inputs.push(source)\n if (source instanceof HTMLCanvasElement) {\n const imageData = canvasImageData(source)\n if (imageData) inputs.push(imageData)\n try {\n inputs.push(await canvasToBlob(source, 'image/png'))\n } catch {\n // happy-dom and some WebKit stills cannot encode.\n }\n } else if (!(source instanceof Blob)) {\n try {\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const imageData = canvasImageData(canvas)\n if (imageData) inputs.push(imageData)\n } catch {\n // Fall through to whatever inputs we already have.\n }\n }\n return inputs\n}\n\nlet zxingPrepare: Promise<typeof import('zxing-wasm/reader')> | undefined\n\nasync function zxingReader() {\n if (!zxingPrepare) {\n decoderState = 'loading'\n decoderError = undefined\n zxingPrepare = (async () => {\n const [mod, wasmBinary] = await Promise.all([\n import('zxing-wasm/reader'),\n loadZxingReaderWasmBinary(),\n ])\n await mod.prepareZXingModule({\n overrides: { wasmBinary },\n fireImmediately: true,\n })\n decoderState = 'ready'\n return mod\n })().catch((cause) => {\n zxingPrepare = undefined\n decoderState = 'failed'\n decoderError = cause instanceof Error ? cause.message : 'zxing-wasm failed to load'\n console.error('[exige-scanner] zxing-wasm failed to load', cause)\n throw new Pdf417DecoderError(cause)\n })\n }\n return zxingPrepare\n}\n\n/** Load packaged zxing-wasm now so a later live poll does not fail silently. */\nexport async function preparePdf417Decoder() {\n await zxingReader()\n}\n\nfunction zxingPosition(result: { position?: DocumentCorners }): DocumentCorners | undefined {\n const position = result.position\n if (!position?.topLeft || !position.topRight || !position.bottomRight || !position.bottomLeft) {\n return undefined\n }\n const corners = {\n topLeft: { x: position.topLeft.x, y: position.topLeft.y },\n topRight: { x: position.topRight.x, y: position.topRight.y },\n bottomRight: { x: position.bottomRight.x, y: position.bottomRight.y },\n bottomLeft: { x: position.bottomLeft.x, y: position.bottomLeft.y },\n }\n const xs = [corners.topLeft.x, corners.topRight.x, corners.bottomRight.x, corners.bottomLeft.x]\n const ys = [corners.topLeft.y, corners.topRight.y, corners.bottomRight.y, corners.bottomLeft.y]\n if (Math.max(...xs) - Math.min(...xs) < 2 || Math.max(...ys) - Math.min(...ys) < 2) {\n return undefined\n }\n return corners\n}\n\nexport function cornersToQuad(corners: DocumentCorners): DocumentQuad {\n return {\n ...corners,\n edges: {\n top: { start: corners.topLeft, end: corners.topRight },\n right: { start: corners.topRight, end: corners.bottomRight },\n bottom: { start: corners.bottomRight, end: corners.bottomLeft },\n left: { start: corners.bottomLeft, end: corners.topLeft },\n },\n }\n}\n\nexport function mapPdf417VariantPoint(\n point: Point,\n variant: Pick<Pdf417Variant, 'originX' | 'originY' | 'scale'>,\n): Point {\n const scale = variant.scale > 0 ? variant.scale : 1\n return {\n x: variant.originX + point.x / scale,\n y: variant.originY + point.y / scale,\n }\n}\n\nexport function mapPdf417VariantCorners(\n corners: DocumentCorners,\n variant: Pick<Pdf417Variant, 'originX' | 'originY' | 'scale'>,\n): DocumentCorners {\n return {\n topLeft: mapPdf417VariantPoint(corners.topLeft, variant),\n topRight: mapPdf417VariantPoint(corners.topRight, variant),\n bottomRight: mapPdf417VariantPoint(corners.bottomRight, variant),\n bottomLeft: mapPdf417VariantPoint(corners.bottomLeft, variant),\n }\n}\n\nasync function decodeWithZxing(\n source: ImageSource,\n): Promise<{ text: string, position?: DocumentCorners } | undefined> {\n const { readBarcodes } = await zxingReader()\n for (const input of await zxingInputs(source)) {\n const results = await readBarcodes(input, {\n formats: ['PDF417'],\n tryHarder: true,\n tryRotate: true,\n tryInvert: true,\n tryDownscale: true,\n downscaleFactor: 2,\n downscaleThreshold: 300,\n maxNumberOfSymbols: 1,\n })\n const hit = results.find((item) => item.text?.trim())\n const text = hit?.text?.trim()\n if (text) return { text, position: zxingPosition(hit ?? {}) }\n }\n return undefined\n}\n\nfunction scaleCanvas(source: HTMLCanvasElement, maxEdge: number): HTMLCanvasElement {\n const long = Math.max(source.width, source.height)\n if (long <= maxEdge) return source\n const scale = maxEdge / long\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(source.width * scale))\n canvas.height = Math.max(1, Math.round(source.height * scale))\n const context = canvas.getContext('2d', { willReadFrequently: true })\n ?? canvas.getContext('2d')\n if (context) {\n context.imageSmoothingEnabled = true\n if ('imageSmoothingQuality' in context) {\n context.imageSmoothingQuality = 'high'\n }\n context.drawImage(source, 0, 0, canvas.width, canvas.height)\n }\n return canvas\n}\n\nfunction cropHorizontalBand(\n source: HTMLCanvasElement,\n topRatio: number,\n bottomRatio: number,\n): HTMLCanvasElement {\n const y = Math.max(0, Math.round(source.height * topRatio))\n const height = Math.max(1, Math.round(source.height * bottomRatio) - y)\n const canvas = document.createElement('canvas')\n canvas.width = source.width\n canvas.height = height\n const context = canvas.getContext('2d', { willReadFrequently: true })\n ?? canvas.getContext('2d')\n if (context) {\n context.drawImage(source, 0, y, source.width, height, 0, 0, source.width, height)\n }\n return canvas\n}\n\nfunction scaledVariant(\n source: HTMLCanvasElement,\n maxEdge: number,\n originX = 0,\n originY = 0,\n): Pdf417Variant {\n const long = Math.max(source.width, source.height)\n if (long <= maxEdge) return { canvas: source, originX, originY, scale: 1 }\n return {\n canvas: scaleCanvas(source, maxEdge),\n originX,\n originY,\n scale: maxEdge / long,\n }\n}\n\n/** Full-frame copies capped at `PDF417_LIVE_MAX_EDGE`. */\nexport function pdf417ScaleCanvases(canvas: HTMLCanvasElement): HTMLCanvasElement[] {\n return pdf417DecodeEdges(canvas.width, canvas.height).map((edge) => scaleCanvas(canvas, edge))\n}\n\n/**\n * Portrait live frames (2160×3840) bury a wide PDF417 in a center band.\n * Crop that band before shrinking so modules stay large enough for zxing.\n */\nexport function pdf417BandCanvases(canvas: HTMLCanvasElement): HTMLCanvasElement[] {\n return pdf417DecodeVariants(canvas)\n .filter((variant) => variant.originY > 0 || variant.originX > 0)\n .map((variant) => variant.canvas)\n}\n\n/** Band crops first (portrait), then capped full-frame scales. */\nexport function pdf417DecodeCanvases(canvas: HTMLCanvasElement): HTMLCanvasElement[] {\n return pdf417DecodeVariants(canvas).map((variant) => variant.canvas)\n}\n\n/** Same copies as `pdf417DecodeCanvases`, with crop/scale so a zxing quad maps back. */\nexport function pdf417DecodeVariants(canvas: HTMLCanvasElement): Pdf417Variant[] {\n const seen = new Set<string>()\n const out: Pdf417Variant[] = []\n const push = (variant: Pdf417Variant) => {\n const key = `${variant.canvas.width}x${variant.canvas.height}@${variant.originX},${variant.originY},${variant.scale}`\n if (seen.has(key)) return\n seen.add(key)\n out.push(variant)\n }\n if (canvas.height >= canvas.width * 1.15 && canvas.height >= 120) {\n for (const [top, bottom] of PORTRAIT_BANDS) {\n const originY = Math.max(0, Math.round(canvas.height * top))\n const crop = cropHorizontalBand(canvas, top, bottom)\n push(scaledVariant(crop, PDF417_LIVE_MAX_EDGE, 0, originY))\n const mid = pdf417DecodeEdges(crop.width, crop.height).find((edge) => edge <= 960)\n if (mid) push(scaledVariant(crop, mid, 0, originY))\n }\n }\n for (const edge of pdf417DecodeEdges(canvas.width, canvas.height)) {\n push(scaledVariant(canvas, edge))\n }\n return out\n}\n\n/**\n * PDF417 from a live frame or still via packaged `zxing-wasm` only.\n * Does not use `BarcodeDetector` (Chrome’s detector often misses a\n * full-in-frame code until it is ~1/3 of the preview). Tries the original\n * then downscaled copies so a close-up can still read.\n */\nexport async function decodePdf417(source: ImageSource): Promise<Pdf417DecodeResult | undefined> {\n let canvas: HTMLCanvasElement\n try {\n canvas = await sourceToCanvas(source, { cloneCanvas: false })\n } catch {\n canvas = source instanceof HTMLCanvasElement ? source : document.createElement('canvas')\n }\n const variants = canvas.width && canvas.height ? pdf417DecodeVariants(canvas) : []\n\n for (const variant of variants) {\n try {\n const decoded = await decodeWithZxing(variant.canvas)\n if (decoded?.text) {\n const corners = decoded.position\n ? mapPdf417VariantCorners(decoded.position, variant)\n : undefined\n return {\n text: decoded.text,\n source: 'zxing-wasm',\n quad: corners ? cornersToQuad(corners) : undefined,\n }\n }\n } catch (cause) {\n if (cause instanceof Pdf417DecoderError) throw cause\n // Next scale.\n }\n }\n if (source instanceof Blob) {\n try {\n const decoded = await decodeWithZxing(source)\n if (!decoded?.text) return undefined\n return {\n text: decoded.text,\n source: 'zxing-wasm',\n quad: decoded.position ? cornersToQuad(decoded.position) : undefined,\n }\n } catch (cause) {\n if (cause instanceof Pdf417DecoderError) throw cause\n return undefined\n }\n }\n return undefined\n}\n","/**\n * AAMVA DL/ID compact barcode (~2020). Line-based three-letter elements after\n * the `@` / `ANSI` header. Unknown IDs stay in `rawElements`.\n */\n\nexport const AAMVA_ELEMENT_IDS = [\n 'DCA', 'DCB', 'DCD', 'DBA', 'DCS', 'DAC', 'DAD', 'DBD', 'DBB', 'DBC',\n 'DAY', 'DAU', 'DAG', 'DAI', 'DAJ', 'DAK', 'DAQ', 'DCF', 'DCG', 'DDE',\n 'DDF', 'DDG', 'DAH', 'DAZ', 'DCI', 'DCJ', 'DCK', 'DBN', 'DBG', 'DBS',\n 'DCU', 'DCE', 'DCL', 'DCM', 'DCN', 'DCO', 'DCP', 'DCQ', 'DCR', 'DDA',\n 'DDB', 'DDC', 'DDD', 'DAW', 'DAX', 'DDH', 'DDI', 'DDJ', 'DDK', 'DDL',\n 'DCT', 'DAL', 'DAM', 'DAN', 'DAO', 'DAR', 'DAS', 'DAT', 'DAV', 'DBH',\n 'DBI', 'DBJ', 'DBK', 'DBL', 'DBM', 'DCH',\n] as const\n\nexport type AamvaElementId = (typeof AAMVA_ELEMENT_IDS)[number]\n\nconst ELEMENT_ID_SET = new Set<string>(AAMVA_ELEMENT_IDS)\n\nexport type ScanUseCase = 'document' | 'drivers-license'\n\nexport type DriverLicenseFieldSource = 'barcode' | 'ocr' | 'merged'\n\nexport interface DriverLicenseFields {\n issuerJurisdiction?: string\n expirationDate?: string\n documentNumber?: string\n firstName?: string\n middleName?: string\n lastName?: string\n dateOfBirth?: string\n sex?: string\n eyeColor?: string\n height?: string\n addressStreet?: string\n addressCity?: string\n addressPostalCode?: string\n issueDate?: string\n country?: string\n addressStreet2?: string\n hairColor?: string\n nameSuffix?: string\n vehicleClass?: string\n restrictionCodes?: string\n endorsementCodes?: string\n documentDiscriminator?: string\n inventoryControlNumber?: string\n raceEthnicity?: string\n organDonor?: string\n veteran?: string\n weight?: string\n}\n\nexport type DriverLicenseFieldKey = keyof DriverLicenseFields\n\nexport const DRIVER_LICENSE_FIELD_KEYS = [\n 'issuerJurisdiction',\n 'expirationDate',\n 'documentNumber',\n 'firstName',\n 'middleName',\n 'lastName',\n 'dateOfBirth',\n 'sex',\n 'eyeColor',\n 'height',\n 'addressStreet',\n 'addressCity',\n 'addressPostalCode',\n 'issueDate',\n 'country',\n 'addressStreet2',\n 'hairColor',\n 'nameSuffix',\n 'vehicleClass',\n 'restrictionCodes',\n 'endorsementCodes',\n 'documentDiscriminator',\n 'inventoryControlNumber',\n 'raceEthnicity',\n 'organDonor',\n 'veteran',\n 'weight',\n] as const satisfies readonly DriverLicenseFieldKey[]\n\nconst DATE_FIELDS = new Set<DriverLicenseFieldKey>([\n 'expirationDate',\n 'dateOfBirth',\n 'issueDate',\n])\n\nconst PRIMARY_ELEMENT_TO_FIELD: Record<string, DriverLicenseFieldKey> = {\n DAJ: 'issuerJurisdiction',\n DBA: 'expirationDate',\n DAQ: 'documentNumber',\n DAC: 'firstName',\n DAD: 'middleName',\n DCS: 'lastName',\n DBB: 'dateOfBirth',\n DBC: 'sex',\n DAY: 'eyeColor',\n DAU: 'height',\n DAG: 'addressStreet',\n DAI: 'addressCity',\n DAK: 'addressPostalCode',\n DBD: 'issueDate',\n DCG: 'country',\n DAH: 'addressStreet2',\n DAZ: 'hairColor',\n DCU: 'nameSuffix',\n DCA: 'vehicleClass',\n DCB: 'restrictionCodes',\n DCD: 'endorsementCodes',\n DCF: 'documentDiscriminator',\n DCK: 'inventoryControlNumber',\n DCL: 'raceEthnicity',\n DDK: 'organDonor',\n DDH: 'organDonor',\n DDL: 'veteran',\n DDI: 'veteran',\n DAW: 'weight',\n DAX: 'weight',\n}\n\nexport interface AamvaParseResult {\n rawElements: Record<string, string>\n fields: DriverLicenseFields\n header?: {\n iin?: string\n aamvaVersion?: string\n jurisdictionVersion?: string\n }\n}\n\nfunction isoDate(year: string, month: string, day: string) {\n const y = Number(year)\n const m = Number(month)\n const d = Number(day)\n if (!Number.isInteger(y) || !Number.isInteger(m) || !Number.isInteger(d)) return undefined\n if (y < 1900 || y > 2100 || m < 1 || m > 12 || d < 1 || d > 31) return undefined\n return `${String(y).padStart(4, '0')}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`\n}\n\nfunction expandTwoDigitYear(yy: number, preferPast: boolean) {\n if (preferPast) return yy >= 30 ? 1900 + yy : 2000 + yy\n return yy >= 50 ? 1900 + yy : 2000 + yy\n}\n\n/**\n * `MMDDYYYY` / `YYYYMMDD` / `MMDDYY` / slash dates → `YYYY-MM-DD`.\n * Unparseable values return undefined so the caller can keep the raw string.\n */\nexport function parseAamvaDate(value: string, preferPast = false): string | undefined {\n const trimmed = value.trim()\n if (!trimmed) return undefined\n const slashed = /^(\\d{1,2})[/-](\\d{1,2})[/-](\\d{2,4})$/.exec(trimmed)\n if (slashed) {\n const month = slashed[1]\n const day = slashed[2]\n let year = slashed[3]\n if (year.length === 2) {\n year = String(expandTwoDigitYear(Number(year), preferPast))\n }\n return isoDate(year, month.padStart(2, '0'), day.padStart(2, '0'))\n }\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(trimmed)\n if (iso) return isoDate(iso[1], iso[2], iso[3])\n const compact = trimmed.replace(/[^\\d]/g, '')\n if (compact.length === 8) {\n const yearFirst = Number(compact.slice(0, 4))\n if (yearFirst >= 1900 && yearFirst <= 2100) {\n return isoDate(compact.slice(0, 4), compact.slice(4, 6), compact.slice(6, 8))\n }\n return isoDate(compact.slice(4, 8), compact.slice(0, 2), compact.slice(2, 4))\n }\n if (compact.length === 6) {\n const year = expandTwoDigitYear(Number(compact.slice(4, 6)), preferPast)\n return isoDate(String(year), compact.slice(0, 2), compact.slice(2, 4))\n }\n return undefined\n}\n\nfunction stripHeaderPrefix(line: string) {\n const trimmed = line.trim()\n if (!trimmed || trimmed === '@') return ''\n if (/^(ANSI|AAMVA)\\b/.test(trimmed)) {\n const subfile = trimmed.search(/(?:DL|ID)[A-Z]{3}/)\n return subfile >= 0 ? trimmed.slice(subfile) : ''\n }\n return trimmed\n}\n\nfunction consumeSubfilePrefix(line: string) {\n if ((line.startsWith('DL') || line.startsWith('ID') || line.startsWith('ZV'))\n && line.length >= 5\n && ELEMENT_ID_SET.has(line.slice(2, 5))) {\n return line.slice(2)\n }\n return line\n}\n\nfunction parseIdStream(text: string, elements: Record<string, string>) {\n let index = 0\n while (index < text.length) {\n const id = text.slice(index, index + 3)\n if (!ELEMENT_ID_SET.has(id)) {\n index += 1\n continue\n }\n index += 3\n let end = index\n while (end < text.length) {\n const next = text.slice(end, end + 3)\n if (ELEMENT_ID_SET.has(next)) break\n if (text[end] === '\\n' || text[end] === '\\r' || text[end] === '\\u001e') break\n end += 1\n }\n const value = text.slice(index, end).trim()\n if (!(id in elements) || value) elements[id] = value\n index = end\n if (text[index] === '\\n' || text[index] === '\\r' || text[index] === '\\u001e') index += 1\n }\n}\n\nfunction parseAnsiHeader(text: string) {\n const match = /(?:ANSI|AAMVA)\\s?(\\d{6})(\\d{2})(\\d{2})(\\d{2})/.exec(text)\n if (!match) return undefined\n return {\n iin: match[1],\n aamvaVersion: match[2],\n jurisdictionVersion: match[3],\n }\n}\n\nexport function parseAamvaElements(text: string): Record<string, string> {\n const normalized = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n')\n const elements: Record<string, string> = {}\n const recordSeparator = String.fromCharCode(30)\n const lines = normalized.split('\\n').flatMap((line) => line.split(recordSeparator))\n const lined = lines.some((line) => {\n const body = consumeSubfilePrefix(stripHeaderPrefix(line))\n return ELEMENT_ID_SET.has(body.slice(0, 3))\n })\n if (lined) {\n for (const line of lines) {\n const body = consumeSubfilePrefix(stripHeaderPrefix(line))\n if (!body) continue\n parseIdStream(body, elements)\n }\n return elements\n }\n parseIdStream(normalized, elements)\n return elements\n}\n\nfunction fieldValue(raw: string, field: DriverLicenseFieldKey) {\n if (DATE_FIELDS.has(field)) {\n return parseAamvaDate(raw, field === 'dateOfBirth') ?? raw.trim()\n }\n if (field === 'issuerJurisdiction' || field === 'country') {\n return raw.trim().toUpperCase()\n }\n if (field === 'addressPostalCode') {\n const compact = raw.trim()\n if (/^\\d{9}$/.test(compact) && compact.endsWith('0000')) return compact.slice(0, 5)\n return compact\n }\n return raw.trim()\n}\n\nexport function fieldsFromAamvaElements(rawElements: Record<string, string>): DriverLicenseFields {\n const fields: DriverLicenseFields = {}\n for (const [id, raw] of Object.entries(rawElements)) {\n const field = PRIMARY_ELEMENT_TO_FIELD[id]\n if (!field || !raw.trim()) continue\n if (fields[field]) continue\n fields[field] = fieldValue(raw, field)\n }\n if (!fields.firstName && rawElements.DCT?.trim()) {\n const parts = rawElements.DCT.trim().split(/\\s+/)\n fields.firstName = parts[0]\n if (!fields.middleName && parts.length > 1) {\n fields.middleName = parts.slice(1).join(' ')\n }\n }\n return fields\n}\n\nexport function parseAamvaPayload(text: string): AamvaParseResult {\n const rawElements = parseAamvaElements(text)\n return {\n rawElements,\n fields: fieldsFromAamvaElements(rawElements),\n header: parseAnsiHeader(text),\n }\n}\n\nexport function emptyFieldSources(): Partial<Record<DriverLicenseFieldKey, DriverLicenseFieldSource>> {\n return {}\n}\n\nexport function assignFieldSource(\n sources: Partial<Record<DriverLicenseFieldKey, DriverLicenseFieldSource>>,\n fields: DriverLicenseFields,\n source: DriverLicenseFieldSource,\n) {\n for (const key of DRIVER_LICENSE_FIELD_KEYS) {\n if (fields[key]) sources[key] = source\n }\n return sources\n}\n","import { canvasUnavailable } from './errors'\nimport { sourceToCanvas } from './image'\nimport type { Box, ImageSource } from './types'\n\nconst sessions = new Map<string, Promise<import('onnxruntime-web').InferenceSession>>()\n\nexport const faceInputSize = 640\nexport const faceYoloxAnchorsAt640 = 80 * 80 + 40 * 40 + 20 * 20\n/** `cx, cy, w, h, objectness`, then one face probability. */\nexport const faceYoloxFeatures = 6\n\nexport interface FaceDetection {\n bbox: Box\n confidence: number\n}\n\nexport interface DecodeFaceTensorOptions {\n confidence?: number\n imageWidth: number\n imageHeight: number\n inputSize?: number\n}\n\nfunction overlap(a: Box, b: Box) {\n const intersection =\n Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) *\n Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y))\n return intersection / Math.max(1, a.width * a.height + b.width * b.height - intersection)\n}\n\nfunction scaledBox(\n centerX: number,\n centerY: number,\n width: number,\n height: number,\n inputSize: number,\n imageWidth: number,\n imageHeight: number,\n): Box {\n return {\n x: ((centerX - width / 2) / inputSize) * imageWidth,\n y: ((centerY - height / 2) / inputSize) * imageHeight,\n width: (width / inputSize) * imageWidth,\n height: (height / inputSize) * imageHeight,\n }\n}\n\n/**\n * Decode a one-class YOLOX-Nano face tensor.\n * Shape is `[1, 8400, 6]` at 640: `cx, cy, w, h, objectness`, then the face probability.\n * Confidence is objectness times that probability. Boxes are stretched back onto the source image.\n */\nexport function decodeFaceTensor(\n values: ArrayLike<number>,\n dimensions: readonly number[],\n options: DecodeFaceTensorOptions,\n): FaceDetection[] {\n const anchors = dimensions.length === 3 ? dimensions[1] : dimensions[0]\n const features = dimensions.length === 3 ? dimensions[2] : dimensions[1]\n if (features !== faceYoloxFeatures || anchors < 1) {\n throw new Error(\n `Face model expects [1, anchors, ${faceYoloxFeatures}]. A 640 YOLOX-Nano face export is [1, ${faceYoloxAnchorsAt640}, ${faceYoloxFeatures}].`,\n )\n }\n const confidence = options.confidence ?? 0.35\n const inputSize = options.inputSize ?? faceInputSize\n const detections: FaceDetection[] = []\n for (let anchor = 0; anchor < anchors; anchor += 1) {\n const base = anchor * features\n const score = values[base + 4] * values[base + 5]\n if (score < confidence) continue\n detections.push({\n confidence: score,\n bbox: scaledBox(\n values[base],\n values[base + 1],\n values[base + 2],\n values[base + 3],\n inputSize,\n options.imageWidth,\n options.imageHeight,\n ),\n })\n }\n return detections\n .sort((a, b) => b.confidence - a.confidence)\n .filter((candidate, index, all) =>\n all.slice(0, index).every((accepted) => overlap(candidate.bbox, accepted.bbox) < 0.45),\n )\n .slice(0, 8)\n}\n\nexport async function detectFaces(\n source: ImageSource,\n modelUrl: string,\n confidence = 0.35,\n): Promise<FaceDetection[]> {\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const ort = await import('onnxruntime-web')\n const size = faceInputSize\n const input = document.createElement('canvas')\n input.width = size\n input.height = size\n const context = input.getContext('2d', { willReadFrequently: true })\n if (!context) throw canvasUnavailable()\n context.drawImage(canvas, 0, 0, size, size)\n const pixels = context.getImageData(0, 0, size, size).data\n const channelSize = size * size\n const data = new Float32Array(channelSize * 3)\n for (let index = 0; index < channelSize; index += 1) {\n data[index] = pixels[index * 4] / 255\n data[channelSize + index] = pixels[index * 4 + 1] / 255\n data[channelSize * 2 + index] = pixels[index * 4 + 2] / 255\n }\n let sessionPromise = sessions.get(modelUrl)\n if (!sessionPromise) {\n sessionPromise = ort.InferenceSession.create(modelUrl, {\n executionProviders: ['wasm'],\n graphOptimizationLevel: 'all',\n })\n sessions.set(modelUrl, sessionPromise)\n }\n const session = await sessionPromise\n const output = await session.run({\n [session.inputNames[0]]: new ort.Tensor('float32', data, [1, 3, size, size]),\n })\n const tensor = output[session.outputNames[0]]\n return decodeFaceTensor(tensor.data as Float32Array, tensor.dims.map(Number), {\n confidence,\n imageWidth: canvas.width,\n imageHeight: canvas.height,\n inputSize: size,\n })\n}\n","import { canvasUnavailable } from './errors'\nimport { sourceToCanvas } from './image'\nimport { downloadModelBytes } from './model-download'\nimport type { ImageSource } from './types'\nimport type { SemanticField, SemanticModelDetection } from './programmatic-redaction'\n\nconst sessions = new Map<string, Promise<import('onnxruntime-web').InferenceSession>>()\n\nexport const defaultFieldClasses: SemanticField[] = [\n 'person_name',\n 'date_of_birth',\n 'address',\n 'document_number',\n 'expiration_date',\n 'postal_code',\n 'state',\n]\n\n/** YOLOv8 channel-major output, or decoded YOLOX-Nano anchor-major output. */\nexport type FieldLayout = 'yolov8' | 'yolox'\n\n/**\n * Decoded YOLOX-Nano at 640×640 (strides 8, 16, 32).\n * 80×80 + 40×40 + 20×20. Official nano ships at 416; this SDK resizes to 640.\n */\nexport const yoloxNanoAnchorsAt640 = 80 * 80 + 40 * 40 + 20 * 20\n\n/** `cx, cy, w, h, objectness` plus one probability per class. Seven classes → 12. */\nexport function yoloxFeatureWidth(classCount: number) {\n return 5 + classCount\n}\n\nconst fieldInputSize = 640\n\nexport interface DecodeFieldTensorOptions {\n classes?: SemanticField[]\n confidence?: number\n layout?: FieldLayout\n imageWidth: number\n imageHeight: number\n inputSize?: number\n}\n\nfunction overlap(a: SemanticModelDetection, b: SemanticModelDetection) {\n const intersection =\n Math.max(0, Math.min(a.bbox.x + a.bbox.width, b.bbox.x + b.bbox.width) - Math.max(a.bbox.x, b.bbox.x)) *\n Math.max(0, Math.min(a.bbox.y + a.bbox.height, b.bbox.y + b.bbox.height) - Math.max(a.bbox.y, b.bbox.y))\n return intersection / Math.max(1, a.bbox.width * a.bbox.height + b.bbox.width * b.bbox.height - intersection)\n}\n\nfunction suppress(detections: SemanticModelDetection[]) {\n return detections\n .sort((a, b) => b.confidence - a.confidence)\n .filter((candidate, index, all) =>\n all.slice(0, index).every((accepted) =>\n accepted.field !== candidate.field || overlap(candidate, accepted) < 0.45,\n ),\n )\n .slice(0, 30)\n}\n\nfunction scaledBox(\n centerX: number,\n centerY: number,\n width: number,\n height: number,\n inputSize: number,\n imageWidth: number,\n imageHeight: number,\n) {\n return {\n x: ((centerX - width / 2) / inputSize) * imageWidth,\n y: ((centerY - height / 2) / inputSize) * imageHeight,\n width: (width / inputSize) * imageWidth,\n height: (height / inputSize) * imageHeight,\n }\n}\n\n/** Rank-3 `[1, anchors, 5+classes]` or rank-2 `[anchors, 5+classes]`. */\nfunction yoloxTensorAxes(dimensions: readonly number[], classCount: number) {\n const features = yoloxFeatureWidth(classCount)\n if (\n dimensions.length === 3 &&\n dimensions[0] === 1 &&\n dimensions[2] === features &&\n dimensions[1] > dimensions[2]\n ) {\n return { anchors: dimensions[1], features }\n }\n if (dimensions.length === 2 && dimensions[1] === features && dimensions[0] > dimensions[1]) {\n return { anchors: dimensions[0], features }\n }\n return null\n}\n\nfunction isYolov8Shape(dimensions: readonly number[]) {\n return dimensions.length === 3 && dimensions[1] >= 5 && dimensions[2] >= 1\n}\n\nfunction yoloxShapeError(classCount: number) {\n const features = yoloxFeatureWidth(classCount)\n return new Error(\n `YOLOX field layout expects [1, anchors, ${features}] (cx, cy, w, h, objectness, ${classCount} class scores). ` +\n `A 640 YOLOX-Nano export with decode baked in is [1, ${yoloxNanoAnchorsAt640}, ${features}].`,\n )\n}\n\nfunction decodeYolov8(\n values: ArrayLike<number>,\n dimensions: readonly number[],\n classes: SemanticField[],\n confidence: number,\n inputSize: number,\n imageWidth: number,\n imageHeight: number,\n) {\n if (!isYolov8Shape(dimensions)) throw new Error('Unsupported field-model YOLO output shape.')\n const features = dimensions[1]\n const candidates = dimensions[2]\n const detections: SemanticModelDetection[] = []\n for (let index = 0; index < candidates; index += 1) {\n let score = 0\n let classIndex = 0\n for (let feature = 4; feature < Math.min(features, classes.length + 4); feature += 1) {\n const candidateScore = values[feature * candidates + index]\n if (candidateScore > score) {\n score = candidateScore\n classIndex = feature - 4\n }\n }\n const field = classes[classIndex]\n if (!field || score < confidence) continue\n detections.push({\n field,\n confidence: score,\n bbox: scaledBox(\n values[index],\n values[candidates + index],\n values[candidates * 2 + index],\n values[candidates * 3 + index],\n inputSize,\n imageWidth,\n imageHeight,\n ),\n })\n }\n return suppress(detections)\n}\n\nfunction decodeYolox(\n values: ArrayLike<number>,\n dimensions: readonly number[],\n classes: SemanticField[],\n confidence: number,\n inputSize: number,\n imageWidth: number,\n imageHeight: number,\n) {\n const axes = yoloxTensorAxes(dimensions, classes.length)\n if (!axes) throw yoloxShapeError(classes.length)\n const detections: SemanticModelDetection[] = []\n for (let anchor = 0; anchor < axes.anchors; anchor += 1) {\n const base = anchor * axes.features\n const objectness = values[base + 4]\n let classScore = 0\n let classIndex = 0\n for (let offset = 0; offset < classes.length; offset += 1) {\n const candidateScore = values[base + 5 + offset]\n if (candidateScore > classScore) {\n classScore = candidateScore\n classIndex = offset\n }\n }\n const field = classes[classIndex]\n const score = objectness * classScore\n if (!field || score < confidence) continue\n detections.push({\n field,\n confidence: score,\n bbox: scaledBox(\n values[base],\n values[base + 1],\n values[base + 2],\n values[base + 3],\n inputSize,\n imageWidth,\n imageHeight,\n ),\n })\n }\n return suppress(detections)\n}\n\n/**\n * Decode a field detector tensor.\n * YOLOv8 is `[1, classes+4, anchors]` (channel-major `cx, cy, w, h`, then class scores).\n * YOLOX-Nano with `--decode_in_inference` is `[1, anchors, 5+classes]`\n * (`cx, cy, w, h, objectness`, then class probabilities already in 640-pixel space).\n * An omitted layout follows the tensor shape and otherwise stays YOLOv8.\n */\nexport function decodeFieldTensor(\n values: ArrayLike<number>,\n dimensions: readonly number[],\n options: DecodeFieldTensorOptions,\n): SemanticModelDetection[] {\n const classes = options.classes ?? defaultFieldClasses\n const confidence = options.confidence ?? 0.35\n const inputSize = options.inputSize ?? fieldInputSize\n const yolox = yoloxTensorAxes(dimensions, classes.length)\n if (options.layout === 'yolov8' && yolox) {\n throw new Error(\n 'This tensor is YOLOX [1, anchors, 5+classes]. Pass fieldLayout: \\'yolox\\', or omit fieldLayout to detect it from the shape.',\n )\n }\n const layout: FieldLayout = options.layout ?? (yolox ? 'yolox' : 'yolov8')\n if (layout === 'yolox') {\n return decodeYolox(values, dimensions, classes, confidence, inputSize, options.imageWidth, options.imageHeight)\n }\n return decodeYolov8(values, dimensions, classes, confidence, inputSize, options.imageWidth, options.imageHeight)\n}\n\nexport async function detectFieldModel(\n source: ImageSource,\n modelUrl: string,\n classes: SemanticField[] = defaultFieldClasses,\n confidence = 0.35,\n layout?: FieldLayout,\n): Promise<SemanticModelDetection[]> {\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const ortPromise = import('onnxruntime-web')\n const size = fieldInputSize\n const input = document.createElement('canvas')\n input.width = size\n input.height = size\n const context = input.getContext('2d', { willReadFrequently: true })\n if (!context) throw canvasUnavailable()\n context.drawImage(canvas, 0, 0, size, size)\n const pixels = context.getImageData(0, 0, size, size).data\n const channelSize = size * size\n const data = new Float32Array(channelSize * 3)\n for (let index = 0; index < channelSize; index += 1) {\n data[index] = pixels[index * 4] / 255\n data[channelSize + index] = pixels[index * 4 + 1] / 255\n data[channelSize * 2 + index] = pixels[index * 4 + 2] / 255\n }\n let sessionPromise = sessions.get(modelUrl)\n if (!sessionPromise) {\n sessionPromise = (async () => {\n try {\n const [runtime, bytes] = await Promise.all([\n ortPromise,\n downloadModelBytes(modelUrl),\n ])\n return await runtime.InferenceSession.create(bytes, {\n executionProviders: ['wasm'],\n graphOptimizationLevel: 'all',\n })\n } catch (error) {\n sessions.delete(modelUrl)\n throw error\n }\n })()\n sessions.set(modelUrl, sessionPromise)\n }\n const [ort, session] = await Promise.all([ortPromise, sessionPromise])\n const output = await session.run({\n [session.inputNames[0]]: new ort.Tensor('float32', data, [1, 3, size, size]),\n })\n const tensor = output[session.outputNames[0]]\n return decodeFieldTensor(tensor.data as Float32Array, tensor.dims.map(Number), {\n classes,\n confidence,\n layout,\n imageWidth: canvas.width,\n imageHeight: canvas.height,\n inputSize: size,\n })\n}\n","import { canvasUnavailable } from './errors'\nimport { canvasToBlob, clampBox, sourceToCanvas } from './image'\nimport { assertLicense } from './license'\nimport type { ImageSource, RedactionRegion, RedactOptions } from './types'\n\nexport interface RedactResult {\n canvas: HTMLCanvasElement\n blob: Blob\n}\n\n/**\n * Paints trusted redaction regions onto a copy of an image and encodes it.\n * Use this when the rectangles are already known. `redactProgrammatically`\n * calls it after matching rules.\n *\n * The source is not mutated. Regions use source-image pixel coordinates and\n * are clipped to the image bounds.\n *\n * @throws {LicenseError} LicenseError when the redaction feature is not licensed.\n * @throws {ScannerError} ScannerError `canvas` when canvas 2D is unavailable.\n */\nexport async function applyRedactions(\n source: ImageSource,\n regions: RedactionRegion[],\n options: RedactOptions = {},\n): Promise<RedactResult> {\n await assertLicense('redaction')\n const canvas = await sourceToCanvas(source)\n const context = canvas.getContext('2d')\n if (!context) throw canvasUnavailable()\n\n for (const region of regions) {\n const box = clampBox(region, canvas.width, canvas.height)\n if (box.width < 1 || box.height < 1) continue\n const mode = region.mode ?? options.mode ?? 'blackout'\n if (mode === 'blackout') {\n context.fillStyle = '#050706'\n context.fillRect(box.x, box.y, box.width, box.height)\n continue\n }\n const buffer = document.createElement('canvas')\n buffer.width = Math.ceil(box.width)\n buffer.height = Math.ceil(box.height)\n const bufferContext = buffer.getContext('2d')\n if (!bufferContext) continue\n bufferContext.filter = `blur(${options.blurRadius ?? 18}px)`\n const bleed = Math.max(12, options.blurRadius ?? 18)\n bufferContext.drawImage(\n canvas,\n box.x - bleed,\n box.y - bleed,\n box.width + bleed * 2,\n box.height + bleed * 2,\n -bleed,\n -bleed,\n box.width + bleed * 2,\n box.height + bleed * 2,\n )\n context.drawImage(buffer, box.x, box.y, box.width, box.height)\n }\n\n return {\n canvas,\n blob: await canvasToBlob(canvas, options.mimeType ?? 'image/png', options.quality),\n }\n}\n","import { sourceToCanvas } from './image'\nimport { assertLicense } from './license'\nimport { detectFaces, type FaceDetection } from './face-model'\nimport { detectFieldModel, type FieldLayout } from './field-model'\nimport { recognizeText } from './ocr'\nimport { applyRedactions, type RedactResult } from './redact'\nimport type { Box, ImageSource, OcrBox, OcrResult, RedactOptions } from './types'\n\nexport type StringMatchMode = 'exact' | 'case-insensitive' | 'regex'\nexport type LabelDirection = 'above' | 'below' | 'left' | 'right' | 'any'\n\nexport interface StringRedactionRule {\n id: string\n strategy: 'string'\n mode: StringMatchMode\n values?: string[]\n pattern?: string\n flags?: string\n scope?: 'word' | 'line'\n}\n\nexport interface LabelRedactionRule {\n id: string\n strategy: 'label'\n label: string\n direction: LabelDirection\n labelMode?: 'exact' | 'case-insensitive'\n valuePattern?: string\n patternFlags?: string\n window?: {\n distance?: number\n crossAxis?: number\n }\n redactLabel?: boolean\n}\n\nexport type SemanticField =\n | 'person_name'\n | 'date_of_birth'\n | 'address'\n | 'document_number'\n | 'expiration_date'\n | 'postal_code'\n | 'state'\n\n/** `logical` uses aliases, MRZ, and layout. `model` uses a field ONNX. */\nexport type SemanticFieldDetector = 'logical' | 'model'\n\nexport interface SemanticRedactionRule {\n id: string\n strategy: 'semantic'\n field: SemanticField\n /**\n * Defaults to `logical`. A loaded field model does not replace logical rules.\n * Set `model` on a separate rule to redact from `models.fields` or `semanticDetections`.\n */\n detector?: SemanticFieldDetector\n}\n\nexport interface SemanticModelDetection {\n field: SemanticField\n bbox: Box\n confidence: number\n}\n\nexport type ProgrammaticRedactionRule =\n | StringRedactionRule\n | LabelRedactionRule\n | SemanticRedactionRule\n\nexport interface NormalizedRect {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface TemplateRectField {\n id: string\n strategy: 'rect'\n rect: NormalizedRect\n}\n\nexport interface TemplateAnchorField extends Omit<LabelRedactionRule, 'id' | 'strategy'> {\n id: string\n strategy: 'anchor'\n}\n\nexport type RedactionTemplateField = TemplateRectField | TemplateAnchorField\n\nexport interface RedactionTemplate {\n id: string\n fields: RedactionTemplateField[]\n}\n\nexport interface MatchRedactionOptions {\n rules: ProgrammaticRedactionRule[]\n template?: RedactionTemplate\n imageWidth?: number\n imageHeight?: number\n semanticDetections?: SemanticModelDetection[]\n /**\n * When true, rules with `detector: 'model'` use `semanticDetections`.\n * Logical rules ignore this flag.\n */\n modelRequested?: boolean\n}\n\nexport interface RedactionMatch {\n ruleId: string\n strategy: 'string' | 'label' | 'template-rect' | 'template-anchor'\n | 'semantic'\n source?: 'string' | 'label' | 'template' | 'alias' | 'mrz' | 'layout' | 'model'\n bbox: Box\n text?: string\n confidence: number\n}\n\nexport interface RedactionMatchResult {\n matches: RedactionMatch[]\n unmatchedRuleIds: string[]\n complete: boolean\n confidence: number\n}\n\nexport interface ProgrammaticRedactionOptions extends MatchRedactionOptions {\n /**\n * Prior OCR. Omit it to run OCR with word boxes.\n * An object without `words` is used as-is. OCR does not re-run, and\n * word, label, and semantic rules then match nothing.\n */\n ocr?: OcrResult\n style?: RedactOptions\n requireAll?: boolean\n models?: {\n fields?: string\n fieldClasses?: SemanticField[]\n /**\n * ONNX output layout. Defaults to `yolov8` (`[1, classes+4, anchors]`).\n * `yolox` is decoded YOLOX-Nano `[1, anchors, 5+classes]`\n * (`cx, cy, w, h, objectness`, then class scores). A tensor of that shape\n * is read as YOLOX even when this is omitted.\n */\n fieldLayout?: FieldLayout\n confidence?: number\n /**\n * When the field model fails to load, model-detector rules use logical finding.\n * Defaults to true. Logical rules are unchanged either way.\n */\n allowHeuristicFallback?: boolean\n /**\n * One-class YOLOX-Nano face ONNX. Boxes are painted on the image.\n * They do not select OCR words. Omitted means no face model is fetched.\n */\n face?: string\n faceConfidence?: number\n }\n}\n\nexport interface ProgrammaticRedactionResult extends RedactionMatchResult {\n status: 'success' | 'partial' | 'unmatched'\n ocr?: OcrResult\n canvas?: HTMLCanvasElement\n blob?: Blob\n modelStatus: 'not-requested' | 'loaded' | 'fallback' | 'failed'\n modelError?: string\n faces: FaceDetection[]\n faceStatus: 'not-requested' | 'loaded' | 'failed'\n faceError?: string\n}\n\ninterface OcrLine {\n words: OcrBox[]\n text: string\n bbox: Box\n}\n\nfunction unionBoxes(items: Array<{ bbox: Box }>): Box {\n const left = Math.min(...items.map((item) => item.bbox.x))\n const top = Math.min(...items.map((item) => item.bbox.y))\n const right = Math.max(...items.map((item) => item.bbox.x + item.bbox.width))\n const bottom = Math.max(...items.map((item) => item.bbox.y + item.bbox.height))\n return { x: left, y: top, width: right - left, height: bottom - top }\n}\n\nfunction groupLines(words: OcrBox[]) {\n const lines: OcrBox[][] = []\n const sorted = [...words].sort((a, b) =>\n a.bbox.y + a.bbox.height / 2 - (b.bbox.y + b.bbox.height / 2) || a.bbox.x - b.bbox.x,\n )\n for (const word of sorted) {\n const center = word.bbox.y + word.bbox.height / 2\n const line = lines.find((candidate) => {\n const box = unionBoxes(candidate)\n const lineCenter = box.y + box.height / 2\n return Math.abs(center - lineCenter) <= Math.max(word.bbox.height, box.height) * 0.65\n })\n if (line) line.push(word)\n else lines.push([word])\n }\n return lines.map<OcrLine>((lineWords) => {\n lineWords.sort((a, b) => a.bbox.x - b.bbox.x)\n return {\n words: lineWords,\n text: lineWords.map((word) => word.text).join(' '),\n bbox: unionBoxes(lineWords),\n }\n })\n}\n\nfunction normalized(value: string, insensitive: boolean, label = false) {\n const trimmed = value.trim().replace(/\\s+/g, ' ')\n const cleaned = label ? trimmed.replace(/[::]\\s*$/, '') : trimmed\n return insensitive ? cleaned.toLocaleLowerCase() : cleaned\n}\n\nfunction averageConfidence(words: OcrBox[]) {\n return words.reduce((total, word) => total + word.confidence, 0) / Math.max(1, words.length)\n}\n\nfunction matchStringRule(rule: StringRedactionRule, lines: OcrLine[]): RedactionMatch[] {\n const mode = rule.mode\n const insensitive = mode === 'case-insensitive'\n const scope = rule.scope ?? 'line'\n let expression: RegExp | undefined\n if (mode === 'regex') {\n const pattern = rule.pattern ?? rule.values?.[0]\n if (!pattern) return []\n expression = new RegExp(pattern, rule.flags?.replace('g', '') ?? '')\n }\n const values = (rule.values ?? []).filter(Boolean)\n const matches: RedactionMatch[] = []\n const add = (words: OcrBox[]) => matches.push({\n ruleId: rule.id,\n strategy: 'string',\n source: 'string',\n bbox: unionBoxes(words),\n text: words.map((word) => word.text).join(' '),\n confidence: averageConfidence(words),\n })\n if (scope === 'word') {\n for (const line of lines) {\n for (const word of line.words) {\n const candidate = normalized(word.text, insensitive)\n const matched = expression\n ? expression.test(word.text)\n : values.some((value) => candidate === normalized(value, insensitive))\n if (matched) add([word])\n }\n }\n return matches\n }\n for (const line of lines) {\n if (expression) {\n if (expression.test(line.text)) add(line.words)\n continue\n }\n for (const value of values) {\n const expected = normalized(value, insensitive).split(' ')\n for (let index = 0; index <= line.words.length - expected.length; index += 1) {\n const candidate = line.words.slice(index, index + expected.length)\n if (candidate.every((word, offset) =>\n normalized(word.text, insensitive) === expected[offset],\n )) add(candidate)\n }\n }\n }\n return matches\n}\n\nfunction findLabelWords(rule: LabelRedactionRule, lines: OcrLine[]) {\n const insensitive = rule.labelMode !== 'exact'\n const expected = normalized(rule.label, insensitive, true).split(' ')\n for (const line of lines) {\n for (let index = 0; index <= line.words.length - expected.length; index += 1) {\n const candidate = line.words.slice(index, index + expected.length)\n if (candidate.every((word, offset) =>\n normalized(word.text, insensitive, true) === expected[offset],\n )) return candidate\n }\n }\n return undefined\n}\n\nfunction directionalWords(\n words: OcrBox[],\n label: Box,\n direction: Exclude<LabelDirection, 'any'>,\n width: number,\n height: number,\n rule: LabelRedactionRule,\n) {\n const distance = rule.window?.distance ?? 0.28\n const crossAxis = rule.window?.crossAxis ?? 0.12\n const labelCenterX = label.x + label.width / 2\n const labelCenterY = label.y + label.height / 2\n return words.filter((word) => {\n const centerX = word.bbox.x + word.bbox.width / 2\n const centerY = word.bbox.y + word.bbox.height / 2\n if (direction === 'right') {\n return word.bbox.x >= label.x + label.width &&\n word.bbox.x - label.x - label.width <= width * distance &&\n Math.abs(centerY - labelCenterY) <= height * crossAxis\n }\n if (direction === 'left') {\n return word.bbox.x + word.bbox.width <= label.x &&\n label.x - word.bbox.x - word.bbox.width <= width * distance &&\n Math.abs(centerY - labelCenterY) <= height * crossAxis\n }\n if (direction === 'below') {\n return word.bbox.y >= label.y + label.height &&\n word.bbox.y - label.y - label.height <= height * distance &&\n Math.abs(centerX - labelCenterX) <= width * crossAxis\n }\n return word.bbox.y + word.bbox.height <= label.y &&\n label.y - word.bbox.y - word.bbox.height <= height * distance &&\n Math.abs(centerX - labelCenterX) <= width * crossAxis\n })\n}\n\nfunction matchLabelRule(\n rule: LabelRedactionRule,\n words: OcrBox[],\n lines: OcrLine[],\n width: number,\n height: number,\n strategy: RedactionMatch['strategy'] = 'label',\n): RedactionMatch[] {\n const labelWords = findLabelWords(rule, lines)\n if (!labelWords) return []\n const labelBox = unionBoxes(labelWords)\n const labelSet = new Set(labelWords)\n const available = words.filter((word) => !labelSet.has(word))\n const directions: Array<Exclude<LabelDirection, 'any'>> =\n rule.direction === 'any' ? ['right', 'below', 'left', 'above'] : [rule.direction]\n let best: { words: OcrBox[]; distance: number } | undefined\n for (const direction of directions) {\n const candidates = directionalWords(available, labelBox, direction, width, height, rule)\n const candidateLines = groupLines(candidates)\n for (const line of candidateLines) {\n if (rule.valuePattern && !new RegExp(rule.valuePattern, rule.patternFlags).test(line.text)) continue\n const centerX = line.bbox.x + line.bbox.width / 2\n const centerY = line.bbox.y + line.bbox.height / 2\n const labelCenterX = labelBox.x + labelBox.width / 2\n const labelCenterY = labelBox.y + labelBox.height / 2\n const distance = Math.hypot((centerX - labelCenterX) / width, (centerY - labelCenterY) / height)\n if (!best || distance < best.distance) best = { words: line.words, distance }\n }\n }\n if (!best) return []\n const matchedWords = rule.redactLabel ? [...labelWords, ...best.words] : best.words\n return [{\n ruleId: rule.id,\n strategy,\n source: strategy === 'template-anchor' ? 'template' : 'label',\n bbox: unionBoxes(matchedWords),\n text: best.words.map((word) => word.text).join(' '),\n confidence: Math.max(0, averageConfidence(best.words) * (1 - Math.min(0.5, best.distance))),\n }]\n}\n\nconst fieldAliases: Record<SemanticField, string[]> = {\n person_name: ['LN', 'FN', 'DCS', 'DAC', 'DAD', 'NAME'],\n date_of_birth: ['DOB', 'DBB', 'BIRTH'],\n address: ['ADDRESS', 'ADDR', 'DAG', 'DAI'],\n document_number: ['DLN', 'DAQ', 'IDNO', 'LICNO'],\n expiration_date: ['EXP', 'DBA', 'EXPIRES'],\n postal_code: ['ZIP', 'DAK', 'POSTAL'],\n state: ['STATE', 'DAJ'],\n}\n\nconst allAliases = new Set(Object.values(fieldAliases).flat())\nconst dateToken = /^(?:\\d{2}[/-]\\d{2}[/-]\\d{2,4}|\\d{4}[/-]\\d{2}[/-]\\d{2}|\\d{6}|\\d{8})$/\nconst streetToken = /\\b(?:ST|STREET|AVE|AVENUE|RD|ROAD|BLVD|BOULEVARD|LN|LANE|DR|DRIVE|WAY|CT|COURT)\\b/i\nconst headerToken = /\\b(?:DRIVER|LICENSE|IDENTIFICATION|CARD|CLASS|SEX|EYES|HEIGHT|RESTRICTIONS)\\b/i\n\nfunction semanticTextIsValid(field: SemanticField, text: string) {\n const value = text.trim()\n if (!value) return false\n if (field === 'date_of_birth' || field === 'expiration_date') return dateToken.test(value.replace(/\\s/g, ''))\n if (field === 'postal_code') return /^(?:\\d{5}(?:-\\d{4})?|[A-Z]\\d[A-Z]\\s?\\d[A-Z]\\d)$/i.test(value)\n if (field === 'state') return /^[A-Z]{2}$/i.test(value)\n if (field === 'document_number') return /^(?=.*\\d)[A-Z0-9-]{5,20}$/i.test(value)\n if (field === 'person_name') {\n return !/\\d/.test(value) && /^[A-ZÀ-ÖØ-öø-ÿ' -]{3,}$/i.test(value) && !headerToken.test(value)\n }\n return /\\d/.test(value) && /[A-Z]/i.test(value)\n}\n\nfunction semanticMatch(\n ruleId: string,\n source: NonNullable<RedactionMatch['source']>,\n words: OcrBox[],\n text?: string,\n confidenceScale = 1,\n): RedactionMatch {\n return {\n ruleId,\n strategy: 'semantic',\n source,\n bbox: unionBoxes(words),\n text: text ?? words.map((word) => word.text).join(' '),\n confidence: Math.max(0, Math.min(1, averageConfidence(words) * confidenceScale)),\n }\n}\n\nfunction aliasSemanticMatches(field: SemanticField, ruleId: string, lines: OcrLine[]) {\n const aliases = fieldAliases[field]\n const matches: RedactionMatch[] = []\n for (const line of lines) {\n for (let index = 0; index < line.words.length; index += 1) {\n const token = line.words[index].text.toUpperCase().replace(/[^A-Z0-9]/g, '')\n const alias = aliases.find((candidate) => token === candidate || token.startsWith(candidate))\n if (!alias) continue\n let valueWords: OcrBox[]\n let valueText: string\n if (token !== alias) {\n valueWords = [line.words[index]]\n valueText = line.words[index].text.replace(new RegExp(`^${alias}[:\\\\s-]*`, 'i'), '')\n } else {\n valueWords = line.words.slice(index + 1).filter((word) =>\n !allAliases.has(word.text.toUpperCase().replace(/[^A-Z0-9]/g, '')),\n )\n valueText = valueWords.map((word) => word.text).join(' ')\n }\n if (!valueWords.length || !semanticTextIsValid(field, valueText)) continue\n matches.push(semanticMatch(ruleId, 'alias', valueWords, valueText, 0.98))\n }\n }\n return matches\n}\n\nfunction mrzSemanticMatches(field: SemanticField, ruleId: string, lines: OcrLine[]) {\n const mrzLines = lines.filter((line) => {\n const compact = line.text.replace(/\\s/g, '')\n return compact.includes('<<') || /^[A-Z0-9<]{25,}$/.test(compact)\n })\n if (!mrzLines.length) return []\n if (field === 'person_name') {\n const line = mrzLines.find((candidate) => candidate.text.includes('<<'))\n if (!line) return []\n const compact = line.text.replace(/\\s/g, '')\n const nameStart = /^[PIV]<[A-Z]{3}/.test(compact) ? 5 : compact.indexOf('<') + 1\n const names = compact.slice(nameStart).replace(/<+/g, ' ').trim()\n return names ? [semanticMatch(ruleId, 'mrz', line.words, names, 0.96)] : []\n }\n if (field === 'date_of_birth') {\n for (const line of mrzLines) {\n const compact = line.text.replace(/\\s/g, '')\n const value = /^\\d{6}/.exec(compact)?.[0] ?? /^.{13}(\\d{6})/.exec(compact)?.[1]\n if (value) return [semanticMatch(ruleId, 'mrz', line.words, value, 0.96)]\n }\n }\n if (field === 'document_number') {\n const line = mrzLines[0]\n return [semanticMatch(ruleId, 'mrz', line.words, line.text, 0.9)]\n }\n return []\n}\n\nfunction yearFromDate(value: string) {\n const compact = value.replace(/\\s/g, '')\n const fourDigit = compact.match(/\\d{4}/)?.[0]\n if (fourDigit) return Number(fourDigit)\n const six = compact.match(/^\\d{2}/)?.[0]\n if (!six) return undefined\n const year = Number(six)\n return year > new Date().getFullYear() % 100 ? 1900 + year : 2000 + year\n}\n\nfunction layoutSemanticMatches(\n field: SemanticField,\n ruleId: string,\n words: OcrBox[],\n lines: OcrLine[],\n height: number,\n) {\n if (field === 'address') {\n const index = lines.findIndex((line) => /\\d/.test(line.text) && streetToken.test(line.text))\n if (index < 0) return []\n const selected = [...lines[index].words]\n const next = lines[index + 1]\n if (next && /(?:\\b[A-Z]{2}\\b.*\\d{5}|\\d{5})/i.test(next.text)) selected.push(...next.words)\n return [semanticMatch(ruleId, 'layout', selected, undefined, 0.76)]\n }\n if (field === 'person_name') {\n const candidates = lines.map((line) => {\n const alphaWords = line.words.filter((word) => /^[A-ZÀ-ÖØ-öø-ÿ'-]+$/i.test(word.text))\n const y = line.bbox.y / Math.max(1, height)\n const valid = alphaWords.length >= 2 && alphaWords.length <= 5 &&\n alphaWords.length === line.words.length && !headerToken.test(line.text) &&\n !streetToken.test(line.text) && !line.text.includes('<')\n const uppercase = line.text.replace(/[^A-Z]/g, '').length / Math.max(1, line.text.replace(/[^A-Za-z]/g, '').length)\n return { line, score: valid ? 0.45 + averageConfidence(line.words) * 0.3 + uppercase * 0.15 + (y < 0.65 ? 0.1 : 0) : 0 }\n }).filter((candidate) => candidate.score >= 0.68).sort((a, b) => b.score - a.score)\n if (!candidates.length || (candidates[1] && candidates[0].score - candidates[1].score < 0.04)) return []\n return [semanticMatch(ruleId, 'layout', candidates[0].line.words, undefined, candidates[0].score)]\n }\n if (field === 'date_of_birth' || field === 'expiration_date') {\n const currentYear = new Date().getFullYear()\n const candidates = words.filter((word) => dateToken.test(word.text.replace(/\\s/g, '')))\n .map((word) => ({ word, year: yearFromDate(word.text) }))\n .filter((candidate) => candidate.year !== undefined)\n const filtered = field === 'date_of_birth'\n ? candidates.filter((candidate) => candidate.year! <= currentYear - 14 && candidate.year! >= currentYear - 120)\n : candidates.filter((candidate) => candidate.year! >= currentYear - 1)\n filtered.sort((a, b) => field === 'date_of_birth' ? a.year! - b.year! : b.year! - a.year!)\n if (!filtered.length) return []\n return [semanticMatch(ruleId, 'layout', [filtered[0].word], undefined, 0.72)]\n }\n if (field === 'postal_code') {\n const candidate = words.find((word) => semanticTextIsValid(field, word.text))\n return candidate ? [semanticMatch(ruleId, 'layout', [candidate], undefined, 0.78)] : []\n }\n if (field === 'state') {\n const candidates = words.filter((word) => semanticTextIsValid(field, word.text))\n return candidates.length === 1 ? [semanticMatch(ruleId, 'layout', candidates, undefined, 0.7)] : []\n }\n if (field === 'document_number') {\n const candidates = words.filter((word) => semanticTextIsValid(field, word.text))\n candidates.sort((a, b) => b.text.length - a.text.length)\n return candidates.length ? [semanticMatch(ruleId, 'layout', [candidates[0]], undefined, 0.7)] : []\n }\n return []\n}\n\nfunction intersectionRatio(box: Box, word: Box) {\n const area =\n Math.max(0, Math.min(box.x + box.width, word.x + word.width) - Math.max(box.x, word.x)) *\n Math.max(0, Math.min(box.y + box.height, word.y + word.height) - Math.max(box.y, word.y))\n return area / Math.max(1, word.width * word.height)\n}\n\nfunction logicalSemanticMatches(\n rule: SemanticRedactionRule,\n words: OcrBox[],\n lines: OcrLine[],\n height: number,\n) {\n const aliases = aliasSemanticMatches(rule.field, rule.id, lines)\n const mrz = mrzSemanticMatches(rule.field, rule.id, lines)\n if (aliases.length || mrz.length) return [...aliases, ...mrz]\n return layoutSemanticMatches(rule.field, rule.id, words, lines, height)\n}\n\nfunction modelSemanticMatch(\n rule: SemanticRedactionRule,\n words: OcrBox[],\n detections: SemanticModelDetection[] | undefined,\n) {\n const modelCandidates = (detections ?? [])\n .filter((detection) => detection.field === rule.field)\n .sort((a, b) => b.confidence - a.confidence)\n for (const detection of modelCandidates) {\n const selected = words.filter((word) => intersectionRatio(detection.bbox, word.bbox) >= 0.3)\n if (!selected.length) continue\n const text = selected.map((word) => word.text).join(' ')\n if (!semanticTextIsValid(rule.field, text)) continue\n return semanticMatch(rule.id, 'model', selected, text, detection.confidence)\n }\n return undefined\n}\n\nfunction matchSemanticRule(\n rule: SemanticRedactionRule,\n words: OcrBox[],\n lines: OcrLine[],\n height: number,\n detections: SemanticModelDetection[] | undefined,\n modelRequested: boolean,\n) {\n const logical = logicalSemanticMatches(rule, words, lines, height)\n if (rule.detector !== 'model') return logical\n if (!modelRequested) return logical\n const modelMatch = modelSemanticMatch(rule, words, detections)\n return modelMatch ? [modelMatch] : []\n}\n\nfunction templateRect(field: TemplateRectField, templateId: string, width: number, height: number): RedactionMatch {\n const clamp = (value: number) => Math.max(0, Math.min(1, value))\n const x = clamp(field.rect.x)\n const y = clamp(field.rect.y)\n return {\n ruleId: `${templateId}.${field.id}`,\n strategy: 'template-rect',\n source: 'template',\n bbox: {\n x: x * width,\n y: y * height,\n width: Math.max(0, Math.min(1 - x, field.rect.width)) * width,\n height: Math.max(0, Math.min(1 - y, field.rect.height)) * height,\n },\n confidence: 1,\n }\n}\n\nexport function matchRedactionRules(\n ocr: OcrResult | undefined,\n options: MatchRedactionOptions,\n): RedactionMatchResult {\n const width = options.imageWidth ?? ocr?.imageWidth ?? 0\n const height = options.imageHeight ?? ocr?.imageHeight ?? 0\n const words = ocr?.words ?? []\n const lines = groupLines(words)\n const matches: RedactionMatch[] = []\n const unmatchedRuleIds: string[] = []\n for (const rule of options.rules) {\n const ruleMatches = rule.strategy === 'string'\n ? matchStringRule(rule, lines)\n : rule.strategy === 'label'\n ? matchLabelRule(rule, words, lines, width, height)\n : matchSemanticRule(\n rule,\n words,\n lines,\n height,\n options.semanticDetections,\n options.modelRequested === true,\n )\n if (ruleMatches.length) matches.push(...ruleMatches)\n else unmatchedRuleIds.push(rule.id)\n }\n for (const field of options.template?.fields ?? []) {\n const ruleId = `${options.template!.id}.${field.id}`\n if (field.strategy === 'rect') {\n if (width && height) matches.push(templateRect(field, options.template!.id, width, height))\n else unmatchedRuleIds.push(ruleId)\n continue\n }\n const anchorRule: LabelRedactionRule = {\n ...field,\n id: ruleId,\n strategy: 'label',\n }\n const fieldMatches = matchLabelRule(anchorRule, words, lines, width, height, 'template-anchor')\n if (fieldMatches.length) matches.push(...fieldMatches)\n else unmatchedRuleIds.push(ruleId)\n }\n const expected = options.rules.length + (options.template?.fields.length ?? 0)\n const matchedIds = new Set(matches.map((match) => match.ruleId))\n const confidence = expected\n ? [...matchedIds].reduce((total, id) => {\n const items = matches.filter((match) => match.ruleId === id)\n return total + Math.max(...items.map((match) => match.confidence))\n }, 0) / expected\n : 1\n return {\n matches,\n unmatchedRuleIds,\n complete: unmatchedRuleIds.length === 0,\n confidence,\n }\n}\n\nfunction needsOcr(options: ProgrammaticRedactionOptions) {\n return options.rules.length > 0 ||\n options.template?.fields.some((field) => field.strategy === 'anchor') === true\n}\n\nexport function redactionStatus(\n summary: RedactionMatchResult,\n requireAll = false,\n): ProgrammaticRedactionResult['status'] {\n if (requireAll && !summary.complete) return 'unmatched'\n return summary.complete ? 'success' : 'partial'\n}\n\n/**\n * Matches rules and paints a new canvas.\n * Returns `success`, `partial`, or `unmatched`. An unmatched rule does not throw.\n *\n * Omit `ocr` to run OCR with `includeWords: true`. An `ocr` object without\n * `words` is used as-is, and OCR does not re-run.\n * Face blackout is `models.face` on this call.\n *\n * @throws {LicenseError} LicenseError when the redaction feature is not licensed. Matching does not start.\n * @throws {ScannerError} ScannerError `canvas` when canvas 2D is unavailable.\n */\nexport async function redactProgrammatically(\n image: ImageSource,\n options: ProgrammaticRedactionOptions,\n): Promise<ProgrammaticRedactionResult> {\n await assertLicense('redaction')\n const canvas = await sourceToCanvas(image, { cloneCanvas: false })\n let ocr = options.ocr\n if (needsOcr(options) && !ocr) {\n ocr = await recognizeText(canvas, { includeWords: true })\n }\n let semanticDetections = options.semanticDetections\n let modelRequested = options.modelRequested === true\n let modelStatus: ProgrammaticRedactionResult['modelStatus'] = 'not-requested'\n let modelError: string | undefined\n if (options.models?.fields) {\n try {\n semanticDetections = await detectFieldModel(\n canvas,\n options.models.fields,\n options.models.fieldClasses,\n options.models.confidence,\n options.models.fieldLayout,\n )\n modelRequested = true\n modelStatus = 'loaded'\n } catch (error) {\n modelError = error instanceof Error ? error.message : 'The field model could not load.'\n const allowFallback = options.models.allowHeuristicFallback !== false\n modelRequested = !allowFallback\n modelStatus = allowFallback ? 'fallback' : 'failed'\n }\n }\n const summary = matchRedactionRules(ocr, {\n rules: options.rules,\n template: options.template,\n imageWidth: canvas.width,\n imageHeight: canvas.height,\n semanticDetections,\n modelRequested,\n })\n const status = redactionStatus(summary, options.requireAll)\n let faces: FaceDetection[] = []\n let faceStatus: ProgrammaticRedactionResult['faceStatus'] = 'not-requested'\n let faceError: string | undefined\n if (options.models?.face) {\n try {\n faces = await detectFaces(canvas, options.models.face, options.models.faceConfidence)\n faceStatus = 'loaded'\n } catch (error) {\n faceError = error instanceof Error ? error.message : 'The face model could not load.'\n faceStatus = 'failed'\n }\n }\n if (status === 'unmatched') {\n return { ...summary, status, ocr, modelStatus, modelError, faces, faceStatus, faceError }\n }\n const output: RedactResult = await applyRedactions(\n canvas,\n [\n ...summary.matches.map((match) => ({ ...match.bbox, mode: options.style?.mode })),\n ...faces.map((face) => ({ ...face.bbox, mode: options.style?.mode })),\n ],\n options.style,\n )\n return {\n ...summary,\n status,\n ocr,\n canvas: output.canvas,\n blob: output.blob,\n modelStatus,\n modelError,\n faces,\n faceStatus,\n faceError,\n }\n}\n","import {\n assignFieldSource,\n emptyFieldSources,\n parseAamvaDate,\n parseAamvaPayload,\n type DriverLicenseFieldKey,\n type DriverLicenseFieldSource,\n type DriverLicenseFields,\n type ScanUseCase,\n} from './aamva'\nimport { decodePdf417, Pdf417DecoderError, preparePdf417Decoder } from './barcode'\nimport { assertLicense } from './license'\nimport { recognizeText } from './ocr'\nimport { matchRedactionRules } from './programmatic-redaction'\nimport { cropDocument, scanDocument, type ScanOptions } from './scan'\nimport type { DocumentDetectionResult, DocumentQuad, OcrResult } from './types'\n\nexport type { DriverLicenseFieldKey, DriverLicenseFieldSource, DriverLicenseFields, ScanUseCase }\n\nexport type DriverLicenseStatus = 'success' | 'frontFailed' | 'backFailed' | 'barcodeUnreadable'\n\nexport interface DriverLicenseResult {\n status: DriverLicenseStatus\n fields: DriverLicenseFields\n rawElements: Record<string, string>\n sources: Partial<Record<DriverLicenseFieldKey, DriverLicenseFieldSource>>\n front: { canvas?: HTMLCanvasElement; ocr?: OcrResult; quad?: DocumentQuad }\n back: { canvas?: HTMLCanvasElement; barcodeText?: string }\n message?: string\n}\n\nexport type DriverLicenseStep =\n | { kind: 'need-back'; front: DriverLicenseResult['front'] }\n | { kind: 'retry'; status: 'frontFailed' | 'backFailed'; message: string }\n | { kind: 'complete'; result: DriverLicenseResult }\n\nexport interface DriverLicenseSessionOptions {\n scan?: ScanOptions\n onDriverLicense?: (result: DriverLicenseResult) => void | Promise<void>\n onCancel?: () => void\n}\n\nexport interface DriverLicenseSession {\n readonly side: 'front' | 'back'\n submitStill(\n frame: HTMLCanvasElement,\n detection?: DocumentDetectionResult,\n ): Promise<DriverLicenseStep>\n cancel(): void\n}\n\nconst OCR_FIELD_RULES = [\n { id: 'issuerJurisdiction', strategy: 'semantic' as const, field: 'state' as const },\n { id: 'expirationDate', strategy: 'semantic' as const, field: 'expiration_date' as const },\n { id: 'documentNumber', strategy: 'semantic' as const, field: 'document_number' as const },\n { id: 'dateOfBirth', strategy: 'semantic' as const, field: 'date_of_birth' as const },\n { id: 'addressStreet', strategy: 'semantic' as const, field: 'address' as const },\n { id: 'addressPostalCode', strategy: 'semantic' as const, field: 'postal_code' as const },\n { id: 'personName', strategy: 'semantic' as const, field: 'person_name' as const },\n]\n\nfunction applyOcrValue(fields: DriverLicenseFields, key: DriverLicenseFieldKey, value: string) {\n const trimmed = value.trim()\n if (!trimmed) return\n if (key === 'expirationDate' || key === 'dateOfBirth' || key === 'issueDate') {\n fields[key] = parseAamvaDate(trimmed, key === 'dateOfBirth') ?? trimmed\n return\n }\n if (key === 'issuerJurisdiction') {\n fields[key] = trimmed.toUpperCase()\n return\n }\n fields[key] = trimmed\n}\n\nexport function driverLicenseFieldsFromOcr(ocr: OcrResult): DriverLicenseFields {\n const fields: DriverLicenseFields = {}\n const matched = matchRedactionRules(ocr, { rules: OCR_FIELD_RULES })\n for (const match of matched.matches) {\n const text = match.text?.trim()\n if (!text) continue\n if (match.ruleId === 'personName') {\n const parts = text.split(/\\s+/).filter(Boolean)\n if (!fields.firstName && parts[0]) fields.firstName = parts[0]\n if (!fields.lastName && parts.length > 1) fields.lastName = parts[parts.length - 1]\n if (!fields.middleName && parts.length > 2) {\n fields.middleName = parts.slice(1, -1).join(' ')\n }\n continue\n }\n applyOcrValue(fields, match.ruleId as DriverLicenseFieldKey, text)\n }\n return fields\n}\n\nexport function mergeDriverLicenseFields(\n barcode: DriverLicenseFields,\n ocr: DriverLicenseFields,\n): { fields: DriverLicenseFields; sources: DriverLicenseResult['sources'] } {\n const fields: DriverLicenseFields = { ...barcode }\n const sources = assignFieldSource(emptyFieldSources(), barcode, 'barcode')\n for (const [key, value] of Object.entries(ocr) as Array<[DriverLicenseFieldKey, string | undefined]>) {\n if (!value) continue\n if (fields[key]) continue\n fields[key] = value\n sources[key] = 'ocr'\n }\n return { fields, sources }\n}\n\nasync function cropIfPossible(\n frame: HTMLCanvasElement,\n detection: DocumentDetectionResult | undefined,\n scan: ScanOptions | undefined,\n) {\n if (detection?.status === 'success' && detection.corners) {\n return cropDocument(frame, detection.corners, {\n maxOutputDimension: scan?.maxOutputDimension,\n })\n }\n return scanDocument(frame, scan)\n}\n\nexport async function processDriverLicenseFront(\n frame: HTMLCanvasElement,\n detection?: DocumentDetectionResult,\n scan?: ScanOptions,\n): Promise<Extract<DriverLicenseStep, { kind: 'need-back' | 'retry' }>> {\n if (!frame.width || !frame.height) {\n return { kind: 'retry', status: 'frontFailed', message: 'The front photo was empty. Try again.' }\n }\n const cropped = await cropIfPossible(frame, detection, scan)\n if (cropped.status !== 'success' || !cropped.canvas) {\n return {\n kind: 'retry',\n status: 'frontFailed',\n message: cropped.message || 'The front of the license was not found. Try again.',\n }\n }\n let ocr: OcrResult | undefined\n try {\n ocr = await recognizeText(cropped.canvas, { includeWords: true })\n } catch {\n ocr = undefined\n }\n return {\n kind: 'need-back',\n front: {\n canvas: cropped.canvas,\n ocr,\n quad: cropped.quad,\n },\n }\n}\n\nfunction abortError() {\n return new DOMException('The barcode scan was cancelled.', 'AbortError')\n}\n\nfunction sleep(ms: number, signal?: AbortSignal) {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(abortError())\n return\n }\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timer)\n reject(abortError())\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\n/** Live back polls this often when the host does not pass `detectionIntervalMs`. */\nexport const LICENSE_BACK_POLL_MS = 140\n\n/** Green barcode box stays up this long before `onDriverLicense`. */\nexport const LICENSE_BACK_HIT_MS = 280\n\nexport type DriverLicensePromptSide = 'front' | 'back' | 'flip'\n\nexport interface Pdf417FrameHit {\n frame: HTMLCanvasElement\n quad?: DocumentQuad\n}\n\n/**\n * Decode PDF417 from successive live frames until one succeeds.\n * Does not wait for a document quad.\n */\nexport async function waitForPdf417Frame(\n grabFrame: () => HTMLCanvasElement | undefined | Promise<HTMLCanvasElement | undefined>,\n options: { intervalMs?: number; signal?: AbortSignal } = {},\n): Promise<Pdf417FrameHit> {\n const intervalMs = options.intervalMs ?? LICENSE_BACK_POLL_MS\n await preparePdf417Decoder()\n for (;;) {\n if (options.signal?.aborted) throw abortError()\n try {\n const frame = await grabFrame()\n if (frame?.width && frame.height) {\n const decoded = await decodePdf417(frame)\n if (decoded?.text) return { frame, quad: decoded.quad }\n }\n } catch (cause) {\n if (options.signal?.aborted) throw abortError()\n if (cause instanceof DOMException && cause.name === 'AbortError') throw cause\n if (cause instanceof Pdf417DecoderError) throw cause\n }\n await sleep(intervalMs, options.signal)\n }\n}\n\nexport async function processDriverLicenseBack(\n frame: HTMLCanvasElement,\n front: DriverLicenseResult['front'],\n): Promise<DriverLicenseResult> {\n if (!frame.width || !frame.height) {\n return {\n status: 'backFailed',\n fields: {},\n rawElements: {},\n sources: {},\n front,\n back: {},\n message: 'The back photo was empty. Try again.',\n }\n }\n const barcodeText = (await decodePdf417(frame))?.text\n const back = { canvas: frame, barcodeText }\n if (!barcodeText) {\n const ocrFields = front.ocr ? driverLicenseFieldsFromOcr(front.ocr) : {}\n return {\n status: 'barcodeUnreadable',\n fields: ocrFields,\n rawElements: {},\n sources: assignFieldSource(emptyFieldSources(), ocrFields, 'ocr'),\n front,\n back,\n message: 'The PDF417 barcode on the back could not be read. Try again.',\n }\n }\n const parsed = parseAamvaPayload(barcodeText)\n const ocrFields = front.ocr ? driverLicenseFieldsFromOcr(front.ocr) : {}\n const merged = mergeDriverLicenseFields(parsed.fields, ocrFields)\n return {\n status: 'success',\n fields: merged.fields,\n rawElements: parsed.rawElements,\n sources: merged.sources,\n front,\n back,\n }\n}\n\n/** Front and back use the finder default (1s Hold still). */\nexport function driverLicenseHoldStillMs(_side: 'front' | 'back') {\n return undefined\n}\n\nexport function driverLicensePrompt(side: DriverLicensePromptSide, usingSystemCamera: boolean) {\n if (side === 'front') {\n return {\n title: 'Scan a driver’s license',\n headline: 'Show the front of the license.',\n detail: usingSystemCamera\n ? 'Take a photo of the front. There is no live document guide.'\n : 'Place the front of the license in the frame.',\n }\n }\n if (side === 'flip') {\n return {\n title: 'Scan a driver’s license',\n headline: 'Turn your license over and scan the back.',\n detail: usingSystemCamera\n ? 'The PDF417 barcode is on the back. Tap Continue, then take a photo of the entire code.'\n : 'The PDF417 barcode is on the back. Tap Continue, then hold the whole code in view.',\n }\n }\n return {\n title: 'Scan a driver’s license',\n headline: 'Hold the whole barcode in view',\n detail: usingSystemCamera\n ? 'Take a photo of the entire PDF417 barcode. We outline the card and the barcode after the photo.'\n : 'Keep the entire PDF417 in the preview. Closer is fine if the whole code is visible.',\n }\n}\n\n/**\n * Two-sided US/Canada AAMVA session. Front is find + OCR. Back is PDF417\n * only — no document finder or DocAligner crop. Barcode wins; front OCR\n * fills empty fields only.\n *\n * @throws {LicenseError} when `scan` or `ocr` is not licensed.\n */\nexport async function startDriverLicenseSession(\n options: DriverLicenseSessionOptions = {},\n): Promise<DriverLicenseSession> {\n await assertLicense('scan')\n await assertLicense('ocr')\n let side: 'front' | 'back' = 'front'\n let front: DriverLicenseResult['front'] | undefined\n let closed = false\n\n const session: DriverLicenseSession = {\n get side() {\n return side\n },\n async submitStill(frame, detection) {\n if (closed) {\n return {\n kind: 'retry',\n status: side === 'back' ? 'backFailed' : 'frontFailed',\n message: 'This license scan was cancelled.',\n }\n }\n if (side === 'front') {\n const step = await processDriverLicenseFront(frame, detection, options.scan)\n if (step.kind === 'need-back') {\n front = step.front\n side = 'back'\n }\n return step\n }\n const result = await processDriverLicenseBack(frame, front ?? {})\n if (result.status === 'backFailed') {\n return {\n kind: 'retry',\n status: 'backFailed',\n message: result.message ?? 'The back of the license could not be captured. Try again.',\n }\n }\n closed = true\n if (typeof options.onDriverLicense === 'function') {\n await options.onDriverLicense(result)\n }\n return { kind: 'complete', result }\n },\n cancel() {\n closed = true\n options.onCancel?.()\n },\n }\n return session\n}\n"],"mappings":"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"}
|