Refactor PDF handling and update Linux packaging
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
- Removed @napi-rs/canvas and pdf-to-img dependencies from package.json and bun.lock to streamline the project. - Added PDF.js data directory configuration in the Linux service file and build scripts. - Introduced a new pdfToImg module for rendering PDF pages to images, enhancing PDF conversion capabilities. - Updated pdfUtils to utilize the new pdfToImg function, improving efficiency and modularity.
This commit is contained in:
parent
a1eafd878d
commit
b3d1457aa2
56
bun.lock
56
bun.lock
@ -5,7 +5,6 @@
|
||||
"": {
|
||||
"name": "farmcontrol-server",
|
||||
"dependencies": {
|
||||
"@napi-rs/canvas": "^1.0.3",
|
||||
"axios": "^1.16.1",
|
||||
"canvas": "^3.2.3",
|
||||
"chalk": "5.4.1",
|
||||
@ -21,7 +20,6 @@
|
||||
"mongoose": "^9.6.2",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-thermal-printer": "^4.6.0",
|
||||
"pdf-to-img": "^6.1.0",
|
||||
"sharp": "^0.34.5",
|
||||
"socket.io": "^4.8.3",
|
||||
"socket.io-client": "^4.8.3",
|
||||
@ -310,30 +308,6 @@
|
||||
|
||||
"@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.4.11", "", { "dependencies": { "sparse-bitfield": "3.0.3" } }, "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA=="],
|
||||
|
||||
"@napi-rs/canvas": ["@napi-rs/canvas@1.0.3", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "1.0.3", "@napi-rs/canvas-darwin-arm64": "1.0.3", "@napi-rs/canvas-darwin-x64": "1.0.3", "@napi-rs/canvas-linux-arm-gnueabihf": "1.0.3", "@napi-rs/canvas-linux-arm64-gnu": "1.0.3", "@napi-rs/canvas-linux-arm64-musl": "1.0.3", "@napi-rs/canvas-linux-riscv64-gnu": "1.0.3", "@napi-rs/canvas-linux-x64-gnu": "1.0.3", "@napi-rs/canvas-linux-x64-musl": "1.0.3", "@napi-rs/canvas-win32-arm64-msvc": "1.0.3", "@napi-rs/canvas-win32-x64-msvc": "1.0.3" } }, "sha512-OlI657a5XXvKGFX7kNeIzJ8rO7IXt87Mqu2H8rXE46viAuOfum/JA7ysX7+eBhxNKznT+RCZh418mndlcFX3+w=="],
|
||||
|
||||
"@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@1.0.3", "", { "os": "android", "cpu": "arm64" }, "sha512-7kSCdUhoXiO+AaIMXdBGdtp6EctZNkmF62Rea/BmVQlwKaM3bBhOzyGUzxyxz9dv5vdBfpyAaxhSRSJF4kqK4A=="],
|
||||
|
||||
"@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@1.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ds14V1BPagLszQyaDTeggny5fNeTCqsUQ5QhFj9VDxSEfzrVxXtdbR0LoFyKa0Siaaw8KvqSk4t7k/WoZJwvbg=="],
|
||||
|
||||
"@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@1.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-qof3LRAAycmkV2I1izZo9RoSHF8kCQr5O05sFwv0jK8rSdYV6KHVwimo6Qb7RxZj40WHKbLHm5JDaUF0o5XUAA=="],
|
||||
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@1.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-FU2kKZLmolHA9+KcUA+l1+xH3WTLUUTQDU/kLv9SEUr2TrRPu94aytOeizFJDHPs/QBcw4QL1mCQhetQXYBbag=="],
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-GVSjntxKeA+/y/ZKf1F+cmUw1WeIkE5aMRPqnZUlBTBvBcrvgWccJAWuYCKPX4QJQwZILIIwhgdAbl51yj6fpA=="],
|
||||
|
||||
"@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@1.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-J51oK/axyZ13kxycumSMfLiDZMdWdOVvqDFI28BpuViZHE3A0bQfr8B5vg8YnPEnqLD3BSn1hkdlh2buspEcNQ=="],
|
||||
|
||||
"@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@1.0.3", "", { "os": "linux", "cpu": "none" }, "sha512-CtQgQjoVTX67jS9XuCTtJ40Sl7wRLMguoFnnGnfDmCWf7kzKFZVwj5ynqUOIGKFMSB61ZCuQlwPvVNxYTTseaw=="],
|
||||
|
||||
"@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-jtfzAHFp+FRaR7zGT4jyCe6wUgAG/dVb5A4Apd8FY9jKarntDfUAlJXscugiH7ZF5kKnu7/lHFk9LaDPcrGEVQ=="],
|
||||
|
||||
"@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@1.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-xTzaUCKUHTY4bCGadeeRZggbRVbGUT1petg7Z8r9AJR2+D9Bqu6nQAgqBGC6D47tA70LjaaaLTrJ7wNY1T74dg=="],
|
||||
|
||||
"@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@1.0.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-ktVLuBkI6QVOm5BwO/WbdGwxgeetAMJa7TTmR8qBarXF0OU2NKjvjUtPJAl2y8t+zBRczJl/1VOl9gua6WcK2g=="],
|
||||
|
||||
"@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@1.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-SGhlQ8bDjL1Cz2KnsKMasr/5sTcwG/SZkB6WCJxLsmSm/3aS2C+3p39bA7iZ2/94+NkVDySZfbiGoaSZSFHYxA=="],
|
||||
|
||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.5", "", { "dependencies": { "@tybys/wasm-util": "0.10.2" }, "peerDependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0" } }, "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q=="],
|
||||
|
||||
"@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
||||
@ -1172,8 +1146,6 @@
|
||||
|
||||
"node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="],
|
||||
|
||||
"node-readable-to-web-readable-stream": ["node-readable-to-web-readable-stream@0.4.2", "", {}, "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="],
|
||||
|
||||
"node-thermal-printer": ["node-thermal-printer@4.6.0", "", { "dependencies": { "iconv-lite": "0.6.3", "pngjs": "7.0.0", "unorm": "1.6.0", "write-file-queue": "0.0.1" } }, "sha512-nl/beSbygyBf4qv6zDZimiD78S2VrMrpffULZWUgKxNjMUfzV2S0MO4/Qposrn2QZosVrQSGYq49VN1HIecw+A=="],
|
||||
@ -1224,10 +1196,6 @@
|
||||
|
||||
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
|
||||
|
||||
"pdf-to-img": ["pdf-to-img@6.2.0", "", { "dependencies": { "pdfjs-dist": "5.6.205" }, "bin": { "pdf2img": "bin/cli.mjs" } }, "sha512-54CaUwpTXw0SkG6U3Twx7AJNM2ZuFNpDCJ7oHmmW113gT8RwLZSbzv4/GOr+H0PFpFisuHcUirZBKgXkJXA1zg=="],
|
||||
|
||||
"pdfjs-dist": ["pdfjs-dist@5.6.205", "", { "optionalDependencies": { "@napi-rs/canvas": "0.1.100", "node-readable-to-web-readable-stream": "0.4.2" } }, "sha512-tlUj+2IDa7G1SbvBNN74UHRLJybZDWYom+k6p5KIZl7huBvsA4APi6mKL+zCxd3tLjN5hOOEE9Tv7VdzO88pfg=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
@ -1682,8 +1650,6 @@
|
||||
|
||||
"path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas": ["@napi-rs/canvas@0.1.100", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.100", "@napi-rs/canvas-darwin-arm64": "0.1.100", "@napi-rs/canvas-darwin-x64": "0.1.100", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.100", "@napi-rs/canvas-linux-arm64-gnu": "0.1.100", "@napi-rs/canvas-linux-arm64-musl": "0.1.100", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.100", "@napi-rs/canvas-linux-x64-gnu": "0.1.100", "@napi-rs/canvas-linux-x64-musl": "0.1.100", "@napi-rs/canvas-win32-arm64-msvc": "0.1.100", "@napi-rs/canvas-win32-x64-msvc": "0.1.100" } }, "sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA=="],
|
||||
|
||||
"png-to-ico/@types/node": ["@types/node@17.0.45", "", {}, "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="],
|
||||
|
||||
"png-to-ico/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
|
||||
@ -1784,28 +1750,6 @@
|
||||
|
||||
"jest-watcher/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.100", "", { "os": "android", "cpu": "arm64" }, "sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.100", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.100", "", { "os": "darwin", "cpu": "x64" }, "sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.100", "", { "os": "linux", "cpu": "arm" }, "sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.100", "", { "os": "linux", "cpu": "arm64" }, "sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.100", "", { "os": "linux", "cpu": "arm64" }, "sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.100", "", { "os": "linux", "cpu": "none" }, "sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.100", "", { "os": "linux", "cpu": "x64" }, "sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.100", "", { "os": "linux", "cpu": "x64" }, "sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.100", "", { "os": "win32", "cpu": "arm64" }, "sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw=="],
|
||||
|
||||
"pdfjs-dist/@napi-rs/canvas/@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.100", "", { "os": "win32", "cpu": "x64" }, "sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA=="],
|
||||
|
||||
"socket.io/accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"socket.io/accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
@ -23,7 +23,6 @@
|
||||
"author": "Tom Butcher",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@napi-rs/canvas": "^1.0.3",
|
||||
"axios": "^1.16.1",
|
||||
"canvas": "^3.2.3",
|
||||
"chalk": "5.4.1",
|
||||
@ -39,7 +38,6 @@
|
||||
"mongoose": "^9.6.2",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-thermal-printer": "^4.6.0",
|
||||
"pdf-to-img": "^6.1.0",
|
||||
"sharp": "^0.34.5",
|
||||
"socket.io": "^4.8.3",
|
||||
"socket.io-client": "^4.8.3",
|
||||
|
||||
@ -9,6 +9,7 @@ Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
Environment=NODE_ENV=production
|
||||
Environment=FC_PDFJS_DATA=/usr/lib/farmcontrol-server/pdfjs-data
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@ -59,4 +59,11 @@ if (exitCode !== 0) {
|
||||
process.exit(exitCode);
|
||||
}
|
||||
|
||||
const pdfjsDataDir = path.join(outputDir, "pdfjs-data");
|
||||
const pdfjsDistDir = path.join(rootDir, "node_modules/pdfjs-dist");
|
||||
await Bun.$`mkdir -p ${pdfjsDataDir}`;
|
||||
await Bun.$`cp -r ${path.join(pdfjsDistDir, "standard_fonts")} ${path.join(pdfjsDataDir, "standard_fonts")}`;
|
||||
await Bun.$`cp -r ${path.join(pdfjsDistDir, "cmaps")} ${path.join(pdfjsDataDir, "cmaps")}`;
|
||||
|
||||
console.log(`Linux binary written to ${outputFile}`);
|
||||
console.log(`PDF.js data copied to ${pdfjsDataDir}`);
|
||||
|
||||
@ -5,6 +5,7 @@ version="$(bun --eval "console.log(JSON.parse(await Bun.file('package.json').tex
|
||||
arch="x64"
|
||||
output_dir="app_dist"
|
||||
binary="${output_dir}/linux/farmcontrol-server"
|
||||
pdfjs_data="${output_dir}/linux/pdfjs-data"
|
||||
deb_name="farmcontrol-server-${version}-${arch}.deb"
|
||||
rpm_name="farmcontrol-server-${version}-${arch}.rpm"
|
||||
|
||||
@ -18,6 +19,7 @@ common_fpm_args=(
|
||||
--before-remove packaging/linux/before-remove.sh
|
||||
--after-remove packaging/linux/after-remove.sh
|
||||
"${binary}=/usr/bin/farmcontrol-server"
|
||||
"${pdfjs_data}=/usr/lib/farmcontrol-server/pdfjs-data"
|
||||
packaging/linux/farmcontrol-server.service=/lib/systemd/system/farmcontrol-server.service
|
||||
)
|
||||
|
||||
|
||||
142
src/pdfToImg.js
Normal file
142
src/pdfToImg.js
Normal file
@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Renders PDF pages to PNG buffers using pdfjs-dist and node-canvas.
|
||||
*/
|
||||
import { createRequire } from "node:module";
|
||||
import { readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { arrayBuffer } from "node:stream/consumers";
|
||||
import { createCanvas, DOMMatrix, ImageData } from "canvas";
|
||||
import * as pdfjs from "pdfjs-dist/legacy/build/pdf.mjs";
|
||||
|
||||
const PDF_DATA_PREFIX = "data:application/pdf;base64,";
|
||||
|
||||
if (!globalThis.DOMMatrix) {
|
||||
globalThis.DOMMatrix = DOMMatrix;
|
||||
}
|
||||
if (!globalThis.ImageData) {
|
||||
globalThis.ImageData = ImageData;
|
||||
}
|
||||
if (!globalThis.navigator?.language) {
|
||||
globalThis.navigator = {
|
||||
language: "en-US",
|
||||
platform: "",
|
||||
userAgent: "",
|
||||
};
|
||||
}
|
||||
|
||||
class CanvasPackageFactory {
|
||||
constructor({ enableHWA = false } = {}) {
|
||||
this.enableHWA = enableHWA;
|
||||
}
|
||||
|
||||
create(width, height) {
|
||||
const canvas = createCanvas(width, height);
|
||||
return {
|
||||
canvas,
|
||||
context: canvas.getContext("2d"),
|
||||
};
|
||||
}
|
||||
|
||||
reset(canvasAndContext, width, height) {
|
||||
canvasAndContext.canvas.width = width;
|
||||
canvasAndContext.canvas.height = height;
|
||||
}
|
||||
|
||||
destroy(canvasAndContext) {
|
||||
canvasAndContext.canvas.width = 0;
|
||||
canvasAndContext.canvas.height = 0;
|
||||
canvasAndContext.canvas = null;
|
||||
canvasAndContext.context = null;
|
||||
}
|
||||
}
|
||||
|
||||
function getPdfjsDataDir() {
|
||||
if (process.env.FC_PDFJS_DATA) {
|
||||
return process.env.FC_PDFJS_DATA;
|
||||
}
|
||||
|
||||
const candidates = [
|
||||
"/usr/lib/farmcontrol-server/pdfjs-data",
|
||||
path.join(path.dirname(process.execPath), "pdfjs-data"),
|
||||
];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
readFileSync(path.join(candidate, "standard_fonts", "FoxitFixed.pfb"));
|
||||
return candidate;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
return path.dirname(require.resolve("pdfjs-dist/package.json"));
|
||||
}
|
||||
|
||||
async function parsePdfInput(input) {
|
||||
if (Buffer.isBuffer(input)) {
|
||||
return Uint8Array.from(input);
|
||||
}
|
||||
if (input instanceof Uint8Array) {
|
||||
return input;
|
||||
}
|
||||
if (typeof input === "string") {
|
||||
if (input.startsWith(PDF_DATA_PREFIX)) {
|
||||
return Uint8Array.from(
|
||||
Buffer.from(input.slice(PDF_DATA_PREFIX.length), "base64"),
|
||||
);
|
||||
}
|
||||
return new Uint8Array(readFileSync(input));
|
||||
}
|
||||
if (typeof input === "object" && input && Symbol.asyncIterator in input) {
|
||||
return new Uint8Array(await arrayBuffer(input));
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"pdfToImg received an unexpected input. Provide a path, data URL, Uint8Array, Buffer, or ReadableStream.",
|
||||
);
|
||||
}
|
||||
|
||||
export async function pdfToImg(pdfInput, options = {}) {
|
||||
const { page_numbers, scale = 2, password, docInitParams } = options;
|
||||
|
||||
const pdfjsDataDir = getPdfjsDataDir();
|
||||
const data = await parsePdfInput(pdfInput);
|
||||
|
||||
const pdfDocument = await pdfjs.getDocument({
|
||||
password,
|
||||
standardFontDataUrl: path.join(pdfjsDataDir, "standard_fonts") + path.sep,
|
||||
cMapUrl: path.join(pdfjsDataDir, "cmaps") + path.sep,
|
||||
cMapPacked: true,
|
||||
isEvalSupported: false,
|
||||
CanvasFactory: CanvasPackageFactory,
|
||||
data,
|
||||
...docInitParams,
|
||||
}).promise;
|
||||
|
||||
const outputImages = [];
|
||||
const pageCount = pdfDocument.numPages;
|
||||
const pagesToRender =
|
||||
page_numbers && Array.isArray(page_numbers) && page_numbers.length > 0
|
||||
? page_numbers
|
||||
: Array.from({ length: pageCount }, (_, index) => index + 1);
|
||||
|
||||
for (const pageNum of pagesToRender) {
|
||||
const page = await pdfDocument.getPage(pageNum);
|
||||
const viewport = page.getViewport({ scale });
|
||||
const canvasFactory = new CanvasPackageFactory();
|
||||
const { canvas, context } = canvasFactory.create(
|
||||
viewport.width,
|
||||
viewport.height,
|
||||
);
|
||||
|
||||
await page.render({
|
||||
canvas,
|
||||
viewport,
|
||||
}).promise;
|
||||
|
||||
outputImages.push(canvas.toBuffer("image/png"));
|
||||
canvasFactory.destroy({ canvas, context });
|
||||
}
|
||||
|
||||
await pdfDocument.destroy();
|
||||
return outputImages;
|
||||
}
|
||||
@ -1,68 +1,35 @@
|
||||
/**
|
||||
* PDF conversion utilities loaded on demand so headless startup
|
||||
* does not require pdf-to-img at module load time.
|
||||
* PDF conversion utilities with optional post-render resizing via sharp.
|
||||
*/
|
||||
import { pdfToImg } from "./pdfToImg.js";
|
||||
|
||||
function resolveDefault(module) {
|
||||
return module?.default ?? module;
|
||||
}
|
||||
|
||||
export async function convertPDFToImage(pdfInput, options = {}) {
|
||||
const [pdfToImgModule, sharpModule] = await Promise.all([
|
||||
import("pdf-to-img"),
|
||||
import("sharp"),
|
||||
]);
|
||||
const { pdf } = pdfToImgModule;
|
||||
const sharpModule = await import("sharp");
|
||||
const sharp = resolveDefault(sharpModule);
|
||||
|
||||
try {
|
||||
const { width, height, page_numbers, scale = 2, ...pdfOnlyOptions } =
|
||||
options;
|
||||
const { width, height, page_numbers, scale = 2, ...pdfOnlyOptions } =
|
||||
options;
|
||||
|
||||
const pdfOptions = {
|
||||
scale,
|
||||
...pdfOnlyOptions,
|
||||
};
|
||||
const images = await pdfToImg(pdfInput, {
|
||||
page_numbers,
|
||||
scale,
|
||||
password: pdfOnlyOptions.password,
|
||||
docInitParams: pdfOnlyOptions.docInitParams,
|
||||
});
|
||||
|
||||
const document = await pdf(pdfInput, pdfOptions);
|
||||
const outputImages = [];
|
||||
|
||||
if (
|
||||
page_numbers &&
|
||||
Array.isArray(page_numbers) &&
|
||||
page_numbers.length > 0
|
||||
) {
|
||||
for (const pageNum of page_numbers) {
|
||||
let image = await document.getPage(pageNum);
|
||||
|
||||
if (width || height) {
|
||||
const resizeOptions = {};
|
||||
if (width) resizeOptions.width = width;
|
||||
if (height) resizeOptions.height = height;
|
||||
image = await sharp(image).resize(resizeOptions).toBuffer();
|
||||
}
|
||||
|
||||
outputImages.push(image);
|
||||
}
|
||||
} else {
|
||||
for await (const image of document) {
|
||||
let processedImage = image;
|
||||
|
||||
if (width || height) {
|
||||
const resizeOptions = {};
|
||||
if (width) resizeOptions.width = width;
|
||||
if (height) resizeOptions.height = height;
|
||||
processedImage = await sharp(image)
|
||||
.resize(resizeOptions)
|
||||
.toBuffer();
|
||||
}
|
||||
|
||||
outputImages.push(processedImage);
|
||||
}
|
||||
}
|
||||
|
||||
return outputImages;
|
||||
} catch (error) {
|
||||
console.error("Error converting PDF to image:", error);
|
||||
throw error;
|
||||
if (!width && !height) {
|
||||
return images;
|
||||
}
|
||||
|
||||
const resizeOptions = {};
|
||||
if (width) resizeOptions.width = width;
|
||||
if (height) resizeOptions.height = height;
|
||||
|
||||
return Promise.all(
|
||||
images.map((image) => sharp(image).resize(resizeOptions).toBuffer()),
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user