# Export the content page as it is

[TOC]

# Overview
Customizations aren't appearing in PDF exports becuase BookStack's PDF engine (by default, dompdf) is a separate process that doesn't execute JavaScript and only recognizes a subset of CSS.

To ensure your styles are applied to PDF exports follows the steps

# Steps

## 1. Install npm

```
apt update
apt install npm -y
```

## 2. Install Puppeteer

```bash
npm install puppeteer
```

## Install Chrome for the Web User

```bash
# Define a local cache path and install
PUPPETEER_CACHE_DIR=/opt/bookstack/.cache npx puppeteer browsers install chrome
```


## Set Cache Permissions

```bash
# Replace 'www-data' with your web user if different (e.g., 'nginx')
chown -R www-data:www-data /opt/bookstack/.cache
```

## Update .env to manipulated export command

Edit `.env` adding:

```bash
ALLOW_UNTRUSTED_SERVER_FETCHING=true
EXPORT_PDF_COMMAND="node /opt/bookstack/puppeteer-pdf.js {input_html_path} {output_pdf_path}"
EXPORT_PDF_COMMAND_TIMEOUT=30
```

## Create the Export Script

In BookStack root directory (`/opt/bookstack`) create a new file named `puppeteer-pdf.js`

```js
const puppeteer = require('puppeteer');
const fs = require('fs');

(async () => {
    const browser = await puppeteer.launch({
        executablePath: '/opt/bookstack/.cache/chrome/linux-146.0.7680.76/chrome-linux64/chrome',
        args: ['--no-sandbox', '--disable-setuid-sandbox']
    });
    const page = await browser.newPage();

    const htmlContent = fs.readFileSync(process.argv[2], 'utf8');
    await page.setContent(htmlContent, { waitUntil: 'networkidle0' });

    // --- NEW TOC GENERATION LOGIC ---
    await page.evaluate(() => {
        const tocPlaceholder = document.body.innerHTML.match(/\[TOC\]/g);
        if (!tocPlaceholder) return;

        // Find all headings (h1 to h4)
        const headings = Array.from(document.querySelectorAll('h1, h2, h3, h4'));
        let tocHtml = '<div class="table-of-contents"><h2>Table of Contents</h2><ul>';

        headings.forEach((heading, index) => {
            const id = heading.id || `heading-${index}`;
            heading.id = id; // Ensure heading has an ID to link to
            const level = heading.tagName.toLowerCase();
            tocHtml += `<li class="toc-${level}"><a href="#${id}">${heading.innerText}</a></li>`;
        });

        tocHtml += '</ul></div>';

        // Replace the [TOC] text with our generated HTML
        document.body.innerHTML = document.body.innerHTML.replace('[TOC]', tocHtml);
    });
    // --------------------------------

    await page.pdf({
        path: process.argv[3],
        format: 'A4',
        printBackground: true,
        margin: { top: '1cm', right: '1cm', bottom: '1cm', left: '1cm' }
    });

    await browser.close();
})();
```