farmcontrol-api/src/templates/pdffactory.js
Tom Butcher 322a086040
All checks were successful
farmcontrol/farmcontrol-api/pipeline/head This commit looks good
Update PDF generation height calculation and corresponding test value
This commit modifies the height calculation in the PDF generation function to increase the height by 2 pixels when the height option is set to 'auto'. Additionally, the related test has been updated to reflect this change, ensuring that the expected height value is now 122px instead of 121px. These adjustments improve the accuracy of the PDF rendering process.
2026-08-20 00:12:27 +01:00

146 lines
3.6 KiB
JavaScript

import log4js from 'log4js';
import puppeteer from 'puppeteer';
import config from '../config.js';
import { getPuppeteerLaunchOptions } from '../puppeteerLaunchOptions.js';
const logger = log4js.getLogger('PDF Factory');
logger.level = config.server.logLevel;
let browser = null;
let launchPromise = null;
let shutdownRegistered = false;
function isBrowserConnected() {
return browser != null && browser.connected;
}
async function launchBrowser() {
logger.info('Launching persistent Puppeteer browser...');
const instance = await puppeteer.launch(getPuppeteerLaunchOptions());
instance.on('disconnected', () => {
if (browser === instance) {
logger.warn('Puppeteer browser disconnected');
browser = null;
launchPromise = null;
}
});
logger.info('Persistent Puppeteer browser ready');
return instance;
}
function registerShutdownHandlers() {
if (shutdownRegistered) {
return;
}
shutdownRegistered = true;
const shutdown = async () => {
await closePDFBrowser();
};
process.once('SIGINT', shutdown);
process.once('SIGTERM', shutdown);
process.once('beforeExit', shutdown);
}
export async function startPDFBrowser() {
if (process.env.NODE_ENV !== 'test') {
registerShutdownHandlers();
}
if (isBrowserConnected()) {
return browser;
}
if (launchPromise == null) {
launchPromise = launchBrowser()
.then((instance) => {
browser = instance;
return instance;
})
.catch((error) => {
launchPromise = null;
throw error;
});
}
return launchPromise;
}
export async function closePDFBrowser() {
const instance = browser;
browser = null;
launchPromise = null;
if (instance != null) {
try {
await instance.close();
} catch (error) {
logger.warn('Error closing Puppeteer browser:', error.message);
}
}
}
async function withPage(run) {
let page;
try {
const instance = await startPDFBrowser();
page = await instance.newPage();
return await run(page);
} catch (error) {
if (page == null && isBrowserConnected() === false) {
const instance = await startPDFBrowser();
page = await instance.newPage();
return await run(page);
}
throw error;
} finally {
if (page != null) {
await page.close().catch(() => {});
}
}
}
/**
* Generates a PDF from HTML content using a persistent Puppeteer browser
* @param {string} html - The HTML content to convert to PDF
* @param {Object} options - PDF generation options
* @param {number} options.width - Document width in mm
* @param {number|string} options.height - Document height in mm, or 'auto'
* @returns {Promise<Buffer>} The PDF buffer
*/
export async function generatePDF(html, options = {}) {
try {
return await withPage(async (page) => {
await page.setContent(html, {
waitUntil: 'domcontentloaded',
});
var height = options?.height || '50';
if (options.height == 'auto') {
const calculatedHeight = await page.evaluate(() => {
return document.getElementById('content').scrollHeight;
});
height = calculatedHeight;
}
return page.pdf({
printBackground: true,
preferCSSPageSize: true,
width: options.width ? `${options.width}mm` : undefined,
height: height ? (options.height == 'auto' ? `${height + 2}px` : `${height}mm`) : undefined,
margin: {
top: '0mm',
right: '0mm',
bottom: '0mm',
left: '0mm',
},
});
});
} catch (error) {
logger.error('Error generating PDF:', error.message);
throw error;
}
}