UNPKG

pdf-to-png-converter

Version:

Node.js utility to convert PDF file/buffer pages to PNG files/buffers with no native dependencies.

127 lines (126 loc) 6.12 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.pdfToPng = pdfToPng; const node_fs_1 = require("node:fs"); const node_path_1 = require("node:path"); const const_1 = require("./const"); const node_canvas_factory_1 = require("./node.canvas.factory"); const propsToPdfDocInitParams_1 = require("./propsToPdfDocInitParams"); /** * Converts a PDF file to PNG images, one per page. * * @param pdfFile - The PDF file to convert. Can be a file path (string) or an ArrayBufferLike. * @param props - Optional configuration options for the conversion process. * @returns A promise that resolves to an array of `PngPageOutput` objects, each representing a PNG image of a PDF page. * * @remarks * - If `pdfFile` is a string, it is treated as a file path and read from disk. * - The `props.pagesToProcess` option allows specifying which pages to convert (1-based indices). * - The `props.viewportScale` option controls the rendering scale of the PDF pages. * - The `props.outputFileMaskFunc` option allows customizing the output file name for each page. * - If `props.outputFolder` is provided, the PNG files are saved to the specified folder. * - The function processes pages in parallel for efficiency. * - All resources are cleaned up after processing. * * @throws Will throw if the PDF file cannot be read or processed. */ async function pdfToPng(pdfFile, props) { // Read the PDF file and initialize the PDF document const isString = typeof pdfFile == 'string'; const pdfFileBuffer = isString ? await (async () => { const buffer = await node_fs_1.promises.readFile(pdfFile); // Ensure we always return an ArrayBuffer if (buffer instanceof ArrayBuffer) { return buffer; } else if (Buffer.isBuffer(buffer)) { return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); } else { throw new Error('Unsupported buffer type'); } })() : pdfFile; const pdfDocument = await getPdfDocument(pdfFileBuffer, props); // Get the pages to process based on the provided options, invalid pages will be filtered out const pagesToProcess = props?.pagesToProcess ?? Array.from({ length: pdfDocument.numPages }, (_, index) => index + 1); const validPagesToProcess = pagesToProcess.filter((pageNumber) => pageNumber <= pdfDocument.numPages && pageNumber >= 1); // Process each page in parallel const pngPagesOutput = []; try { const pageViewportScale = props?.viewportScale !== undefined ? props.viewportScale : const_1.PDF_TO_PNG_OPTIONS_DEFAULTS.viewportScale; const defaultMask = isString ? (0, node_path_1.parse)(pdfFile).name : const_1.PDF_TO_PNG_OPTIONS_DEFAULTS.outputFileMask; const pngPageOutputs = await Promise.all(validPagesToProcess.map((pageNumber) => { const pageName = props?.outputFileMaskFunc?.(pageNumber) ?? `${defaultMask}_page_${pageNumber}.png`; return processPdfPage(pdfDocument, pageName, pageNumber, pageViewportScale); })); pngPagesOutput.push(...pngPageOutputs); } finally { await pdfDocument.cleanup(); } // Save the PNG files to the output folder if (props?.outputFolder !== undefined) { const outputFolder = (0, node_path_1.join)(process.cwd(), props.outputFolder); await node_fs_1.promises.mkdir(outputFolder, { recursive: true }); await Promise.all(pngPagesOutput.map(async (pngPageOutput) => { pngPageOutput.path = (0, node_path_1.join)(outputFolder, pngPageOutput.name); await node_fs_1.promises.writeFile(pngPageOutput.path, pngPageOutput.content); })); } return pngPagesOutput; } /** * Loads a PDF document from a given ArrayBuffer and returns a PDF.js document proxy. * * @param pdfFileBuffer - The buffer containing the PDF file data. * @param props - Optional configuration options for PDF loading. * @returns A promise that resolves to a PDFDocumentProxy representing the loaded PDF. * * @remarks * This function dynamically imports the PDF.js library and initializes the document * using the provided buffer and options. The options are converted to PDF.js-compatible * initialization parameters via `propsToPdfDocInitParams`. * * @throws Will throw if the PDF cannot be loaded or parsed. */ async function getPdfDocument(pdfFileBuffer, props) { const { getDocument } = await import('pdfjs-dist/legacy/build/pdf.mjs'); const documentInitParameters = (0, propsToPdfDocInitParams_1.propsToPdfDocInitParams)(props); return await getDocument({ ...documentInitParameters, data: new Uint8Array(pdfFileBuffer), }).promise; } /** * Renders a specific page of a PDF document to a PNG image buffer. * * @param pdf - The PDF.js document proxy representing the loaded PDF. * @param pageName - The name to associate with the rendered page. * @param pageNumber - The 1-based index of the page to render. * @param pageViewportScale - The scale factor to apply to the page viewport for rendering. * @returns A promise that resolves to a `PngPageOutput` object containing the rendered PNG buffer and page metadata. */ async function processPdfPage(pdf, pageName, pageNumber, pageViewportScale) { const page = await pdf.getPage(pageNumber); const viewport = page.getViewport({ scale: pageViewportScale }); const canvasFactory = new node_canvas_factory_1.NodeCanvasFactory(); const { canvas, context } = canvasFactory.create(viewport.width, viewport.height); try { await page.render({ canvasContext: context, viewport }).promise; const pngPageOutput = { pageNumber, name: pageName, content: canvas.toBuffer('image/png'), path: '', width: viewport.width, height: viewport.height, }; return pngPageOutput; } finally { page.cleanup(); canvasFactory.destroy({ canvas, context }); } }