fix(linux): make FieldLens an explicit PDF to PWG CUPS filter
Some checks failed
Desktop Release / Bootstrap updater signing key (push) Has been cancelled
Desktop Release / Windows x64 NSIS (push) Has been cancelled
Desktop Release / Linux x64 DEB + AppImage (push) Has been cancelled
Desktop Release / Publish public desktop release (push) Has been cancelled

This commit is contained in:
Admin 2026-09-24 07:05:08 +00:00
parent 6df39bcb70
commit 408e01f212
5 changed files with 68 additions and 62 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@fieldlens/print", "name": "@fieldlens/print",
"version": "0.1.11", "version": "0.1.12",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "fieldlens-print" name = "fieldlens-print"
version = "0.1.11" version = "0.1.12"
description = "FieldLens print-stream barcode capture companion" description = "FieldLens print-stream barcode capture companion"
authors = ["Arrowstack"] authors = ["Arrowstack"]
edition = "2021" edition = "2021"

View File

@ -25,81 +25,87 @@ const FIELDLENS_CUPS_SPOOL_DIR: &str = "/var/cache/cups/fieldlens-print";
const FIELDLENS_CUPS_FILTER: &str = r#"#!/usr/bin/python3 const FIELDLENS_CUPS_FILTER: &str = r#"#!/usr/bin/python3
import os import os
import re import re
import shutil
import subprocess
import sys import sys
import tempfile
SPOOL = os.path.join(os.environ.get("CUPS_CACHEDIR", "/var/cache/cups"), "fieldlens-print") CACHE = os.path.join(os.environ.get("CUPS_CACHEDIR", "/var/cache/cups"), "fieldlens-print")
def safe(value): def safe(value):
value = re.sub(r"[^A-Za-z0-9._-]+", "-", value or "job").strip("-") value = re.sub(r"[^A-Za-z0-9._-]+", "-", value or "job").strip("-")
return value[:80] or "job" return value[:80] or "job"
def find_pdftoraster():
for path in (
"/usr/lib/cups/filter/pdftoraster",
"/usr/libexec/cups/filter/pdftoraster",
):
if os.path.isfile(path) and os.access(path, os.X_OK):
return path
return None
job_id = safe(sys.argv[1] if len(sys.argv) > 1 else "job") job_id = safe(sys.argv[1] if len(sys.argv) > 1 else "job")
title = safe(sys.argv[3] if len(sys.argv) > 3 else "print") title = safe(sys.argv[3] if len(sys.argv) > 3 else "print")
printer = safe(os.environ.get("PRINTER", "printer")) printer = safe(os.environ.get("PRINTER", "printer"))
content_type = os.environ.get("CONTENT_TYPE", "application/pdf") prefix = "test-" if printer.lower() == "fieldlens-test-capture" else "print-"
is_test = printer.lower() == "fieldlens-test-capture" name = f"{prefix}{printer}-{job_id}-{title}.pdf"
prefix = "test-" if is_test else "print-" final_path = os.path.join(CACHE, name)
ext = "pdf" if "pdf" in content_type else "dat"
# Use a deterministic name so multiple PDF pre-filter stages for the same
# CUPS job do not create duplicate FieldLens jobs.
name = f"{prefix}{printer}-{job_id}-{title}.{ext}"
final_path = os.path.join(SPOOL, name)
part_path = final_path + ".part" part_path = final_path + ".part"
source = None input_path = None
capture = None temporary_input = None
captured_ok = False
try: try:
if len(sys.argv) > 6 and sys.argv[6]: if len(sys.argv) > 6 and sys.argv[6]:
source = open(sys.argv[6], "rb") input_path = sys.argv[6]
else: else:
source = sys.stdin.buffer fd, temporary_input = tempfile.mkstemp(prefix="fieldlens-cups-", suffix=".pdf")
with os.fdopen(fd, "wb") as target:
shutil.copyfileobj(sys.stdin.buffer, target)
input_path = temporary_input
# If an earlier pre-filter stage already captured this job, only pass the try:
# bytes onward. Otherwise tee the pre-driver document into CUPS_CACHEDIR. os.makedirs(CACHE, exist_ok=True)
if not os.path.exists(final_path): if not os.path.exists(final_path):
try: shutil.copyfile(input_path, part_path)
os.makedirs(SPOOL, exist_ok=True)
capture = open(part_path, "wb")
except OSError as error:
print(f"WARNING: FieldLens capture cache unavailable: {error}", file=sys.stderr)
capture = None
while True:
chunk = source.read(65536)
if not chunk:
break
if capture is not None:
try:
capture.write(chunk)
except OSError as error:
print(f"WARNING: FieldLens capture write failed: {error}", file=sys.stderr)
try:
capture.close()
except OSError:
pass
capture = None
sys.stdout.buffer.write(chunk)
sys.stdout.buffer.flush()
if capture is not None:
capture.flush()
capture.close()
capture = None
os.chmod(part_path, 0o644) os.chmod(part_path, 0o644)
os.replace(part_path, final_path) os.replace(part_path, final_path)
captured_ok = True
print(f"INFO: FieldLens captured {name}", file=sys.stderr) print(f"INFO: FieldLens captured {name}", file=sys.stderr)
except OSError as error:
print(f"WARNING: FieldLens capture cache failed: {error}", file=sys.stderr)
pdftoraster = find_pdftoraster()
if pdftoraster is None:
print("ERROR: FieldLens could not find the standard CUPS pdftoraster filter", file=sys.stderr)
sys.exit(1)
env = os.environ.copy()
env["FINAL_CONTENT_TYPE"] = "image/pwg-raster"
args = [
pdftoraster,
sys.argv[1] if len(sys.argv) > 1 else "0",
sys.argv[2] if len(sys.argv) > 2 else "unknown",
sys.argv[3] if len(sys.argv) > 3 else "FieldLens Print",
sys.argv[4] if len(sys.argv) > 4 else "1",
sys.argv[5] if len(sys.argv) > 5 else "",
input_path,
]
result = subprocess.run(
args,
stdout=sys.stdout.buffer,
stderr=sys.stderr.buffer,
env=env,
)
sys.exit(result.returncode)
finally: finally:
if source is not None and source is not sys.stdin.buffer: if temporary_input is not None:
source.close() try:
if capture is not None: os.unlink(temporary_input)
capture.close() except OSError:
if not captured_ok: pass
try: try:
os.unlink(part_path) os.unlink(part_path)
except OSError: except OSError:
@ -583,9 +589,9 @@ fn build_fieldlens_ppd(target_printer: &str) -> Result<PathBuf> {
cleaned.push('\n'); cleaned.push('\n');
let prefilter = concat!( let prefilter = concat!(
"*% FieldLens original-document capture before printer rendering\n", "*% FieldLens original-document capture and PDF-to-PWG conversion\n",
"*cupsPreFilter: \"application/pdf 0 fieldlens-capture\"\n", "*cupsFilter2: \"application/pdf image/pwg-raster 10 fieldlens-capture\"\n",
"*cupsPreFilter: \"application/vnd.cups-pdf 0 fieldlens-capture\"\n" "*cupsFilter2: \"application/vnd.cups-pdf image/pwg-raster 10 fieldlens-capture\"\n"
); );
if let Some(index) = cleaned.rfind("*% End") { if let Some(index) = cleaned.rfind("*% End") {
cleaned.insert_str(index, prefilter); cleaned.insert_str(index, prefilter);

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "FieldLens Print", "productName": "FieldLens Print",
"version": "0.1.11", "version": "0.1.12",
"identifier": "dev.arrowstack.fieldlens.print", "identifier": "dev.arrowstack.fieldlens.print",
"build": { "build": {
"beforeDevCommand": "npm run dev", "beforeDevCommand": "npm run dev",

View File

@ -85,7 +85,7 @@ let config: AppConfig
let jobs: Job[] = [] let jobs: Job[] = []
let status: Status let status: Status
let loginTimer: number | undefined let loginTimer: number | undefined
let currentVersion = '0.1.11' let currentVersion = '0.1.12'
let bundleType: BundleType | null = null let bundleType: BundleType | null = null
let availableUpdate: Update | null = null let availableUpdate: Update | null = null
let availableManualUpdate: ManualUpdate | null = null let availableManualUpdate: ManualUpdate | null = null