From 47db9ac867482d672b42fadb0f1de018a440c71c Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 24 Sep 2026 08:41:42 +0000 Subject: [PATCH] fix(linux): capture before cups-filters universal conversion --- package.json | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/src/printers.rs | 96 +++++++++++++++++---------------------- src-tauri/tauri.conf.json | 2 +- src/main.ts | 2 +- 5 files changed, 45 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index 1b1f1e3..064dfc3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fieldlens/print", - "version": "0.1.12", + "version": "0.1.13", "private": true, "type": "module", "scripts": { diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e6eb0c5..24e339c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fieldlens-print" -version = "0.1.12" +version = "0.1.13" description = "FieldLens print-stream barcode capture companion" authors = ["Arrowstack"] edition = "2021" diff --git a/src-tauri/src/printers.rs b/src-tauri/src/printers.rs index 7f1e2bb..b5118f8 100644 --- a/src-tauri/src/printers.rs +++ b/src-tauri/src/printers.rs @@ -25,10 +25,7 @@ const FIELDLENS_CUPS_SPOOL_DIR: &str = "/var/cache/cups/fieldlens-print"; const FIELDLENS_CUPS_FILTER: &str = r#"#!/usr/bin/python3 import os import re -import shutil -import subprocess import sys -import tempfile CACHE = os.path.join(os.environ.get("CUPS_CACHEDIR", "/var/cache/cups"), "fieldlens-print") @@ -36,15 +33,6 @@ def safe(value): value = re.sub(r"[^A-Za-z0-9._-]+", "-", value or "job").strip("-") 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") title = safe(sys.argv[3] if len(sys.argv) > 3 else "print") printer = safe(os.environ.get("PRINTER", "printer")) @@ -53,63 +41,62 @@ name = f"{prefix}{printer}-{job_id}-{title}.pdf" final_path = os.path.join(CACHE, name) part_path = final_path + ".part" -input_path = None -temporary_input = None +source = None +capture = None +captured_ok = False try: if len(sys.argv) > 6 and sys.argv[6]: - input_path = sys.argv[6] + source = open(sys.argv[6], "rb") else: - 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 + source = sys.stdin.buffer try: os.makedirs(CACHE, exist_ok=True) if not os.path.exists(final_path): - shutil.copyfile(input_path, part_path) - os.chmod(part_path, 0o644) - os.replace(part_path, final_path) - print(f"INFO: FieldLens captured {name}", file=sys.stderr) + capture = open(part_path, "wb") except OSError as error: - print(f"WARNING: FieldLens capture cache failed: {error}", file=sys.stderr) + print(f"WARNING: FieldLens capture cache unavailable: {error}", file=sys.stderr) + capture = None - pdftoraster = find_pdftoraster() - if pdftoraster is None: - print("ERROR: FieldLens could not find the standard CUPS pdftoraster filter", file=sys.stderr) - sys.exit(1) + while True: + chunk = source.read(65536) + if not chunk: + break - env = os.environ.copy() - env["FINAL_CONTENT_TYPE"] = "image/pwg-raster" + 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 - 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, - ] + sys.stdout.buffer.write(chunk) - result = subprocess.run( - args, - stdout=sys.stdout.buffer, - stderr=sys.stderr.buffer, - env=env, - ) - sys.exit(result.returncode) + sys.stdout.buffer.flush() + + if capture is not None: + capture.flush() + capture.close() + capture = None + os.chmod(part_path, 0o644) + os.replace(part_path, final_path) + captured_ok = True + print(f"INFO: FieldLens captured {name}", file=sys.stderr) finally: - if temporary_input is not None: + if source is not None and source is not sys.stdin.buffer: + source.close() + if capture is not None: + capture.close() + if not captured_ok: try: - os.unlink(temporary_input) + os.unlink(part_path) except OSError: pass - try: - os.unlink(part_path) - except OSError: - pass "#; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -589,9 +576,8 @@ fn build_fieldlens_ppd(target_printer: &str) -> Result { cleaned.push('\n'); let prefilter = concat!( - "*% FieldLens original-document capture and PDF-to-PWG conversion\n", - "*cupsFilter2: \"application/pdf image/pwg-raster 10 fieldlens-capture\"\n", - "*cupsFilter2: \"application/vnd.cups-pdf image/pwg-raster 10 fieldlens-capture\"\n" + "*% FieldLens original-document capture before cups-filters universal conversion\n", + "*cupsPreFilter: \"application/vnd.universal-input 0 fieldlens-capture\"\n" ); if let Some(index) = cleaned.rfind("*% End") { cleaned.insert_str(index, prefilter); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index bb499b3..d430785 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "FieldLens Print", - "version": "0.1.12", + "version": "0.1.13", "identifier": "dev.arrowstack.fieldlens.print", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/main.ts b/src/main.ts index 50b514f..42daf1a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -85,7 +85,7 @@ let config: AppConfig let jobs: Job[] = [] let status: Status let loginTimer: number | undefined -let currentVersion = '0.1.12' +let currentVersion = '0.1.13' let bundleType: BundleType | null = null let availableUpdate: Update | null = null let availableManualUpdate: ManualUpdate | null = null