UNPKG

qus-node-pdf-generator

Version:
156 lines (139 loc) 4.98 kB
import { Injectable } from '@nestjs/common'; import * as Handlebars from 'handlebars'; import chromium from '@sparticuz/chromium'; import puppeteer, { Browser, Page } from 'puppeteer'; @Injectable() export class PdfService { /** * Renders a single template into a PDF. * * @param template * @param data * @param format * @param landscape */ async render(template: string, data: any, format: PdfFormat, landscape: boolean = false): Promise<Uint8Array> { const compiledTemplate: HandlebarsTemplateDelegate = Handlebars.compile(template); const htmlContent: string = compiledTemplate(data); console.log("chrome path " + await chromium.executablePath()); const browser: Browser = await puppeteer.launch({ executablePath: await chromium.executablePath(), args: chromium.args, headless: chromium.headless, }); const page: Page = await browser.newPage(); await page.setContent(htmlContent, { waitUntil: 'networkidle0' }); const pdfBuffer: Uint8Array = await page.pdf({ format, landscape }); await browser.close(); return pdfBuffer; } /** * Renders multiple templates into a single PDF. * * @param templates * @param data * @param format * @param addPageBreaks * @param styles * @param landscape */ async renderMultiple( templates: string[], data: any[], format: PdfFormat, addPageBreaks: boolean = true, styles?: string, landscape: boolean = false ): Promise<Uint8Array> { const defaultStyles = this.getDefaultCss(addPageBreaks); const appliedStyles: string = styles || defaultStyles; let htmlContent: string = `<body>${appliedStyles}`; for (let i = 0; i < templates.length; i++) { const compiledTemplate: HandlebarsTemplateDelegate = Handlebars.compile(templates[i]); const renderedContent = compiledTemplate(data[i]); htmlContent += `<div class="page">${renderedContent}</div>`; } htmlContent += `</body>`; console.log('Chromium Path:', await chromium.executablePath()) const browser: Browser = await puppeteer.launch({ headless: 'shell' }); const page: Page = await browser.newPage(); await page.setContent(htmlContent, { waitUntil: 'networkidle0' }); const pdfBuffer: Uint8Array = await page.pdf({ format, landscape, printBackground: true }); await browser.close(); return pdfBuffer; } /** * Generates HTML content from multiple templates. * * @param templates * @param data * @param addPageBreaks * @param styles */ async generateHtml(templates: string[], data: any[], addPageBreaks: boolean = true, styles?: string): Promise<string> { // Default styles if none are provided const defaultStyles = ` <style> body { margin: 0; padding: 0; width: 100%; overflow-x: hidden; /* Prevent horizontal scrolling */ } .page { width: 100%; max-width: 800px; /* Constrain content width */ margin: 0 auto; /* Center content */ padding: 20px; /* Add padding for better readability */ box-sizing: border-box; ${addPageBreaks ? 'page-break-after: always;' : ''} /* Optional page break */ } .page:last-child { page-break-after: auto; /* Avoid unnecessary blank page at the end */ } </style> `; // Use provided styles or fallback to default const appliedStyles: string = styles || defaultStyles; // Combine all templates into a single HTML string let htmlContent: string = `<body>${appliedStyles}`; for (let i = 0; i < templates.length; i++) { const compiledTemplate: HandlebarsTemplateDelegate = Handlebars.compile(templates[i]); const renderedContent = compiledTemplate(data[i]); htmlContent += `<div class="page">${renderedContent}</div>`; } htmlContent += `</body>`; return htmlContent; } private getDefaultCss(addPageBreaks: boolean): string { return ` <style> body { margin: 0; padding: 0; width: 100%; overflow-x: hidden; } .page { width: 100%; max-width: 800px; margin: 0 auto; padding: 20px; box-sizing: border-box; ${addPageBreaks ? 'page-break-after: always;' : ''} } .page:last-child { page-break-after: auto; } </style> `; } } // Enum for PDF formats export enum PdfFormat { A3 = 'A3', A4 = 'A4', A5 = 'A5', Letter = 'letter', Legal = 'legal' }