UNPKG

pdf-node

Version:

A modern, feature-rich PDF generation library for Node.js with TypeScript support. Convert HTML to PDF with Handlebars templates, buffers, and streams.

166 lines (165 loc) 4.35 kB
import * as pdf from 'html-pdf'; import Handlebars from 'handlebars'; import * as ejs from 'ejs'; import handleError from 'cli-error-handler'; const templateCache = {}; export {Handlebars, ejs}; /** * Generates a PDF from an HTML template and data * @param options PDF generation options * @returns Promise that resolves with the generated PDF (as buffer or file info) * @throws {Error} If required options are missing or PDF generation fails */ export function generatePDF(options) { return new Promise(async (resolve, reject) => { if (!options || !options.html || !options.data) { reject(new Error('Some, or all, options are missing.')); return; } if (options.type !== undefined && options.type !== 'pdf') { reject(new Error('Only PDF file type is supported')); return; } const pdfOptions = options.pdfOptions || {}; const useCache = options.cacheTemplate !== false; // Default to true const engine = options.engine || 'handlebars'; // Get compiled template let html; try { html = await compileTemplate( options.html, options.data, engine, useCache ); } catch (error) { handleError('Error compiling template', error); reject(error); return; } // Check if buffer output is requested if (options.buffer === true) { // Output will be PDF buffer (useful for APIs/web services) pdf.create(html, pdfOptions).toBuffer(function (err, buffer) { if (err) { handleError('error in creating buffer', err); reject(err); return; } console.log( 'PDF buffer generated, size:', buffer.length, 'bytes' ); resolve({ buffer: buffer, size: buffer.length, type: 'application/pdf' }); }); } else { // Output will be PDF file (default behavior) if (!options.path) { reject( new Error( 'Path is required when buffer option is not set to true.' ) ); return; } const filepath = options.path; pdf.create(html, pdfOptions).toFile(filepath, function (err, res) { if (err) { handleError('error in creating file', err); reject(err); return; } console.log('file generated:', res.filename); resolve(res); }); } }); } /** * Adds a page break to the PDF * @returns HTML string with a page break */ export function addNewPage() { return '<div style="page-break-after: always;"></div>'; } /** * Compiles a template with the specified engine * @param template The template string * @param data Data to inject into the template * @param engine Template engine to use ('handlebars', 'ejs', or 'html') * @param useCache Whether to cache the compiled template * @returns Compiled HTML string */ async function compileTemplate( template, data, engine = 'handlebars', useCache = true ) { const cacheKey = useCache ? `${engine}:${template}` : null; // Return cached template if available if (cacheKey && templateCache[cacheKey]) { const cached = templateCache[cacheKey]; if (typeof cached === 'function') { return cached(data); } return cached; } // Compile and cache the template let compiled; switch (engine) { case 'ejs': compiled = async templateData => { try { // Use the promise-based renderFile with a fake filename return await ejs.render(template, templateData, { async: true }); } catch (err) { throw err instanceof Error ? err : new Error(String(err)); } }; break; case 'html': // Simple variable replacement for HTML mode compiled = template.replace(/\{\{([^}]+)\}\}/g, (_, key) => { return data[key.trim()] || ''; }); break; case 'handlebars': default: const handlebarsTemplate = Handlebars.compile(template); compiled = templateData => Promise.resolve(handlebarsTemplate(templateData)); break; } // Cache the compiled template if needed if (useCache && cacheKey) { templateCache[cacheKey] = compiled; } return typeof compiled === 'function' ? compiled(data) : compiled; } /** * Clears the template cache */ export function clearTemplateCache() { Object.keys(templateCache).forEach(key => { delete templateCache[key]; }); } const pdfNode = { generatePDF, addNewPage, clearTemplateCache, // Export template engines for advanced usage engines: { handlebars: Handlebars, ejs } }; export default pdfNode;