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-renderer": "vite build src/electron --outDir build/electron",
|
||||||
"build:electron": "pnpm build && electron-builder",
|
"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: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": "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": "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-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"
|
"cleanBuild": "rimraf build"
|
||||||
},
|
},
|
||||||
"author": "Tom Butcher",
|
"author": "Tom Butcher",
|
||||||
@ -70,11 +72,12 @@
|
|||||||
],
|
],
|
||||||
"assets": [
|
"assets": [
|
||||||
"build/config.json",
|
"build/config.json",
|
||||||
|
"node_modules/pdf-to-img/**/*",
|
||||||
|
"node_modules/pdfjs-dist/**/*",
|
||||||
"node_modules/sharp/**/*",
|
"node_modules/sharp/**/*",
|
||||||
"node_modules/@img/**/*",
|
"node_modules/@img/**/*",
|
||||||
"node_modules/canvas/**/*",
|
"node_modules/canvas/**/*",
|
||||||
"node_modules/@napi-rs/**/*",
|
"node_modules/@napi-rs/**/*"
|
||||||
"node_modules/pdf-to-img/**/*"
|
|
||||||
],
|
],
|
||||||
"targets": [
|
"targets": [
|
||||||
"node18-linux-x64"
|
"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 log4js from "log4js";
|
||||||
import { loadConfig } from "../../config.js";
|
import { loadConfig } from "../../config.js";
|
||||||
import { ThermalPrinter, PrinterTypes } from "node-thermal-printer";
|
import { ThermalPrinter, PrinterTypes } from "node-thermal-printer";
|
||||||
import { convertPDFToImage } from "../../utils.js";
|
import { convertPDFToImage } from "../../pdfUtils.js";
|
||||||
|
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
const logger = log4js.getLogger("Receipt Printer Interface");
|
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 readline from "node:readline";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import { pdf } from "pdf-to-img";
|
|
||||||
import sharp from "sharp";
|
|
||||||
|
|
||||||
let isPrompting = false; // prevent multiple prompts at the same time
|
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