UNPKG

@maistik/nuxt-pdf

Version:

A Nuxt 3 module for server-side PDF generation using Handlebars templates

381 lines (298 loc) 7.93 kB
# @maistik/nuxt-pdf A powerful Nuxt 3/4 module for server-side PDF generation using Handlebars templates. Generate beautiful, data-driven PDFs with support for multiple providers (Gotenberg, Browserless, Puppeteer Core) and built-in internationalization. ## Features - 🎨 **Handlebars Templates** - Lightweight templating without Vue overhead - 🌍 **Internationalization** - Built-in i18n support with `{{t}}` helper - 🔄 **Provider Agnostic** - Support for Gotenberg, Browserless, and Puppeteer Core - 🎯 **SSR Safe** - Everything runs server-side in Nitro/Node - 🧩 **Composable API** - Easy-to-use `usePdf()` composable - 📱 **Responsive Design** - CSS-based layouts with print media queries - 🎪 **Playground** - Demo app with sample templates ## Quick Start ### Installation ```bash npm install @maistik/nuxt-pdf handlebars ``` ### Configuration Add the module to your `nuxt.config.ts`: ```typescript export default defineNuxtConfig({ modules: ['@maistik/nuxt-pdf'], pdf: { provider: 'puppeteer', // or 'gotenberg' or 'browserless' components: ['pdf'], sharedComponents: ['pdf/partials'], enableI18n: true, defaultLocale: 'en', availableLocales: ['en', 'es', 'fr'], i18nMessages: { en: { invoice: { title: 'Invoice', total: 'Total' } }, es: { invoice: { title: 'Factura', total: 'Total' } } }, providers: { puppeteer: { launchOptions: { headless: true, args: ['--no-sandbox'] } } } } }) ``` ### Create a Template Create `pdf/Invoice.hbs` in your project: ```handlebars <style> @page { size: A4; margin: 20mm; } body { font-family: Arial, sans-serif; } .header { text-align: center; margin-bottom: 30px; } .total { font-weight: bold; font-size: 18px; } </style> <div class="header"> <h1>{{t "invoice.title"}}</h1> <p>Invoice #{{invoiceNumber}}</p> </div> <table> {{#each items}} <tr> <td>{{this.description}}</td> <td>{{formatCurrency this.price}}</td> </tr> {{/each}} </table> <div class="total"> {{t "invoice.total"}}: {{formatCurrency total}} </div> ``` ### Generate PDFs Use the composable in your Vue components: ```vue <script setup> const { generate, download } = usePdf() const generateInvoice = async () => { const data = { invoiceNumber: 'INV-001', items: [ { description: 'Service', price: 100 } ], total: 100 } // Generate and preview const blob = await generate('Invoice', data, { format: 'A4' }, 'en') // Or download directly await download('Invoice', data, { format: 'A4' }, 'invoice.pdf', 'en') } </script> ``` ## Providers ### Puppeteer Core (Local) Best for development and on-premise deployments: ```typescript pdf: { provider: 'puppeteer', providers: { puppeteer: { launchOptions: { headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] } } } } ``` ### Gotenberg (Docker) Perfect for containerized environments: ```typescript pdf: { provider: 'gotenberg', providers: { gotenberg: { url: 'http://gotenberg:3000' } } } ``` ### Browserless (Cloud) Great for serverless deployments: ```typescript pdf: { provider: 'browserless', providers: { browserless: { url: 'https://chrome.browserless.io', apiKey: process.env.BROWSERLESS_API_KEY } } } ``` ## Built-in Helpers ### Internationalization ```handlebars {{t "invoice.title"}} <!-- Outputs localized text --> ``` ### Currency Formatting ```handlebars {{formatCurrency 1234.56}} <!-- $1,234.56 --> {{formatCurrency 1234.56 "EUR"}} <!-- €1,234.56 --> ``` ### Date Formatting ```handlebars {{formatDate date}} <!-- 12/25/2024 --> {{formatDate date "full"}} <!-- Wednesday, December 25, 2024 --> ``` ### Number Formatting ```handlebars {{formatNumber 1234.56}} <!-- 1,234.56 --> {{formatNumber 0.15 style="percent"}} <!-- 15% --> ``` ### Math Operations ```handlebars {{add 10 5}} <!-- 15 --> {{subtract 10 5}} <!-- 5 --> {{multiply 10 5}} <!-- 50 --> {{divide 10 5}} <!-- 2 --> {{percentage 25 100}} <!-- 25% --> ``` ## Custom Helpers You can define your own custom helpers in the configuration: ```typescript pdf: { customHelpers: { // Simple value transformation customFormat: (value: any) => `[${value}]`, // String manipulation repeat: (str: string, times: number) => str.repeat(times || 1), // Block helper with conditional logic ifEquals: function(this: any, arg1: any, arg2: any, options: any) { return (arg1 === arg2) ? options.fn(this) : options.inverse(this) } } } ``` Use them in templates: ```handlebars {{customFormat "hello"}} <!-- [hello] --> {{repeat "★" 5}} <!-- ★★★★★ --> {{#ifEquals status "active"}}Active User{{else}}Inactive User{{/ifEquals}} ``` ### String Helpers ```handlebars {{upper "hello world"}} <!-- HELLO WORLD --> {{lower "HELLO WORLD"}} <!-- hello world --> {{capitalize "hello world"}} <!-- Hello world --> {{truncate "Long text here" 10}} <!-- Long text... --> ``` ### Line Calculations ```handlebars {{lineTotal quantity price}} <!-- quantity * price --> ``` ## Template Features ### Automatic Enrichment The module automatically enriches your data based on template names: **Invoice Templates** get: - `subtotal` - Sum of all line items - `tax` - Calculated tax amount - `total` - Subtotal + tax - `dueDate` - Calculated from issue date + payment terms **Sales Report Templates** get: - `quarters` - Quarterly breakdown of sales data - `rating` - Performance rating based on total sales ### CSS Styling Use standard CSS with print-specific rules: ```css @page { size: A4; margin: 20mm; } .page-break { page-break-before: always; } @media print { .no-break { page-break-inside: avoid; } } ``` ### Partials Create reusable components in your `sharedComponents` directory: ```handlebars <!-- pdf/partials/header.hbs --> <div class="header"> <h1>{{title}}</h1> <p>{{subtitle}}</p> </div> ``` Use in templates: ```handlebars {{> header title="My Document" subtitle="Generated Report"}} ``` ## API Reference ### `usePdf()` The main composable for PDF operations: ```typescript const { generate, // (template, data, options?, locale?) => Promise<Blob> download, // (template, data, options?, filename?, locale?) => Promise<void> getAvailableLocales, // () => string[] getDefaultLocale // () => string } = usePdf() ``` ### Options ```typescript interface PdfOptions { format?: 'A4' | 'Letter' | 'Legal' margin?: { top?: number bottom?: number left?: number right?: number } landscape?: boolean printBackground?: boolean pageBreak?: { before?: string[] after?: string[] avoid?: string[] } } ``` ## Development ### Playground The module includes a full playground application: ```bash npm run dev ``` This starts a demo app with sample Invoice and Sales Report templates in multiple languages. ### Building ```bash npm run build ``` ### Testing ```bash npm run test ``` ## Examples Check out the `playground/` directory for complete examples including: - **Invoice Template** - Complete invoice with line items, taxes, and totals - **Sales Report Template** - Comprehensive report with metrics and quarterly breakdown - **Multi-language Support** - Templates in English, Spanish, and French - **Multiple Providers** - Configuration examples for all supported providers ## License MIT ## Contributing Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository. ## Support - 📖 [Documentation](https://github.com/Maistik-Studio/nuxt-pdf) - 🐛 [Issue Tracker](https://github.com/Maistik-Studio/nuxt-pdf/issues) - 💬 [Discussions](https://github.com/Maistik-Studio/nuxt-pdf/discussions)