Extending Export Formats
Relevant source files
The following files were used as context for generating this wiki page:
The Writer application provides a robust infrastructure for transforming Markdown documents into various distribution formats, including HTML, PDF, and RTF. The export system is built upon a central rendering pipeline that converts Markdown AST (Abstract Syntax Tree) into stylized HTML, which then serves as the foundation for more complex file format generation.
Extensions to the export system involve implementing new exporter utilities that leverage the MarkdownRenderer and PreviewTheme components. By following the existing patterns, developers can add support for additional document formats while maintaining consistent styling across all output types.
Sources: CLAUDE.md, Writer/WriterApp.swift:30-80
Export Architecture
The export system follows a modular pattern where each format is handled by a dedicated utility structure. These utilities typically take the raw Markdown string and a PreviewTheme as input, process the content through the MarkdownRenderer, and handle the platform-specific logic for file persistence.
Core Components
| Component | Responsibility | Source File |
|---|---|---|
MarkdownRenderer | Core engine for converting Markdown to HTML using a custom MarkupVisitor. | Writer/MarkdownRenderer.swift |
HTMLVisitor | A MarkupVisitor implementation that maps Markdown elements (headers, lists, tables) to HTML tags. | Writer/MarkdownRenderer.swift:5-125 |
PreviewTheme | Provides CSS styling and typography settings used during the export process. | Writer/PreviewTheme.swift |
HTMLExporter | Generates standalone HTML files with inline CSS. | Writer/HTMLExporter.swift |
PDFExporter | Uses a headless WKWebView to render HTML and capture it as a PDF document. | Writer/PDFExporter.swift |
RTFExporter | Converts HTML to NSAttributedString for Rich Text Format generation. | Writer/RTFExporter.swift |
Data Flow for Exports
The following diagram illustrates how Markdown data flows from the EditorStore through the rendering pipeline to the final exported file.
The Rendering Pipeline
Every export format relies on the MarkdownRenderer to perform the initial transformation of text. The renderer uses the Apple Markdown framework to parse text into a Document and then applies a HTMLVisitor to generate the markup string.
HTML Transformation
The MarkdownRenderer.renderBodyContent(_:) method handles the heavy lifting, including escaping HTML entities and processing custom syntax like ==highlight== into <mark> tags.
Implementing New Formats
To add a new export format, a new exporter structure should be created that implements a save() method. This method typically presents an NSSavePanel and performs the necessary conversion.
PDF Export Logic
The PDFExporter is unique as it requires a WKWebView to handle layout and pagination. It uses JavaScript to calculate the content height before generating the PDF data.
Sources: Writer/PDFExporter.swift:39-68
RTF Export Logic
The RTFExporter leverages NSAttributedString's ability to initialize from HTML data.
- Generate the full HTML document with styles.
- Create
Datafrom the HTML string. - Initialize
NSAttributedStringusing the.htmldocument type. - Call
.rtf(from:documentAttributes:)to generate the final file data.
Sources: Writer/RTFExporter.swift:39-65
Integration with Application UI
Export actions are triggered from the WriterApp command menu. Each format requires a corresponding action in the File menu and a handling function that coordinates with the EditorStore.
| Feature | Implementation Detail |
|---|---|
| Keyboard Shortcuts | HTML (⌘⇧E), PDF (⌘⇧P), RTF (⌘⇧R) |
| Filename Suggestion | Derived from selectedFileURL with the target extension. |
| Error Handling | Errors are caught and displayed via a SwiftUI .alert. |
Sources: Writer/WriterApp.swift:101-125, Writer/WriterApp.swift:20-75
Summary
Extending export formats in Writer involves leveraging the existing MarkdownRenderer to obtain an HTML representation of the document, then applying format-specific conversions. The architecture ensures that all exports respect the user's active PreviewTheme by injecting the theme's CSS into the intermediate HTML document before final conversion.