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
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:
parent
6df39bcb70
commit
408e01f212
@ -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": {
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -25,85 +25,91 @@ 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)
|
os.chmod(part_path, 0o644)
|
||||||
capture = open(part_path, "wb")
|
os.replace(part_path, final_path)
|
||||||
except OSError as error:
|
print(f"INFO: FieldLens captured {name}", file=sys.stderr)
|
||||||
print(f"WARNING: FieldLens capture cache unavailable: {error}", file=sys.stderr)
|
except OSError as error:
|
||||||
capture = None
|
print(f"WARNING: FieldLens capture cache failed: {error}", file=sys.stderr)
|
||||||
|
|
||||||
while True:
|
pdftoraster = find_pdftoraster()
|
||||||
chunk = source.read(65536)
|
if pdftoraster is None:
|
||||||
if not chunk:
|
print("ERROR: FieldLens could not find the standard CUPS pdftoraster filter", file=sys.stderr)
|
||||||
break
|
sys.exit(1)
|
||||||
|
|
||||||
if capture is not None:
|
env = os.environ.copy()
|
||||||
try:
|
env["FINAL_CONTENT_TYPE"] = "image/pwg-raster"
|
||||||
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)
|
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()
|
result = subprocess.run(
|
||||||
|
args,
|
||||||
if capture is not None:
|
stdout=sys.stdout.buffer,
|
||||||
capture.flush()
|
stderr=sys.stderr.buffer,
|
||||||
capture.close()
|
env=env,
|
||||||
capture = None
|
)
|
||||||
os.chmod(part_path, 0o644)
|
sys.exit(result.returncode)
|
||||||
os.replace(part_path, final_path)
|
|
||||||
captured_ok = True
|
|
||||||
print(f"INFO: FieldLens captured {name}", file=sys.stderr)
|
|
||||||
finally:
|
finally:
|
||||||
if source is not None and source is not sys.stdin.buffer:
|
if temporary_input is not None:
|
||||||
source.close()
|
|
||||||
if capture is not None:
|
|
||||||
capture.close()
|
|
||||||
if not captured_ok:
|
|
||||||
try:
|
try:
|
||||||
os.unlink(part_path)
|
os.unlink(temporary_input)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
try:
|
||||||
|
os.unlink(part_path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user