pdf-to-png-converter
Version:
Node.js utility to convert PDF file/buffer pages to PNG files/buffers. No build-time compilation required — pre-built native binaries included for all major platforms.
79 lines (78 loc) • 3.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const node_worker_threads_1 = require("node:worker_threads");
const normalizePdfToPngOptions_js_1 = require("./normalizePdfToPngOptions.js");
const pageRenderer_js_1 = require("./pageRenderer.js");
const pdfjsLoader_js_1 = require("./pdfjsLoader.js");
/**
* Worker-thread entry point for `renderInWorkerThreads` mode.
*
* Each worker loads its OWN copy of the PDF document (pdf.js documents are not transferable
* across threads) and renders the pages the pool assigns to it, end-to-end: getPage → render →
* PNG encode. Disk writes stay on the main thread so the SEC-001/002/003 path guards run in
* exactly one place. Workers never exit on their own — the pool terminates them.
*
* Runs only as a compiled artifact (`out/pageRenderWorker.js`); see `resolveWorkerEntryPath`
* in `src/workerPool.ts` for how the pool locates it.
*/
if (node_worker_threads_1.parentPort === null) {
throw new Error('pageRenderWorker must be started as a worker thread.');
}
const port = node_worker_threads_1.parentPort;
const init = node_worker_threads_1.workerData;
// Re-normalize inside the worker: pure, keeps NormalizedPdfToPngOptions the single validation
// boundary on both sides of the thread hop, and reconstructs defaulted fields dropped from the
// serializable subset.
const normalizedOptions = (0, normalizePdfToPngOptions_js_1.normalizePdfToPngOptions)(init.documentOptions);
let documentPromise;
/**
* Posts a response that carries a thrown value. Error instances structured-clone with
* name/message/stack preserved; values that cannot be cloned (functions, exotic objects some
* libraries throw) are downgraded to a plain Error so the response always gets through.
*/
function postErrorResponse(build, error) {
try {
port.postMessage(build(error));
}
catch {
port.postMessage(build(new Error(error instanceof Error ? error.message : String(error))));
}
}
async function handleRender(request) {
let pdfDocument;
try {
// Loaded lazily once per worker; getPdfDocument transfers (detaches) this worker's
// private copy of the buffer, which is fine — it is loaded exactly once.
documentPromise ??= (0, pdfjsLoader_js_1.getPdfDocument)(init.pdfBuffer, normalizedOptions);
pdfDocument = await documentPromise;
}
catch (error) {
postErrorResponse((cause) => ({ type: 'fatal', error: cause }), error);
return;
}
try {
const page = await (0, pageRenderer_js_1.renderPdfPage)(pdfDocument, request.pageName, request.pageNumber, normalizedOptions.viewportScale, init.materializeContent);
const response = {
type: 'result',
index: request.index,
pageNumber: page.pageNumber,
name: page.name,
width: page.width,
height: page.height,
rotation: page.rotation,
content: page.content,
};
// The PNG bytes are structured-clone COPIED across the thread boundary, not
// transferred: @napi-rs/canvas allocates encode() output as a napi-external
// ArrayBuffer, which Node.js cannot transfer ("Cannot transfer object of
// unsupported type"). One copy per page (typically well under 1 MB) is negligible
// next to the render work this mode parallelizes.
port.postMessage(response);
}
catch (error) {
postErrorResponse((cause) => ({ type: 'render-error', index: request.index, error: cause }), error);
}
}
port.on('message', (request) => {
void handleRender(request);
});