From d1854d85ce2113570d0313e4612f31b0fbd3b0e6 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 19 Oct 2025 20:23:28 +0300 Subject: [PATCH] feat(desktop/print): integrate for export to PDF --- apps/client/src/widgets/note_detail.ts | 3 +- .../src/assets/translations/en/server.json | 3 +- apps/server/src/services/window.ts | 34 ++++++++++++------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/apps/client/src/widgets/note_detail.ts b/apps/client/src/widgets/note_detail.ts index 5310b327a..7d2791e32 100644 --- a/apps/client/src/widgets/note_detail.ts +++ b/apps/client/src/widgets/note_detail.ts @@ -326,13 +326,14 @@ export default class NoteDetailWidget extends NoteContextAwareWidget { } async exportAsPdfEvent() { - if (!this.noteContext?.isActive() || !this.note) { + if (!this.noteContext?.isActive() || !this.note || !this.notePath) { return; } const { ipcRenderer } = utils.dynamicRequire("electron"); ipcRenderer.send("export-as-pdf", { title: this.note.title, + notePath: this.notePath, pageSize: this.note.getAttributeValue("label", "printPageSize") ?? "Letter", landscape: this.note.hasAttribute("label", "printLandscape") }); diff --git a/apps/server/src/assets/translations/en/server.json b/apps/server/src/assets/translations/en/server.json index b583bb343..972f45154 100644 --- a/apps/server/src/assets/translations/en/server.json +++ b/apps/server/src/assets/translations/en/server.json @@ -373,7 +373,8 @@ "export_filter": "PDF Document (*.pdf)", "unable-to-export-message": "The current note could not be exported as a PDF.", "unable-to-export-title": "Unable to export as PDF", - "unable-to-save-message": "The selected file could not be written to. Try again or select another destination." + "unable-to-save-message": "The selected file could not be written to. Try again or select another destination.", + "unable-to-print": "Unable to print the note" }, "tray": { "tooltip": "Trilium Notes", diff --git a/apps/server/src/services/window.ts b/apps/server/src/services/window.ts index f149fd1e0..83d1ca185 100644 --- a/apps/server/src/services/window.ts +++ b/apps/server/src/services/window.ts @@ -8,7 +8,7 @@ import sqlInit from "./sql_init.js"; import cls from "./cls.js"; import keyboardActionsService from "./keyboard_actions.js"; import electron from "electron"; -import type { App, BrowserWindowConstructorOptions, BrowserWindow, WebContents } from "electron"; +import type { App, BrowserWindowConstructorOptions, BrowserWindow, WebContents, IpcMainEvent } from "electron"; import { formatDownloadTitle, isDev, isMac, isWindows } from "./utils.js"; import { t } from "i18next"; import { RESOURCE_DIR } from "./resource_dir.js"; @@ -71,15 +71,28 @@ electron.ipcMain.on("create-extra-window", (event, arg) => { interface PrintOpts { notePath: string; + printToPdf: boolean; } interface ExportAsPdfOpts { + notePath: string; title: string; landscape: boolean; pageSize: "A0" | "A1" | "A2" | "A3" | "A4" | "A5" | "A6" | "Legal" | "Letter" | "Tabloid" | "Ledger"; } electron.ipcMain.on("print-note", async (e, { notePath }: PrintOpts) => { + const browserWindow = await getBrowserWindowForPrinting(e, notePath); + browserWindow.webContents.print({}, (success, failureReason) => { + if (success) { + browserWindow.destroy(); + } else { + electron.dialog.showErrorBox(t("pdf.unable-to-print"), failureReason); + } + }); +}); + +async function getBrowserWindowForPrinting(e: IpcMainEvent, notePath: string) { const browserWindow = new electron.BrowserWindow({ show: false, webPreferences: { @@ -96,19 +109,14 @@ electron.ipcMain.on("print-note", async (e, { notePath }: PrintOpts) => { window.addEventListener("note-ready", () => resolve()); }); `); - browserWindow.webContents.print({}, () => { - browserWindow.destroy(); - }); -}); + return browserWindow; +} -electron.ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => { - const browserWindow = electron.BrowserWindow.fromWebContents(e.sender); - if (!browserWindow) { - return; - } +electron.ipcMain.on("export-as-pdf", async (e, { title, notePath, landscape, pageSize }: ExportAsPdfOpts) => { + const browserWindow = await getBrowserWindowForPrinting(e, notePath); const filePath = electron.dialog.showSaveDialogSync(browserWindow, { - defaultPath: formatDownloadTitle(opts.title, "file", "application/pdf"), + defaultPath: formatDownloadTitle(title, "file", "application/pdf"), filters: [ { name: t("pdf.export_filter"), @@ -123,8 +131,8 @@ electron.ipcMain.on("export-as-pdf", async (e, opts: ExportAsPdfOpts) => { let buffer: Buffer; try { buffer = await browserWindow.webContents.printToPDF({ - landscape: opts.landscape, - pageSize: opts.pageSize, + landscape, + pageSize, generateDocumentOutline: true, generateTaggedPDF: true, printBackground: true,