export type ReaderSize = { width: number; height: number; }; export const READER_ZOOM_MIN = 50; export const READER_ZOOM_MAX = 300; export const READER_ZOOM_STEP = 25; export const READER_ZOOM_DEFAULT = 100; export function clampReaderZoom(zoom: number) { const steppedZoom = Math.round(zoom / READER_ZOOM_STEP) * READER_ZOOM_STEP; return Math.max(READER_ZOOM_MIN, Math.min(steppedZoom, READER_ZOOM_MAX)); } export function readerZoomLabel(zoom: number) { return `${clampReaderZoom(zoom)} %`; } export function readerZoomFactor(zoom: number) { return clampReaderZoom(zoom) / 100; } export function clampReaderPage(page: number, pageCount: number) { const safePageCount = Math.max(1, Math.floor(pageCount)); return Math.max(1, Math.min(Math.floor(page), safePageCount)); } export function previousReaderPage(page: number, pageCount: number) { return clampReaderPage(page - 1, pageCount); } export function nextReaderPage(page: number, pageCount: number) { return clampReaderPage(page + 1, pageCount); } export function readerPositionLabel(page: number, pageCount: number) { return `Page ${clampReaderPage(page, pageCount)} / ${Math.max(1, Math.floor(pageCount))}`; } export function containPageSize(viewport: ReaderSize, page: ReaderSize): ReaderSize { if (viewport.width <= 0 || viewport.height <= 0 || page.width <= 0 || page.height <= 0) { return { width: 0, height: 0 }; } const scale = Math.min(viewport.width / page.width, viewport.height / page.height); return { width: Math.max(1, Math.floor(page.width * scale)), height: Math.max(1, Math.floor(page.height * scale)) }; } export function orientedContainPageSize(viewport: ReaderSize, page: ReaderSize): ReaderSize { if (viewport.width <= 0 || viewport.height <= 0 || page.width <= 0 || page.height <= 0) { return { width: 0, height: 0 }; } const fitByHeight = page.height >= page.width; const primaryScale = fitByHeight ? viewport.height / page.height : viewport.width / page.width; const crossScale = fitByHeight ? viewport.width / page.width : viewport.height / page.height; const scale = Math.min(primaryScale, crossScale); return { width: Math.max(1, Math.floor(page.width * scale)), height: Math.max(1, Math.floor(page.height * scale)) }; } export function zoomReaderSize(size: ReaderSize, zoom: number): ReaderSize { const factor = readerZoomFactor(zoom); return { width: Math.max(1, Math.floor(size.width * factor)), height: Math.max(1, Math.floor(size.height * factor)) }; } export function readableViewportSize(size: ReaderSize): ReaderSize | null { if (size.width < 32 || size.height < 32) return null; return { width: Math.floor(size.width), height: Math.floor(size.height) }; }