Wiki
PDF Generation Pipeline
How QuoteNode generates branded PDF documents from offer snapshots using Thymeleaf and Gotenberg.
PDF Generation Pipeline
QuoteNode generates print-ready PDF documents from offer data using a two-stage pipeline: HTML rendering via Thymeleaf templates, followed by PDF conversion via Gotenberg.
Pipeline Overview
Offer Snapshot (JSON)
│
▼
Thymeleaf Template Engine
├── Layout fragments (header, items table, totals, footer)
├── CSS inlined for Gotenberg compatibility
├── Product images embedded as base64 URIs
└── Pagination support (Page X/Y)
│
▼
HTML Document (complete, self-contained)
│
▼
Gotenberg (Chromium-based container)
│
▼
PDF File (stored with SHA-256 checksum)
Snapshot-Based Rendering
PDFs are always rendered from immutable snapshots, never from live offer data. When an offer is sent:
- The system captures a complete JSON snapshot of the offer state
- This snapshot includes: customer data, all line items, pricing, FX rates, branding configuration, and template settings
- The snapshot is stored and versioned (snapshot_version increments on each resend)
This guarantees that the PDF always represents exactly what was offered on that specific date — regardless of any subsequent changes to catalog prices, customer data, or branding.
Template System
Template Families
The current implementation includes the CLASSIC_B2B_TECHNICAL template family. Future variants are planned:
MINIMAL_B2B— clean, minimal layout for simple offersB2C_FRIENDLY— consumer-oriented layout with product imagesMULTI_CURRENCY— optimized for multi-currency pricing tablesSERVICE_HOURS— focused on time-based service offerings
Template Structure
Each PDF template consists of:
- Header — seller logo, company name, address, and contact details
- Meta section — recipient address, offer number, salesperson name, date, and validity period
- Items table — configurable columns with pricing details
- Totals summary — subtotal, discounts, shipping, VAT breakdown, and grand total
- Trading conditions — payment terms, warranty, delivery conditions, transport terms
- Signature block — space for approval signatures
- Footer — company registration details (NIP, KRS, court of registration)
Configurable Columns
The items table columns are controlled per offer via template settings:
| Setting | Effect |
|---|---|
showSku | Show/hide the SKU column |
showImages | Display product photos in the table |
imageSize | Small (60px), medium (90px), or none |
showDescription | Include product descriptions |
showUnit | Show unit of measure column |
showVatColumn | Show VAT rate column (auto-detected when multiple rates present) |
priceMode | NET, GROSS, or NET+VAT+GROSS display |
discountDisplay | BAKED_IN, SHOW_COLUMN, or HIDDEN |
Branding Integration
The PDF automatically applies the tenant’s branding configuration:
- Logo — company logo (PNG or SVG) placed in the header, automatically sized
- Primary color — applied to headers, borders, and accents
- Accent color — applied to secondary elements
- Company details — name, address, phone, email, registration info
- Custom offer title — override the default “OFERTA CENOWA” heading
- “Powered by” badge — displayed on Free edition, removable on Pro+ licenses
Processing Modes
Synchronous (Default)
For offers with fewer than 50 line items:
- API request triggers PDF generation
- Thymeleaf renders HTML → Gotenberg converts to PDF
- PDF stream returned in the same HTTP response
- SLA target: p95 < 3 seconds
Asynchronous
For large offers (50+ line items) or when the server is under load:
- API request returns HTTP 202 with a
jobId - A
PdfJobrecord is created in PostgreSQL with statusPENDING - A worker thread picks up the job (SKIP LOCKED pattern, 2 concurrent workers)
- On success: PDF stored, status updated to
COMPLETED, notification sent to user - On failure: retry with exponential backoff (30s → 120s → 600s, 3 max attempts)
- Permanently failed jobs are moved to a Dead Letter Queue (DLQ) table
Storage & Retention
Generated PDFs are stored as files with metadata in the generated_documents table:
- Filename — unique identifier
- File size — in bytes
- SHA-256 checksum — for integrity verification
- Snapshot version — links back to the offer snapshot used for generation
- Created at — timestamp of generation
Retention policy: PDFs are retained for 365 days by default (configurable). After the retention period, files are eligible for cleanup but the metadata record is preserved for audit purposes.
Gotenberg Configuration
Gotenberg runs as a separate Docker container in the deployment stack. It uses Chromium for HTML-to-PDF conversion, providing:
- Accurate CSS rendering (including flexbox, grid, and modern CSS)
- Pagination with configurable margins
- Page size support (A4 default, letter, and custom sizes)
- Header and footer rendering (page numbers)
The backend communicates with Gotenberg via its HTTP API. No direct Chromium interaction is needed.