export interface PdfPoint { x: number; y: number } export interface ScreenPoint { x: number; y: number } export interface PdfRect { x: number; y: number; width: number; height: number } export interface ScreenRect { x: number; y: number; width: number; height: number } export interface ViewportParams { scale: number; // CSS zoom level (1.0 = 100%) rotation: number; // 0, 90, 180, 270 (clockwise) canonicalWidth: number; // Unrotated CropBox width in PDF points canonicalHeight: number;// Unrotated CropBox height in PDF points dpr?: number; // Backing-store density; never part of CSS geometry } export function pdfToScreen(p: PdfPoint, vp: ViewportParams): ScreenPoint { // Screen points are CSS pixels. Device-pixel-ratio only controls the PDF.js // backing canvas; including it here would make Fabric objects overflow the // CSS overlay on retina displays. const s = vp.scale; const { rotation, canonicalWidth: W, canonicalHeight: H } = vp; const { x, y } = p; const rot = ((rotation % 360) + 360) % 360; switch (rot) { case 0: return { x: x * s, y: y * s }; case 90: return { x: (H - y) * s, y: x * s }; case 180: return { x: (W - x) * s, y: (H - y) * s }; case 270: return { x: y * s, y: (W - x) * s }; default: throw new Error(`Invalid rotation: ${rotation}`); } } export function screenToPdf(p: ScreenPoint, vp: ViewportParams): PdfPoint { const s = vp.scale; const { rotation, canonicalWidth: W, canonicalHeight: H } = vp; const sx = p.x / s; const sy = p.y / s; const rot = ((rotation % 360) + 360) % 360; switch (rot) { case 0: return { x: sx, y: sy }; case 90: return { x: sy, y: H - sx }; case 180: return { x: W - sx, y: H - sy }; case 270: return { x: W - sy, y: sx }; default: throw new Error(`Invalid rotation: ${rotation}`); } } export function pdfRectToScreen(r: PdfRect, vp: ViewportParams): ScreenRect { const p1 = pdfToScreen({ x: r.x, y: r.y }, vp); const p2 = pdfToScreen({ x: r.x + r.width, y: r.y + r.height }, vp); return { x: Math.min(p1.x, p2.x), y: Math.min(p1.y, p2.y), width: Math.abs(p2.x - p1.x), height: Math.abs(p2.y - p1.y) }; } export function screenRectToPdf(r: ScreenRect, vp: ViewportParams): PdfRect { const p1 = screenToPdf({ x: r.x, y: r.y }, vp); const p2 = screenToPdf({ x: r.x + r.width, y: r.y + r.height }, vp); return { x: Math.min(p1.x, p2.x), y: Math.min(p1.y, p2.y), width: Math.abs(p2.x - p1.x), height: Math.abs(p2.y - p1.y) }; }