diff --git a/.gitignore b/.gitignore index 307beae..3be9634 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.env -/node_modules \ No newline at end of file +/node_modules +/output \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4bfae88..5d68a8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@hpcc-js/wasm-graphviz": "^1.29.1", "chalk": "^6.0.0", "dotenv": "^17.4.2", + "pdfconvert-wasm": "^0.1.0", "sharp": "^0.35.4", "thumbhash": "^0.1.1" }, @@ -1475,6 +1476,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pdfconvert-wasm": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/pdfconvert-wasm/-/pdfconvert-wasm-0.1.0.tgz", + "integrity": "sha512-Hf2Gy/lgjcygI2as9O/GJXQjsszAqGrPBlkgyMTp2QT3R2PLSxiUfXu6FP5Qi7XfZmyjwWMnj4O1gy7Coh9kzQ==", + "license": "GPLv3" + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", diff --git a/package.json b/package.json index a127bc5..c06632c 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@hpcc-js/wasm-graphviz": "^1.29.1", "chalk": "^6.0.0", "dotenv": "^17.4.2", + "pdfconvert-wasm": "^0.1.0", "sharp": "^0.35.4", "thumbhash": "^0.1.1" }, diff --git a/src/util/generateDocumentThumb.js b/src/util/generateDocumentThumb.js index 0dc3d59..e024040 100644 --- a/src/util/generateDocumentThumb.js +++ b/src/util/generateDocumentThumb.js @@ -1,48 +1,19 @@ import sharp from 'sharp'; -import { spawn } from 'node:child_process'; +import { pdfToPng } from 'pdfconvert-wasm'; const THUMB_MAX = 200; /** - * Renders page 1 of a PDF into a PNG buffer using pdftocairo. + * Renders page 1 of a PDF into a PNG buffer using pdfconvert-wasm. * @param {Buffer} pdfBuffer * @returns {Promise} */ -function renderPdfPageToPng(pdfBuffer) { - return new Promise((resolve, reject) => { - const proc = spawn('pdftocairo', ['-png', '-singlefile', '-', '-'], { - stdio: ['pipe', 'pipe', 'pipe'], - }); - - const chunks = []; - const errChunks = []; - - proc.stdout.on('data', (chunk) => chunks.push(chunk)); - proc.stderr.on('data', (chunk) => errChunks.push(chunk)); - - proc.stdin.on('error', (err) => { - console.warn('[pdftocairo] stdin stream error:', err.message); - }); - - proc.on('error', (err) => { - reject(new Error(`Failed to start pdftocairo: ${err.message}`)); - }); - - proc.on('close', (code) => { - if (code !== 0) { - const stderr = Buffer.concat(errChunks).toString('utf8').trim(); - reject(new Error(`pdftocairo exited with code ${code}${stderr ? `: ${stderr}` : ''}`)); - } else { - resolve(Buffer.concat(chunks)); - } - }); - - try { - proc.stdin.end(pdfBuffer); - } catch (writeErr) { - console.warn('[pdftocairo] Failed writing to stdin:', writeErr.message); - } - }); +async function renderPdfPageToPng(pdfBuffer) { + const images = await pdfToPng(pdfBuffer, { dpi: 150, singleFile: true }); + if (!images || images.length === 0 || !images[0].data) { + throw new Error('pdfconvert-wasm produced no image output'); + } + return Buffer.from(images[0].data); } /** @@ -51,9 +22,10 @@ function renderPdfPageToPng(pdfBuffer) { * * @param {Buffer} content * @param {string} mimetype + * @param {Object} [options] * @returns {Promise<{thumbavif: Buffer|null, thumbx: number|null, thumby: number|null}>} */ -export default async function generateDocumentThumb(content, mimetype) { +export default async function generateDocumentThumb(content, mimetype, options = {}) { if (!Buffer.isBuffer(content) || content.length === 0) { return { thumbavif: null, thumbx: null, thumby: null }; }