PDF Export Feature
Relevant source files
The following files were used as context for generating this wiki page:
The PDF Export feature in the Writer application provides users with the ability to convert Markdown documents into professionally formatted PDF files. This module leverages a hybrid rendering pipeline that first transforms Markdown into a styled HTML document and subsequently utilizes macOS system frameworks to generate a high-fidelity PDF output.
The feature is integrated into the macOS menu system and utilizes standard system dialogs for file destination selection, ensuring a native user experience. It supports custom styling through the application's theme system, allowing exported documents to maintain visual consistency with the live preview.
Sources: CLAUDE.md:19-33, Writer/WriterApp.swift:42-55
Architecture and Data Flow
The PDF export process is orchestrated through a series of asynchronous steps involving Markdown parsing, HTML generation, and WebKit-based PDF rendering.
Rendering Pipeline
The system utilizes a multi-stage pipeline:
- Markdown Parsing: Raw text is processed using a
MarkupVisitorto generate HTML structure. - HTML Wrapping: The generated body content is wrapped in a full HTML document structure containing inline CSS derived from the active
PreviewTheme. - WebKit Rendering: A hidden
WKWebViewloads the HTML. - PDF Generation: The
WKWebView.pdf()API generates the final data buffer.
Sources: Writer/PDFExporter.swift:15-55, Writer/MarkdownRenderer.swift:145-165
Flow Diagram: Export Process
The following diagram illustrates the lifecycle of a PDF export request from the user interface to the final file write operation.
Key Components
PDFExporter Struct
The core logic resides in the PDFExporter struct, which is marked with @MainActor to ensure thread safety when interacting with UI-bound components like WKWebView and NSSavePanel.
| Property/Method | Description |
|---|---|
pageWidth | Fixed at 612 points (US Letter width). |
pageHeight | Fixed at 792 points (US Letter height). |
pageMargin | Set to 72 points (1 inch). |
save(suggestedName:) | The primary entry point that coordinates the UI and rendering logic. |
getContentHeight(_:) | Executes JavaScript to determine the actual rendered height of the document. |
generatePDF(from:) | Configures WKPDFConfiguration and triggers the WebKit PDF export. |
Sources: Writer/PDFExporter.swift:8-21, Writer/PDFExporter.swift:63-71
Markdown Transformation
The export feature relies on MarkdownRenderer to convert raw text into a format suitable for the WebKit engine.
static func renderBodyContent(_ markdown: String) -> String {
let escaped = escapeHTMLEntities(markdown)
let document = Document(parsing: escaped)
var visitor = HTMLVisitor()
var html = document.accept(&visitor)
html = processHighlights(html)
return html
}
Sources: Writer/MarkdownRenderer.swift:147-156
Implementation Details
Asynchronous Coordination
Generating a PDF requires waiting for the asynchronous loading of a web view. The PDFExporter implements a polling mechanism to ensure the document.readyState is "complete" before attempting to capture the PDF.
Sources: Writer/PDFExporter.swift:74-84
Theme Integration
The PreviewTheme provides the cssStyles property, which is injected directly into the <style> block of the generated HTML. This ensures that fonts (e.g., system-ui, Georgia, SF Mono) and spacing defined in PreviewTheme.sansSerif or PreviewTheme.serif are preserved in the PDF output.
Sources: Writer/PreviewTheme.swift:48-60, Writer/MarkdownRenderer.swift:163-178
User Interface Integration
The export is triggered via the "File" menu in WriterApp. It is conditionally enabled only when a file is selected or the editor contains text.
| Command | Shortcut | Logic |
|---|---|---|
| Export to PDF | ⌘⇧P | Initializes PDFExporter with editorStore.documentText and themeStore.previewTheme. |
Sources: Writer/WriterApp.swift:42-45, Writer/WriterApp.swift:131-134
Error Handling
The system handles export errors through the ExportError enumeration. Common failures include:
- Cancelled: User closes the
NSSavePanelwithout selecting a location. - WriteFailed: Disk I/O errors or conversion failures.
Errors are caught in the WriterApp call site and presented to the user via a SwiftUI .alert().
Sources: Writer/HTMLExporter.swift:44-55, Writer/WriterApp.swift:49-55
Conclusion
The PDF Export Feature provides a robust solution for document distribution by combining custom Markdown rendering logic with the powerful WebKit engine. By utilizing a hidden rendering surface and dynamic height calculation, it produces well-formatted, multi-page-ready documents that respect the user's selected theme and typography.