import { chromium, type Browser, type Page } from 'playwright'; export interface PDFOptions { format?: string; landscape?: boolean; margin?: { top: number; right: number; bottom: number; left: number }; printBackground?: boolean; headerTemplate?: string; footerTemplate?: string; } export class PDFConverterService { private browser: Browser | null = null; async initialize(): Promise { if (this.browser) return; const launchOptions: Record = { headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'], }; if (process.env.PLAYWRIGHT_CHROMIUM_PATH) { launchOptions.executablePath = process.env.PLAYWRIGHT_CHROMIUM_PATH; } this.browser = await chromium.launch(launchOptions); } async close(): Promise { if (this.browser) { await this.browser.close(); this.browser = null; } } async convert(html: string, options: PDFOptions = {}): Promise { await this.initialize(); if (!this.browser) throw new Error('PDF converter not initialized'); const page: Page = await this.browser.newPage(); try { await page.setContent(html, { waitUntil: 'networkidle' }); await page.waitForTimeout(300); const pdf = await page.pdf({ format: options.format as any || 'A4', landscape: options.landscape || false, printBackground: options.printBackground !== false, margin: options.margin ? { top: `${options.margin.top / 1440}in`, right: `${options.margin.right / 1440}in`, bottom: `${options.margin.bottom / 1440}in`, left: `${options.margin.left / 1440}in`, } : undefined, displayHeaderFooter: !!(options.headerTemplate || options.footerTemplate), headerTemplate: options.headerTemplate || '
', footerTemplate: options.footerTemplate || '
', }); return pdf; } finally { await page.close(); } } }