fix(web): lecteur PDF — pages blanches après rendu et base minimale stable
Le canvas PDF restait vide après rendu (blank-detected): la détection d'encre reposait sur un seuil de blanc trop sensible et un fond de canvas non maîtrisé. Le rendu passe à un fond sentinel explicite avec classification rendered-ok/blank-detected et un seuil de pixels visibles. Le lecteur est par ailleurs ramené à une base minimale stable: layout plein viewport (app-main-reader), suppression des modes de lecture et des préférences par livre, helpers de pagination/viewport extraits dans readerLayout.ts. Shell, lecteurs CBZ/EPUB et styles alignés sur cette base. Refs #31 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -1,34 +1,52 @@
|
||||
import type { ReaderMode } from "../api/types";
|
||||
import type { ReaderSize } from "./readerLayout";
|
||||
|
||||
const PDF_RENDER_BACKGROUND = { red: 247, green: 240, blue: 223 };
|
||||
const PDF_BLANK_THRESHOLD = 245;
|
||||
const PDF_BACKGROUND_TOLERANCE = 4;
|
||||
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 function pdfRenderScale(mode: ReaderMode, frameSize: { width: number; height: number }, pageSize: { width: number; height: number }) {
|
||||
if (pageSize.width <= 0 || pageSize.height <= 0) return 1;
|
||||
const fitWidth = frameSize.width / pageSize.width;
|
||||
if (mode === "vertical") return Math.min(1.65, fitWidth);
|
||||
return Math.min(fitWidth, frameSize.height / pageSize.height);
|
||||
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);
|
||||
}
|
||||
|
||||
function isNearRenderBackground(red: number, green: number, blue: number) {
|
||||
export function pdfCanvasVisible(pageRendered: boolean, hasPageError: boolean) {
|
||||
return pageRendered && !hasPageError;
|
||||
}
|
||||
|
||||
function isNearSentinel(red: number, green: number, blue: number) {
|
||||
return (
|
||||
Math.abs(red - PDF_RENDER_BACKGROUND.red) <= PDF_BACKGROUND_TOLERANCE &&
|
||||
Math.abs(green - PDF_RENDER_BACKGROUND.green) <= PDF_BACKGROUND_TOLERANCE &&
|
||||
Math.abs(blue - PDF_RENDER_BACKGROUND.blue) <= PDF_BACKGROUND_TOLERANCE
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
export function pdfCanvasHasVisibleInk(imageData: Pick<ImageData, "data">) {
|
||||
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<ImageData, "data">) {
|
||||
let visiblePixels = 0;
|
||||
const data = imageData.data;
|
||||
for (let index = 0; index < data.length; index += 4) {
|
||||
const red = data[index] ?? 255;
|
||||
const green = data[index + 1] ?? 255;
|
||||
const blue = data[index + 2] ?? 255;
|
||||
const alpha = data[index + 3] ?? 255;
|
||||
if (alpha === 0) continue;
|
||||
const nearWhite = red >= PDF_BLANK_THRESHOLD && green >= PDF_BLANK_THRESHOLD && blue >= PDF_BLANK_THRESHOLD;
|
||||
if (!nearWhite && !isNearRenderBackground(red, green, blue)) return true;
|
||||
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<ImageData, "data">): 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})`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user