Add PDF conversion utilities and update build scripts for Linux packaging
All checks were successful
farmcontrol/farmcontrol-server/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-server/pipeline/head This commit looks good
Introduce a new pdfUtils.js file for on-demand PDF conversion using pdf-to-img and sharp. Update package.json to include new build scripts for Linux packaging, enhancing the build process with public package handling. Add .npmrc for public hoisting patterns and create build-linux-packages.sh for streamlined package creation.
This commit is contained in:
parent
d1592bf55e
commit
55b4c1a42e
6
.npmrc
Normal file
6
.npmrc
Normal file
@ -0,0 +1,6 @@
|
||||
public-hoist-pattern[]=pdf-to-img
|
||||
public-hoist-pattern[]=pdfjs-dist
|
||||
public-hoist-pattern[]=sharp
|
||||
public-hoist-pattern[]=@img/*
|
||||
public-hoist-pattern[]=canvas
|
||||
public-hoist-pattern[]=@napi-rs/*
|
||||
11
package.json
11
package.json
@ -15,8 +15,10 @@
|
||||
"build:electron-renderer": "vite build src/electron --outDir build/electron",
|
||||
"build:electron": "pnpm build && electron-builder",
|
||||
"build:electron:mac": "pnpm build && electron-builder --mac dmg --arm64 --x64 && electron-builder --mac pkg --arm64 && electron-builder --mac pkg --x64",
|
||||
"build:linux": "pnpm run cleanBuild && pnpm run build:server && pnpm run build:linux-bundle && shx mkdir -p app_dist/linux && pkg build/pkg-entry.cjs --config package.json --targets node18-linux-x64 --output app_dist/linux/farmcontrol-server && fpm -f -s dir -t deb -n farmcontrol-server -p app_dist/linux/ -v $(node -p \"require('./package.json').version\") --after-install packaging/linux/after-install.sh --after-remove packaging/linux/after-remove.sh app_dist/linux/farmcontrol-server=/usr/bin/farmcontrol-server packaging/linux/farmcontrol-server.service=/lib/systemd/system/farmcontrol-server.service && fpm -f -s dir -t rpm -n farmcontrol-server -p app_dist/linux/ -v $(node -p \"require('./package.json').version\") --after-install packaging/linux/after-install.sh --after-remove packaging/linux/after-remove.sh app_dist/linux/farmcontrol-server=/usr/bin/farmcontrol-server packaging/linux/farmcontrol-server.service=/lib/systemd/system/farmcontrol-server.service",
|
||||
"build:linux-bundle": "esbuild src/headless.js --bundle --platform=node --target=node18 --format=cjs --outfile=build/pkg-entry.cjs --packages=external --external:electron --inject:./scripts/import-meta-url.js --define:import.meta.url=import_meta_url",
|
||||
"build:linux": "pnpm run cleanBuild && pnpm run build:server && pnpm run build:linux-bundle && pnpm run build:linux-pkg && pnpm run build:linux-packages",
|
||||
"build:linux-bundle": "node scripts/build-linux-bundle.mjs",
|
||||
"build:linux-pkg": "shx mkdir -p app_dist/linux && pnpm exec pkg build/pkg-entry.cjs --config package.json --targets node18-linux-x64 --public-packages \"pdf-to-img,pdfjs-dist,sharp,canvas,@img/*,@napi-rs/*\" --output app_dist/linux/farmcontrol-server",
|
||||
"build:linux-packages": "bash scripts/build-linux-packages.sh",
|
||||
"cleanBuild": "rimraf build"
|
||||
},
|
||||
"author": "Tom Butcher",
|
||||
@ -70,11 +72,12 @@
|
||||
],
|
||||
"assets": [
|
||||
"build/config.json",
|
||||
"node_modules/pdf-to-img/**/*",
|
||||
"node_modules/pdfjs-dist/**/*",
|
||||
"node_modules/sharp/**/*",
|
||||
"node_modules/@img/**/*",
|
||||
"node_modules/canvas/**/*",
|
||||
"node_modules/@napi-rs/**/*",
|
||||
"node_modules/pdf-to-img/**/*"
|
||||
"node_modules/@napi-rs/**/*"
|
||||
],
|
||||
"targets": [
|
||||
"node18-linux-x64"
|
||||
|
||||
27
scripts/build-linux-bundle.mjs
Normal file
27
scripts/build-linux-bundle.mjs
Normal file
@ -0,0 +1,27 @@
|
||||
import * as esbuild from 'esbuild'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'node:path'
|
||||
|
||||
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
||||
|
||||
await esbuild.build({
|
||||
entryPoints: [path.join(rootDir, 'src/headless.js')],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
target: 'node18',
|
||||
format: 'cjs',
|
||||
outfile: path.join(rootDir, 'build/pkg-entry.cjs'),
|
||||
external: [
|
||||
'electron',
|
||||
'sharp',
|
||||
'canvas',
|
||||
'@napi-rs/canvas',
|
||||
'pdf-to-img',
|
||||
'pdfjs-dist'
|
||||
],
|
||||
inject: [path.join(rootDir, 'scripts/import-meta-url.js')],
|
||||
define: {
|
||||
'import.meta.url': 'import_meta_url'
|
||||
},
|
||||
logLevel: 'info'
|
||||
})
|
||||
21
scripts/build-linux-packages.sh
Executable file
21
scripts/build-linux-packages.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
version="$(node -p "require('./package.json').version")"
|
||||
output_dir="app_dist/linux"
|
||||
binary="${output_dir}/farmcontrol-server"
|
||||
|
||||
common_fpm_args=(
|
||||
-f
|
||||
-s dir
|
||||
-n farmcontrol-server
|
||||
-p "${output_dir}/"
|
||||
-v "${version}"
|
||||
--after-install packaging/linux/after-install.sh
|
||||
--after-remove packaging/linux/after-remove.sh
|
||||
"${binary}=/usr/bin/farmcontrol-server"
|
||||
packaging/linux/farmcontrol-server.service=/lib/systemd/system/farmcontrol-server.service
|
||||
)
|
||||
|
||||
fpm -t deb "${common_fpm_args[@]}"
|
||||
fpm -t rpm "${common_fpm_args[@]}"
|
||||
@ -2,7 +2,7 @@
|
||||
import log4js from "log4js";
|
||||
import { loadConfig } from "../../config.js";
|
||||
import { ThermalPrinter, PrinterTypes } from "node-thermal-printer";
|
||||
import { convertPDFToImage } from "../../utils.js";
|
||||
import { convertPDFToImage } from "../../pdfUtils.js";
|
||||
|
||||
const config = loadConfig();
|
||||
const logger = log4js.getLogger("Receipt Printer Interface");
|
||||
|
||||
61
src/pdfUtils.js
Normal file
61
src/pdfUtils.js
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* PDF conversion utilities loaded on demand so headless startup
|
||||
* does not require ESM-only pdf-to-img at module load time.
|
||||
*/
|
||||
export async function convertPDFToImage(pdfInput, options = {}) {
|
||||
const [{ pdf }, sharp] = await Promise.all([
|
||||
import('pdf-to-img'),
|
||||
import('sharp')
|
||||
])
|
||||
|
||||
try {
|
||||
let scale = options.scale || 2
|
||||
|
||||
const pdfOptions = {
|
||||
scale,
|
||||
...options
|
||||
}
|
||||
|
||||
const document = await pdf(pdfInput, pdfOptions)
|
||||
const outputImages = []
|
||||
|
||||
if (
|
||||
options.page_numbers &&
|
||||
Array.isArray(options.page_numbers) &&
|
||||
options.page_numbers.length > 0
|
||||
) {
|
||||
for (const pageNum of options.page_numbers) {
|
||||
let image = await document.getPage(pageNum)
|
||||
|
||||
if (options.width || options.height) {
|
||||
const resizeOptions = {}
|
||||
if (options.width) resizeOptions.width = options.width
|
||||
if (options.height) resizeOptions.height = options.height
|
||||
image = await sharp.default(image).resize(resizeOptions).toBuffer()
|
||||
}
|
||||
|
||||
outputImages.push(image)
|
||||
}
|
||||
} else {
|
||||
for await (const image of document) {
|
||||
let processedImage = image
|
||||
|
||||
if (options.width || options.height) {
|
||||
const resizeOptions = {}
|
||||
if (options.width) resizeOptions.width = options.width
|
||||
if (options.height) resizeOptions.height = options.height
|
||||
processedImage = await sharp.default(image)
|
||||
.resize(resizeOptions)
|
||||
.toBuffer()
|
||||
}
|
||||
|
||||
outputImages.push(processedImage)
|
||||
}
|
||||
}
|
||||
|
||||
return outputImages
|
||||
} catch (error) {
|
||||
console.error('Error converting PDF to image:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
70
src/utils.js
70
src/utils.js
@ -1,7 +1,5 @@
|
||||
import readline from "node:readline";
|
||||
import os from "os";
|
||||
import { pdf } from "pdf-to-img";
|
||||
import sharp from "sharp";
|
||||
|
||||
let isPrompting = false; // prevent multiple prompts at the same time
|
||||
|
||||
@ -56,71 +54,3 @@ export function getDeviceInfo() {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a PDF buffer or file path to an array of image buffers
|
||||
* @param {Buffer|string} pdfInput - PDF buffer or file path
|
||||
* @param {Object} options - Conversion options (optional)
|
||||
* @param {number} options.scale - Scale factor for image resolution (default: 2)
|
||||
* @param {number} options.width - Target width in pixels (will calculate scale if provided)
|
||||
* @param {number} options.height - Target height in pixels (optional)
|
||||
* @param {number[]} options.page_numbers - Array of page numbers to convert (optional, converts all pages by default)
|
||||
* @returns {Promise<Buffer[]>} Array of image buffers, one for each page
|
||||
*/
|
||||
export async function convertPDFToImage(pdfInput, options = {}) {
|
||||
try {
|
||||
// pdf-to-img uses scale instead of width/height directly
|
||||
// Default scale of 2 provides good quality for thermal printers
|
||||
let scale = options.scale || 2;
|
||||
|
||||
const pdfOptions = {
|
||||
scale,
|
||||
...options,
|
||||
};
|
||||
|
||||
const document = await pdf(pdfInput, pdfOptions);
|
||||
const outputImages = [];
|
||||
|
||||
// If specific page numbers are requested, use getPage() for each
|
||||
if (
|
||||
options.page_numbers &&
|
||||
Array.isArray(options.page_numbers) &&
|
||||
options.page_numbers.length > 0
|
||||
) {
|
||||
for (const pageNum of options.page_numbers) {
|
||||
// page numbers are 1-indexed in the API
|
||||
let image = await document.getPage(pageNum);
|
||||
|
||||
// Resize with Sharp if width or height are provided
|
||||
if (options.width || options.height) {
|
||||
const resizeOptions = {};
|
||||
if (options.width) resizeOptions.width = options.width;
|
||||
if (options.height) resizeOptions.height = options.height;
|
||||
image = await sharp(image).resize(resizeOptions).toBuffer();
|
||||
}
|
||||
|
||||
outputImages.push(image);
|
||||
}
|
||||
} else {
|
||||
// Convert all pages: async iterable to array
|
||||
for await (const image of document) {
|
||||
let processedImage = image;
|
||||
|
||||
// Resize with Sharp if width or height are provided
|
||||
if (options.width || options.height) {
|
||||
const resizeOptions = {};
|
||||
if (options.width) resizeOptions.width = options.width;
|
||||
if (options.height) resizeOptions.height = options.height;
|
||||
processedImage = await sharp(image).resize(resizeOptions).toBuffer();
|
||||
}
|
||||
|
||||
outputImages.push(processedImage);
|
||||
}
|
||||
}
|
||||
|
||||
return outputImages; // Array of image buffers
|
||||
} catch (error) {
|
||||
console.error("Error converting PDF to image:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user