- core/printing.py: SumatraPDF silent print backend (dry_run, timeout, error capture) - core/print_document.py: orchestrate generate() + print, per-report paper size, printer fallback - core/print_api.py: FastAPI POST /api/print + GET /api/health, HTTPBearer auth, English Swagger at /api/docs - serve.py: uvicorn entrypoint - config: printers/print/api sections in settings.yaml + core/settings.py - requirements.txt: add fastapi, uvicorn - .gitignore: ignore logs/
65 lines
2.0 KiB
Python
65 lines
2.0 KiB
Python
"""打印后端:把已生成的 PDF 静默送到物理/网络打印机。
|
||
|
||
MVP 使用 SumatraPDF 命令行打印(轻量、支持 -print-to 指定打印机与纸张,
|
||
配合 -silent 适合服务端无人值守打印)。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import subprocess
|
||
from pathlib import Path
|
||
|
||
|
||
def build_command(
|
||
sumatrapdf_path: str, printer: str, settings: str, pdf_path: str
|
||
) -> list[str]:
|
||
"""构造 SumatraPDF 打印命令。"""
|
||
return [
|
||
sumatrapdf_path,
|
||
"-print-to",
|
||
printer,
|
||
"-print-settings",
|
||
settings,
|
||
"-silent",
|
||
pdf_path,
|
||
]
|
||
|
||
|
||
def print_pdf(
|
||
pdf_path: str | Path,
|
||
printer: str,
|
||
settings: str = "paper=A4,orientation=portrait,color=bw",
|
||
sumatrapdf_path: str = "C:/Program Files/SumatraPDF/SumatraPDF.exe",
|
||
timeout: int = 60,
|
||
dry_run: bool = False,
|
||
) -> dict:
|
||
"""调用 SumatraPDF 把 PDF 静默打印到指定打印机。
|
||
|
||
返回结构化结果;dry_run=True 时只构造命令并上报,不真正执行(用于验证链路)。
|
||
"""
|
||
pdf_path = str(pdf_path)
|
||
cmd = build_command(sumatrapdf_path, printer, settings, pdf_path)
|
||
result: dict = {
|
||
"printer": printer,
|
||
"pdf": pdf_path,
|
||
"command": cmd,
|
||
"dry_run": dry_run,
|
||
}
|
||
if dry_run:
|
||
result["ok"] = True
|
||
result["note"] = "dry_run: command constructed, not executed"
|
||
return result
|
||
try:
|
||
proc = subprocess.run(
|
||
cmd, capture_output=True, text=True, timeout=timeout, check=False
|
||
)
|
||
result["returncode"] = proc.returncode
|
||
result["stdout"] = proc.stdout
|
||
result["stderr"] = proc.stderr
|
||
result["ok"] = proc.returncode == 0
|
||
if proc.returncode != 0:
|
||
result["error"] = (proc.stderr or proc.stdout or "unknown error").strip()
|
||
except subprocess.TimeoutExpired:
|
||
result["ok"] = False
|
||
result["error"] = f"print timeout after {timeout}s"
|
||
return result
|