Paperjet/frontend/src/lib/coords.ts

73 lines
2.5 KiB
TypeScript

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)
};
}