Archived
feat: pdf reader
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
export type PdfRect = [number, number, number, number];
|
||||
|
||||
export interface ParsedPdfAnchor {
|
||||
page: number;
|
||||
rects: PdfRect[];
|
||||
}
|
||||
|
||||
const PDF_ANCHOR_PREFIX = "pdf:v1:";
|
||||
|
||||
export function isPdfAnchor(anchor: string): boolean {
|
||||
return anchor.startsWith(PDF_ANCHOR_PREFIX) || anchor.startsWith("pdf:page:");
|
||||
}
|
||||
|
||||
export function parsePageHref(href: string): number | null {
|
||||
const match = href.match(/^pdf:page:(\d+)$/);
|
||||
if (!match) return null;
|
||||
const page = Number(match[1]);
|
||||
return Number.isFinite(page) && page > 0 ? page : null;
|
||||
}
|
||||
|
||||
export function serializePdfAnchor(page: number, rects?: PdfRect[]): string {
|
||||
const base = `${PDF_ANCHOR_PREFIX}p=${page}`;
|
||||
if (!rects?.length) return base;
|
||||
return `${base};rects=${encodeURIComponent(JSON.stringify(rects))}`;
|
||||
}
|
||||
|
||||
export function parsePdfAnchor(anchor: string): ParsedPdfAnchor | null {
|
||||
if (anchor.startsWith("pdf:page:")) {
|
||||
const page = parsePageHref(anchor);
|
||||
return page ? { page, rects: [] } : null;
|
||||
}
|
||||
if (!anchor.startsWith(PDF_ANCHOR_PREFIX)) return null;
|
||||
|
||||
const pageMatch = anchor.match(/p=(\d+)/);
|
||||
if (!pageMatch) return null;
|
||||
const page = Number(pageMatch[1]);
|
||||
if (!Number.isFinite(page) || page < 1) return null;
|
||||
|
||||
const rectsMatch = anchor.match(/rects=([^;]+)/);
|
||||
let rects: PdfRect[] = [];
|
||||
if (rectsMatch) {
|
||||
try {
|
||||
const parsed = JSON.parse(decodeURIComponent(rectsMatch[1])) as unknown;
|
||||
if (Array.isArray(parsed)) {
|
||||
rects = parsed.filter(
|
||||
(r): r is PdfRect =>
|
||||
Array.isArray(r) &&
|
||||
r.length === 4 &&
|
||||
r.every((n) => typeof n === "number" && Number.isFinite(n)),
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
rects = [];
|
||||
}
|
||||
}
|
||||
|
||||
return { page, rects };
|
||||
}
|
||||
|
||||
export function rectsFromSelection(
|
||||
range: Range,
|
||||
pageElement: HTMLElement,
|
||||
): PdfRect[] {
|
||||
const pageRect = pageElement.getBoundingClientRect();
|
||||
if (pageRect.width <= 0 || pageRect.height <= 0) return [];
|
||||
|
||||
const rects: PdfRect[] = [];
|
||||
for (const clientRect of range.getClientRects()) {
|
||||
if (clientRect.width <= 0 || clientRect.height <= 0) continue;
|
||||
const x = (clientRect.left - pageRect.left) / pageRect.width;
|
||||
const y = (clientRect.top - pageRect.top) / pageRect.height;
|
||||
const w = clientRect.width / pageRect.width;
|
||||
const h = clientRect.height / pageRect.height;
|
||||
rects.push([
|
||||
Math.max(0, Math.min(1, x)),
|
||||
Math.max(0, Math.min(1, y)),
|
||||
Math.max(0, Math.min(1, w)),
|
||||
Math.max(0, Math.min(1, h)),
|
||||
]);
|
||||
}
|
||||
return rects;
|
||||
}
|
||||
Reference in New Issue
Block a user