From f7a36fc9975f722e4ec7ab7b7bd10b48b5c9fcfa Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 14 Apr 2026 23:31:05 +0300 Subject: [PATCH] feat(print/pdf): adjustable page size --- .../src/translations/en/translation.json | 3 +- apps/client/src/widgets/NoteDetail.tsx | 4 +- .../src/widgets/dialogs/print_preview.tsx | 37 ++++++++++++++----- apps/server/src/services/window.ts | 2 +- packages/commons/src/lib/attribute_names.ts | 1 + 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 41e575e5be..f9cbbb146f 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -2311,7 +2311,8 @@ "save": "Save as PDF", "orientation": "Orientation", "portrait": "Portrait", - "landscape": "Landscape" + "landscape": "Landscape", + "page_size": "Page size" }, "pdf": { "attachments_one": "{{count}} attachment", diff --git a/apps/client/src/widgets/NoteDetail.tsx b/apps/client/src/widgets/NoteDetail.tsx index 9ab8fa2e44..e94a95b6e8 100644 --- a/apps/client/src/widgets/NoteDetail.tsx +++ b/apps/client/src/widgets/NoteDetail.tsx @@ -147,10 +147,10 @@ export default function NoteDetail() { toast.closePersistent("printing"); handlePrintReport(printReport); }; - const onPreviewResult = (_e: any, { buffer, notePath, pageSize }: { buffer: Uint8Array; notePath: string; pageSize: string }) => { + const onPreviewResult = (_e: any, { buffer, notePath }: { buffer: Uint8Array; notePath: string }) => { toast.closePersistent("printing"); if (note) { - appContext.triggerCommand("showPrintPreview", { pdfBuffer: buffer, note, notePath, pageSize }); + appContext.triggerCommand("showPrintPreview", { pdfBuffer: buffer, note, notePath }); } }; ipcRenderer.on("print-progress", onPrintProgress); diff --git a/apps/client/src/widgets/dialogs/print_preview.tsx b/apps/client/src/widgets/dialogs/print_preview.tsx index 2216e06a26..31e00b8f20 100644 --- a/apps/client/src/widgets/dialogs/print_preview.tsx +++ b/apps/client/src/widgets/dialogs/print_preview.tsx @@ -5,17 +5,18 @@ import { t } from "../../services/i18n"; import toast from "../../services/toast"; import { dynamicRequire, isElectron } from "../../services/utils"; import Button, { ButtonGroup } from "../react/Button"; -import { useNoteLabelBoolean, useTriliumEvent } from "../react/hooks"; +import { useNoteLabelBoolean, useNoteLabelWithDefault, useTriliumEvent } from "../react/hooks"; import Modal from "../react/Modal"; import PdfViewer from "../type_widgets/file/PdfViewer"; import OptionsRow from "../type_widgets/options/components/OptionsRow"; import OptionsSection from "../type_widgets/options/components/OptionsSection"; +const PAGE_SIZES = ["A0", "A1", "A2", "A3", "A4", "A5", "A6", "Legal", "Letter", "Tabloid", "Ledger"] as const; + export interface PrintPreviewData { pdfBuffer: Uint8Array; note: FNote; notePath: string; - pageSize: string; } export default function PrintPreviewDialog() { @@ -25,9 +26,9 @@ export default function PrintPreviewDialog() { const [loading, setLoading] = useState(false); const bufferRef = useRef(); const notePathRef = useRef(""); - const pageSizeRef = useRef(""); const [landscape, setLandscape] = useNoteLabelBoolean(note, "printLandscape"); + const [pageSize, setPageSize] = useNoteLabelWithDefault(note, "printPageSize", "Letter"); const updatePreview = useCallback((buffer: Uint8Array) => { bufferRef.current = buffer; @@ -45,7 +46,6 @@ export default function PrintPreviewDialog() { useTriliumEvent("showPrintPreview", (data: PrintPreviewData) => { setNote(data.note); notePathRef.current = data.notePath; - pageSizeRef.current = data.pageSize; updatePreview(data.pdfBuffer); setShown(true); }); @@ -74,16 +74,21 @@ export default function PrintPreviewDialog() { function handleOrientationChange(newLandscape: boolean) { if (newLandscape === landscape) return; setLandscape(newLandscape); - regeneratePreview(newLandscape); + regeneratePreview({ landscape: newLandscape, pageSize }); } - function regeneratePreview(newLandscape: boolean) { + function handlePageSizeChange(newPageSize: string) { + if (newPageSize === pageSize) return; + setPageSize(newPageSize); + regeneratePreview({ landscape, pageSize: newPageSize }); + } + + function regeneratePreview(opts: { landscape: boolean; pageSize: string }) { if (!isElectron()) return; setLoading(true); const { ipcRenderer } = dynamicRequire("electron"); - // Listen for the result once. const onResult = (_e: any, { buffer }: { buffer: Uint8Array }) => { toast.closePersistent("printing"); updatePreview(buffer); @@ -91,10 +96,9 @@ export default function PrintPreviewDialog() { ipcRenderer.once("export-as-pdf-preview-result", onResult); ipcRenderer.send("export-as-pdf-preview", { - title: note?.title ?? "", notePath: notePathRef.current, - pageSize: pageSizeRef.current, - landscape: newLandscape + pageSize: opts.pageSize, + landscape: opts.landscape }); } @@ -135,6 +139,19 @@ export default function PrintPreviewDialog() { /> + + + + diff --git a/apps/server/src/services/window.ts b/apps/server/src/services/window.ts index ea120deb72..bded84603f 100644 --- a/apps/server/src/services/window.ts +++ b/apps/server/src/services/window.ts @@ -187,7 +187,7 @@ electron.ipcMain.on("export-as-pdf-preview", async (e, { notePath, landscape, pa ` }); - e.sender.send("export-as-pdf-preview-result", { buffer, notePath, pageSize }); + e.sender.send("export-as-pdf-preview-result", { buffer, notePath }); } catch (_e) { electron.dialog.showErrorBox(t("pdf.unable-to-export-title"), t("pdf.unable-to-export-message")); } finally { diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts index bde3ba98f2..b1e398c5ab 100644 --- a/packages/commons/src/lib/attribute_names.ts +++ b/packages/commons/src/lib/attribute_names.ts @@ -62,6 +62,7 @@ type Labels = { // Print/export printLandscape: boolean; + printPageSize: string; // Note-type specific webViewSrc: string;