import * as fs from 'fs/promises'; import * as os from 'os'; import * as path from 'path'; import { PDFDocument, degrees } from 'pdf-lib'; import type { InboxDocument } from '../database/entities/inbox-document.entity'; /** * Wendet die virtuellen Edits (DeletedPages, Rotations) auf das Original-PDF an * und schreibt das Ergebnis in eine temporäre Datei. Gibt den Pfad zurück. * Aufrufer ist verantwortlich für das Aufräumen. */ export async function applyEditsToTemp( doc: InboxDocument, pdfPath: string, ): Promise { const bytes = await fs.readFile(pdfPath); const pdf = await PDFDocument.load(bytes, { ignoreEncryption: true }); const rotations = doc.Rotations ?? {}; for (const [pageStr, rot] of Object.entries(rotations)) { const pageNum = Number(pageStr); if (!Number.isInteger(pageNum)) continue; const idx = pageNum - 1; if (idx < 0 || idx >= pdf.getPageCount()) continue; const normalized = ((Math.round(rot / 90) * 90) % 360 + 360) % 360; if (normalized === 0) continue; pdf.getPage(idx).setRotation(degrees(normalized)); } // Seiten in absteigender Reihenfolge entfernen, damit Indizes stabil bleiben const deleted = [...(doc.DeletedPages ?? [])].sort((a, b) => b - a); for (const pageNum of deleted) { const idx = pageNum - 1; if (idx < 0 || idx >= pdf.getPageCount()) continue; pdf.removePage(idx); } const out = await pdf.save(); const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'inbox-pp-')); const tmpPath = path.join(tmpDir, 'document.pdf'); await fs.writeFile(tmpPath, out); return tmpPath; } export async function cleanupTemp(filePath: string | null): Promise { if (!filePath) return; try { await fs.unlink(filePath); await fs.rmdir(path.dirname(filePath)).catch(() => undefined); } catch { // ignore } } /** * Baut ein PDF aus einer Teilmenge von Originalseiten auf (rotiert, keine gelöschten Seiten). * segmentPages enthält 1-basierte Originalseitenzahlen; gelöschte Seiten müssen vom Aufrufer * bereits herausgefiltert worden sein. */ export async function buildSegmentBuffer( doc: InboxDocument, pdfPath: string, segmentPages: number[], ): Promise { const bytes = await fs.readFile(pdfPath); const srcPdf = await PDFDocument.load(bytes, { ignoreEncryption: true }); const outPdf = await PDFDocument.create(); const rotations = doc.Rotations ?? {}; const indices = segmentPages.map((p) => p - 1); const copied = await outPdf.copyPages(srcPdf, indices); copied.forEach((page, i) => { const rot = rotations[String(segmentPages[i])]; if (rot !== undefined) { const normalized = ((Math.round(rot / 90) * 90) % 360 + 360) % 360; if (normalized !== 0) page.setRotation(degrees(normalized)); } outPdf.addPage(page); }); return Buffer.from(await outPdf.save()); } export async function extractSectionToTemp( pdfPath: string, pageIndices: number[], ): Promise { const bytes = await fs.readFile(pdfPath); const srcPdf = await PDFDocument.load(bytes, { ignoreEncryption: true }); const outPdf = await PDFDocument.create(); const copied = await outPdf.copyPages(srcPdf, pageIndices); copied.forEach(p => outPdf.addPage(p)); const out = await outPdf.save(); const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'inbox-section-')); const tmpPath = path.join(tmpDir, 'section.pdf'); await fs.writeFile(tmpPath, out); return tmpPath; }