diff --git a/package.json b/package.json index 096621b..1b1f1e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fieldlens/print", - "version": "0.1.11", + "version": "0.1.12", "private": true, "type": "module", "scripts": { diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fe2e7df..e6eb0c5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fieldlens-print" -version = "0.1.11" +version = "0.1.12" 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 7581252..7f1e2bb 100644 --- a/src-tauri/src/printers.rs +++ b/src-tauri/src/printers.rs @@ -25,85 +25,91 @@ 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 -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): 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")) -content_type = os.environ.get("CONTENT_TYPE", "application/pdf") -is_test = printer.lower() == "fieldlens-test-capture" -prefix = "test-" if is_test else "print-" -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) +prefix = "test-" if printer.lower() == "fieldlens-test-capture" else "print-" +name = f"{prefix}{printer}-{job_id}-{title}.pdf" +final_path = os.path.join(CACHE, name) part_path = final_path + ".part" -source = None -capture = None -captured_ok = False +input_path = None +temporary_input = None + try: if len(sys.argv) > 6 and sys.argv[6]: - source = open(sys.argv[6], "rb") + input_path = sys.argv[6] 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 - # bytes onward. Otherwise tee the pre-driver document into CUPS_CACHEDIR. - if not os.path.exists(final_path): - try: - 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 + 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) + except OSError as error: + print(f"WARNING: FieldLens capture cache failed: {error}", file=sys.stderr) - while True: - chunk = source.read(65536) - if not chunk: - break + pdftoraster = find_pdftoraster() + if pdftoraster is None: + print("ERROR: FieldLens could not find the standard CUPS pdftoraster filter", file=sys.stderr) + sys.exit(1) - 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 + env = os.environ.copy() + env["FINAL_CONTENT_TYPE"] = "image/pwg-raster" - sys.stdout.buffer.write(chunk) + 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.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) + result = subprocess.run( + args, + stdout=sys.stdout.buffer, + stderr=sys.stderr.buffer, + env=env, + ) + sys.exit(result.returncode) finally: - 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: + if temporary_input is not None: try: - os.unlink(part_path) + os.unlink(temporary_input) except OSError: pass + try: + os.unlink(part_path) + except OSError: + pass "#; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -583,9 +589,9 @@ fn build_fieldlens_ppd(target_printer: &str) -> Result { cleaned.push('\n'); let prefilter = concat!( - "*% FieldLens original-document capture before printer rendering\n", - "*cupsPreFilter: \"application/pdf 0 fieldlens-capture\"\n", - "*cupsPreFilter: \"application/vnd.cups-pdf 0 fieldlens-capture\"\n" + "*% 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" ); 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 c152fe7..bb499b3 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.11", + "version": "0.1.12", "identifier": "dev.arrowstack.fieldlens.print", "build": { "beforeDevCommand": "npm run dev", diff --git a/src/main.ts b/src/main.ts index 147373d..50b514f 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.11' +let currentVersion = '0.1.12' let bundleType: BundleType | null = null let availableUpdate: Update | null = null let availableManualUpdate: ManualUpdate | null = null