import { App, Modal, Plugin, Setting, Notice, MarkdownRenderer, Component, TFile } from "obsidian"; export default class CustomPdfExportPlugin extends Plugin { async onload() { this.addRibbonIcon("document", "Custom PDF Export", () => { new PdfExportModal(this.app).open(); }); this.addCommand({ id: "open-custom-pdf-export", name: "Export note to styled PDF", callback: () => { new PdfExportModal(this.app).open(); } }); } } class PdfExportModal extends Modal { pdfFont: string = "Inter"; pdfFontSize: string = "14px"; pdfThemeColor: string = "#333333"; previewContainer: HTMLDivElement; previewComponent: Component; activeFile: TFile | null = null; constructor(app: App) { super(app); this.previewComponent = new Component(); } onOpen() { const { contentEl } = this; contentEl.empty(); this.previewComponent.load(); contentEl.addClass("pdf-export-modal-content"); const container = contentEl.createDiv({ cls: "pdf-split-container" }); /* LEFT PANEL */ const controlsPane = container.createDiv({ cls: "pdf-controls-pane" }); controlsPane.createEl("h2", { text: "PDF Settings" }); new Setting(controlsPane) .setName("Font Family") .addText(t => t.setValue(this.pdfFont).onChange(v => { this.pdfFont = v; this.updatePreviewVariables(); }) ); new Setting(controlsPane) .setName("Font Size") .addText(t => t.setValue(this.pdfFontSize).onChange(v => { this.pdfFontSize = v; this.updatePreviewVariables(); }) ); new Setting(controlsPane) .setName("Theme Color") .addColorPicker(c => c.setValue(this.pdfThemeColor).onChange(v => { this.pdfThemeColor = v; this.updatePreviewVariables(); }) ); const exportBtn = controlsPane.createEl("button", { text: "Export PDF", cls: "mod-cta" }); exportBtn.onclick = () => this.executeExport(); /* RIGHT PANEL */ const previewPane = container.createDiv({ cls: "pdf-preview-pane" }); previewPane.createEl("h3", { text: "Preview" }); this.previewContainer = previewPane.createDiv({ cls: "pdf-preview-container pdf-export" }); this.activeFile = this.app.workspace.getActiveFile(); if (this.activeFile) { this.renderPreview(); } else { this.previewContainer.setText("Open a note to preview."); } this.updatePreviewVariables(); } onClose() { this.previewComponent.unload(); } async renderPreview() { if (!this.activeFile) return; const content = await this.app.vault.cachedRead(this.activeFile); this.previewContainer.empty(); const transformed = this.transformFootnotesForPdf(content); await MarkdownRenderer.renderMarkdown( transformed, this.previewContainer, this.activeFile.path, this.previewComponent ); } updatePreviewVariables() { if (!this.previewContainer) return; this.previewContainer.style.setProperty("--pdf-font", this.pdfFont); this.previewContainer.style.setProperty("--pdf-font-size", this.pdfFontSize); this.previewContainer.style.setProperty("--pdf-theme-color", this.pdfThemeColor); } transformFootnotesForPdf(content: string): string { /* convert ^[note] → (note) for preview */ return content.replace(/\^\[([^\]]+)\]/g, (_, note) => { return `(${note})`; }); } executeExport() { if (!this.activeFile) { new Notice("No active note."); return; } /* inject global variables so Obsidian PDF exporter can see them */ document.body.style.setProperty("--pdf-font", this.pdfFont); document.body.style.setProperty("--pdf-font-size", this.pdfFontSize); document.body.style.setProperty("--pdf-theme-color", this.pdfThemeColor); /* trigger Obsidian native PDF export */ // @ts-ignore this.app.commands.executeCommandById("workspace:export-pdf"); new Notice("Choose where to save the exported PDF."); this.close(); /* cleanup */ setTimeout(() => { document.body.style.removeProperty("--pdf-font"); document.body.style.removeProperty("--pdf-font-size"); document.body.style.removeProperty("--pdf-theme-color"); }, 5000); } }