163 lines
4.5 KiB
Svelte
163 lines
4.5 KiB
Svelte
<script lang="ts">
|
|
import { onDestroy } from 'svelte';
|
|
import { PDFDocument } from 'pdf-lib';
|
|
import FileDropzone from '$lib/components/FileDropzone.svelte';
|
|
import PdfPageGrid from '$lib/components/PdfPageGrid.svelte';
|
|
import Button from '$lib/components/ui/button.svelte';
|
|
import { downloadBlob } from '$lib/download';
|
|
import { loadPdfDocument, type PdfDocumentHandle } from '$lib/pdf-thumbnails';
|
|
|
|
let pdfFile: File | null = $state(null);
|
|
let pdfHandle: PdfDocumentHandle | null = $state(null);
|
|
let selectedPages = $state(new Set<number>());
|
|
let isProcessing = $state(false);
|
|
let error = $state<string | null>(null);
|
|
let loadingError = $state<string | null>(null);
|
|
|
|
const handleFileSelected = async (files: File[]) => {
|
|
const file = files[0];
|
|
if (!file) return;
|
|
|
|
pdfHandle?.destroy();
|
|
pdfHandle = null;
|
|
pdfFile = file;
|
|
selectedPages = new Set();
|
|
loadingError = null;
|
|
error = null;
|
|
|
|
try {
|
|
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
pdfHandle = await loadPdfDocument(bytes);
|
|
} catch (err) {
|
|
console.error('Failed to load PDF:', err);
|
|
loadingError = 'PDF konnte nicht geladen werden.';
|
|
pdfHandle = null;
|
|
}
|
|
};
|
|
|
|
const togglePage = (pageNumber: number) => {
|
|
const next = new Set(selectedPages);
|
|
if (next.has(pageNumber)) {
|
|
next.delete(pageNumber);
|
|
} else {
|
|
next.add(pageNumber);
|
|
}
|
|
selectedPages = next;
|
|
};
|
|
|
|
const selectAll = () => {
|
|
if (!pdfHandle) return;
|
|
const all = new Set<number>();
|
|
for (let i = 1; i <= pdfHandle.pageCount; i++) all.add(i);
|
|
selectedPages = all;
|
|
};
|
|
|
|
const selectNone = () => {
|
|
selectedPages = new Set();
|
|
};
|
|
|
|
const removePages = async () => {
|
|
if (!pdfFile || !pdfHandle || selectedPages.size === 0) return;
|
|
|
|
isProcessing = true;
|
|
error = null;
|
|
|
|
try {
|
|
const bytes = new Uint8Array(await pdfFile.arrayBuffer());
|
|
const sourcePdf = await PDFDocument.load(bytes);
|
|
const outputPdf = await PDFDocument.create();
|
|
|
|
const pagesToKeep: number[] = [];
|
|
for (let i = 0; i < sourcePdf.getPageCount(); i++) {
|
|
if (!selectedPages.has(i + 1)) {
|
|
pagesToKeep.push(i);
|
|
}
|
|
}
|
|
|
|
if (pagesToKeep.length === 0) {
|
|
error = 'Es muss mindestens eine Seite übrig bleiben.';
|
|
isProcessing = false;
|
|
return;
|
|
}
|
|
|
|
const copiedPages = await outputPdf.copyPages(sourcePdf, pagesToKeep);
|
|
copiedPages.forEach((page) => outputPdf.addPage(page));
|
|
|
|
const resultBytes = await outputPdf.save();
|
|
const baseName = pdfFile.name.replace(/\.pdf$/i, '');
|
|
downloadBlob(resultBytes, `${baseName}_ohne_seiten.pdf`);
|
|
} catch (err) {
|
|
console.error('Failed to remove pages:', err);
|
|
error = 'Fehler beim Entfernen der Seiten.';
|
|
} finally {
|
|
isProcessing = false;
|
|
}
|
|
};
|
|
|
|
onDestroy(() => {
|
|
pdfHandle?.destroy();
|
|
});
|
|
</script>
|
|
|
|
<div class="mx-auto max-w-4xl space-y-6">
|
|
<div>
|
|
<h1 class="text-2xl font-bold text-primary-900">Seiten entfernen</h1>
|
|
<p class="mt-2 text-sm text-primary-700">
|
|
Wählen Sie Seiten aus, die Sie aus dem PDF entfernen möchten.
|
|
</p>
|
|
</div>
|
|
|
|
{#if !pdfHandle}
|
|
<FileDropzone onFilesSelected={handleFileSelected} />
|
|
{#if loadingError}
|
|
<div class="mt-4 rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-800">
|
|
{loadingError}
|
|
</div>
|
|
{/if}
|
|
{:else}
|
|
<div class="space-y-4">
|
|
<FileDropzone
|
|
onFilesSelected={handleFileSelected}
|
|
label="Anderes PDF ablegen"
|
|
sublabel={pdfFile?.name ?? ''}
|
|
class="py-4"
|
|
/>
|
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
|
<h2 class="text-lg font-semibold text-primary-900">
|
|
{pdfFile?.name} — {pdfHandle.pageCount} Seite{pdfHandle.pageCount === 1 ? '' : 'n'}
|
|
</h2>
|
|
<div class="flex gap-2">
|
|
<Button variant="tertiary" class="min-w-0 px-3" onclick={selectAll}>
|
|
Alle auswählen
|
|
</Button>
|
|
<Button variant="tertiary" class="min-w-0 px-3" onclick={selectNone}>
|
|
Auswahl aufheben
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<PdfPageGrid thumbnails={pdfHandle.thumbnails} {selectedPages} onPageClick={togglePage} />
|
|
|
|
{#if error}
|
|
<div class="rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-800">
|
|
{error}
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="flex items-center justify-between">
|
|
<p class="text-sm text-primary-700">
|
|
{selectedPages.size} Seite{selectedPages.size === 1 ? '' : 'n'} zum Entfernen ausgewählt
|
|
</p>
|
|
<Button
|
|
onclick={removePages}
|
|
disabled={isProcessing ||
|
|
selectedPages.size === 0 ||
|
|
selectedPages.size >= pdfHandle.pageCount}
|
|
>
|
|
{isProcessing ? 'Wird verarbeitet...' : 'Seiten entfernen & herunterladen'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|