Export Layer

PDF Export Engine

Source Files: pdf-export.ts Complexity: Medium

The PDF Export Engine enables users to quickly export markdown notes as polished, print-quality PDFs. By converting markdown to HTML and utilizing a headless Chrome/Chromium shell, it outputs high-resolution documents with fully embedded images and custom styles.

1. Overview

Markdown files are great for writing but can be difficult to share as standalone, formatted documents. The PDF export engine provides an easy sharing solution:

  • Converts Markdown: Converts markdown formatting to HTML structures using marked.
  • Inlines Images: Scans relative markdown images, reads them from the disk, and inlines them as Base64 data URLs. This makes the generated HTML self-contained, ensuring images render correctly in headless browsers.
  • Headless Printing: Automatically locates Chrome, Edge, Brave, or Chromium on the user's operating system (Mac, Windows, or Linux) and triggers headless printing to PDF.

2. Under the Hood & Code Walkthrough

2.1 Inlining Local Images as Base64

To prevent images from breaking when printing headlessly, the extension scans the markdown text, reads local images, and converts them to base64 data URLs in a single sweep:

function embedImagesInMarkdown(mdContent: string, mdFilePath: string): string {
    return mdContent.replace(/!\[(.*?)\]\((.*?)\)/g, (match, alt, imgPath) => {
        if (/^\s*https?:\/\//.test(imgPath)) { return match; }
        
        const absPath = path.isAbsolute(imgPath)
            ? imgPath
            : path.join(path.dirname(mdFilePath), imgPath);
            
        if (!fs.existsSync(absPath)) { return match; }
        
        const ext = path.extname(absPath).slice(1).toLowerCase();
        const mimeMap: Record<string, string> = {
            svg: 'image/svg+xml',
            jpg: 'image/jpeg',
            jpeg: 'image/jpeg',
            png: 'image/png',
        };
        const mime = mimeMap[ext] || 'image/png';
        
        try {
            const data = fs.readFileSync(absPath).toString('base64');
            return `![${alt}](data:${mime};base64,${data})`;
        } catch {
            return match;
        }
    });
}

2.2 Operating System Browser Discovery

The extension automatically searches for compatible browsers across macOS, Windows, and Linux:

function findChromePath(): string | undefined {
    switch (process.platform) {
        case 'darwin': // macOS
            const macPaths = [
                '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
                '/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge',
                '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser',
            ];
            return macPaths.find(p => fs.existsSync(p));
        case 'win32': // Windows
            const winPaths = [
                process.env['PROGRAMFILES(X86)'] + '\\Google\\Chrome\\Application\\chrome.exe',
                process.env['PROGRAMFILES'] + '\\Google\\Chrome\\Application\\chrome.exe',
                process.env['PROGRAMFILES(X86)'] + '\\Microsoft\\Edge\\Application\\msedge.exe',
            ];
            return winPaths.find(p => p && fs.existsSync(p));
        case 'linux': // Linux
            const linuxPaths = [
                '/usr/bin/google-chrome',
                '/usr/bin/google-chrome-stable',
                '/usr/bin/chromium-browser',
            ];
            return linuxPaths.find(p => fs.existsSync(p));
        default:
            return undefined;
    }
}