import type { ReaderSize } from "./readerLayout"; const PDF_SENTINEL_BACKGROUND = { red: 247, green: 240, blue: 223 }; const PDF_BACKGROUND_TOLERANCE = 5; const PDF_VISIBLE_DELTA = 10; const PDF_MIN_VISIBLE_PIXELS = 12; export type PdfRenderResult = "rendered-ok" | "blank-detected"; export function pdfRenderScale(frameSize: ReaderSize, pageSize: ReaderSize) { if (frameSize.width <= 0 || frameSize.height <= 0 || pageSize.width <= 0 || pageSize.height <= 0) return 1; return Math.min(frameSize.width / pageSize.width, frameSize.height / pageSize.height); } export function pdfCanvasVisible(pageRendered: boolean, hasPageError: boolean) { return pageRendered && !hasPageError; } function isNearSentinel(red: number, green: number, blue: number) { return ( Math.abs(red - PDF_SENTINEL_BACKGROUND.red) <= PDF_BACKGROUND_TOLERANCE && Math.abs(green - PDF_SENTINEL_BACKGROUND.green) <= PDF_BACKGROUND_TOLERANCE && Math.abs(blue - PDF_SENTINEL_BACKGROUND.blue) <= PDF_BACKGROUND_TOLERANCE ); } function isVisibleInk(red: number, green: number, blue: number, alpha: number) { if (alpha < 24) return false; if (isNearSentinel(red, green, blue)) return false; const distanceFromWhite = Math.max(255 - red, 255 - green, 255 - blue); return distanceFromWhite >= PDF_VISIBLE_DELTA; } export function pdfCanvasHasVisibleContent(imageData: Pick) { let visiblePixels = 0; const data = imageData.data; for (let index = 0; index < data.length; index += 4) { if (isVisibleInk(data[index] ?? 255, data[index + 1] ?? 255, data[index + 2] ?? 255, data[index + 3] ?? 255)) { visiblePixels += 1; if (visiblePixels >= PDF_MIN_VISIBLE_PIXELS) return true; } } return false; } export function classifyPdfCanvas(imageData: Pick): PdfRenderResult { return pdfCanvasHasVisibleContent(imageData) ? "rendered-ok" : "blank-detected"; } export function pdfSentinelBackground() { return `rgb(${PDF_SENTINEL_BACKGROUND.red}, ${PDF_SENTINEL_BACKGROUND.green}, ${PDF_SENTINEL_BACKGROUND.blue})`; }