From fb2ce271fa6957cd507d7864e78ee2732351f7bc Mon Sep 17 00:00:00 2001 From: Steffen Becker Date: Thu, 18 Jun 2026 21:32:24 +0200 Subject: [PATCH] feat: raster pdf and vector pdf(beta) --- src/App.svelte | 138 ++++++++++++++++------------------- src/lib/export/pdf.ts | 166 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 225 insertions(+), 79 deletions(-) diff --git a/src/App.svelte b/src/App.svelte index 94d9561..228fa88 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -5,7 +5,7 @@ import Toolbar from './lib/editor/Toolbar.svelte'; import ToolbarExpanded from './lib/editor/ToolbarExpanded.svelte'; import { buildOdt, deriveFilename } from './lib/export/odt'; - import { exportPdf } from './lib/export/pdf'; + import { exportPdf, printPdf } from './lib/export/pdf'; import { supportsFsAccess, saveOdt, saveAsOdt, openOdt } from './lib/export/saveFile'; import { importOdt } from './lib/import/odt'; import { getPageBreakDebug } from './lib/editor/pageBreaks'; @@ -93,9 +93,9 @@ // reload restores the doc from localStorage but the first Save re-prompts. let fileHandle: FileSystemFileHandle | null = $state(null); const fsSupported = supportsFsAccess(); - let saveMenuOpen = $state(false); let fileInput: HTMLInputElement | null = $state(null); let pdfBusy = $state(false); + let exportMenuOpen = $state(false); // odf-kit export options for the current header/footer + page geometry. function hfOpts() { @@ -190,7 +190,7 @@ async function handleSave() { if (!editor) return; - saveMenuOpen = false; + exportMenuOpen = false; const json = editor.getJSON() as Parameters[0]; try { const bytes = await buildOdt(json, pageMargins, pageOrientation, hfOpts()); @@ -206,7 +206,7 @@ async function handleSaveAs() { if (!editor) return; - saveMenuOpen = false; + exportMenuOpen = false; const json = editor.getJSON() as Parameters[0]; try { const bytes = await buildOdt(json, pageMargins, pageOrientation, hfOpts()); @@ -222,7 +222,7 @@ // user can "Save as PDF" — vector text matching the editor, with header/footer. async function handleExportPdf() { if (!editor || pdfBusy) return; - saveMenuOpen = false; + exportMenuOpen = false; pdfBusy = true; try { await exportPdf({ @@ -239,9 +239,28 @@ } } - function saveMenuClickOutside(node: HTMLElement) { + // Vector PDF via the browser's print dialog: crisp, tiny, fonts embedded. Tables + // break natively; header/footer become CSS @page boxes (basic text + page numbers). + function handlePrintPdf() { + if (!editor) return; + exportMenuOpen = false; + try { + printPdf({ + json: editor.getJSON(), + margins: pageMargins, + orientation: pageOrientation, + headerDoc, + footerDoc, + }); + } catch (err) { + console.error('[pdf] Print failed:', err); + alert('Could not print to PDF.'); + } + } + + function exportMenuClickOutside(node: HTMLElement) { function handler(e: MouseEvent) { - if (!node.contains(e.target as Node)) saveMenuOpen = false; + if (!node.contains(e.target as Node)) exportMenuOpen = false; } window.addEventListener('mousedown', handler); return { destroy() { window.removeEventListener('mousedown', handler); } }; @@ -295,14 +314,6 @@ {/snippet} - {#snippet pdfIcon()} - - - {/snippet}
- {#if fsSupported} -
- - - {#if saveMenuOpen} - - {/if} -
- {:else} - - - {/if} + {#if exportMenuOpen} + + {/if} +