feat(cli): add shared PDF-to-image rendering and document usage
add shared render_pdf_pages helper for consistent rasterization reuse the same renderer in regression test and CLI flow add new render-image CLI command to export PDF pages as PNG support single-page render (--page) and full export (--all-pages) add configurable output path and DPI options document the new rendering command and options in README
This commit is contained in:
1 parent
34fe877bb2
commit
34e9aa49d6
8 files changed
+188
-55
No files matched your search
@@ -64,6 +64,9 @@ def load_config(config_path: Path | None = None) -> Dict[str, Any]:
|
||||
f"config.json fehlen Pflichtfelder: {', '.join(missing)}"
|
||||
)
|
||||
|
||||
# Optional settings with backward-compatible defaults
|
||||
data.setdefault("pdf_need_appearances", False)
|
||||
|
||||
_config_cache = data
|
||||
_config_path_cache = config_path
|
||||
return data
|
||||
|
||||
+25
-4
@@ -247,6 +247,14 @@ def fill_pdf(
|
||||
|
||||
_apply_opt_mappings(fields, opt_maps)
|
||||
|
||||
SUMMARY_FIELDS_FOR_AP = (
|
||||
"anwesend_sum",
|
||||
"anwesend_gessum",
|
||||
"pauschale_5_sum",
|
||||
"pauschale_25_sum",
|
||||
"pauschale_sum",
|
||||
)
|
||||
|
||||
writer = PdfWriter()
|
||||
writer.clone_document_from_reader(reader)
|
||||
|
||||
@@ -255,11 +263,24 @@ def fill_pdf(
|
||||
|
||||
_update_widget_visibility(writer, fields)
|
||||
|
||||
# Tell the PDF viewer to regenerate appearance streams from field values.
|
||||
# Without this flag, dropdown fields (plan, pauschale) display stale text
|
||||
# instead of their actual selected option (e.g. "8,60" for pauschale).
|
||||
# Second pass: regenerate AP only for summary text fields.
|
||||
# This keeps footer/buttons visible with NeedAppearances=False while still
|
||||
# rendering summary values in image-based comparisons.
|
||||
summary_fields = {
|
||||
name: fields[name]
|
||||
for name in SUMMARY_FIELDS_FOR_AP
|
||||
if name in fields
|
||||
}
|
||||
if summary_fields:
|
||||
for page in writer.pages:
|
||||
writer.update_page_form_field_values(page, summary_fields, auto_regenerate=True)
|
||||
|
||||
# Configure viewer-side appearance regeneration via config switch.
|
||||
# Default is False for stable pymupdf rendering (footer/buttons visible).
|
||||
if "/AcroForm" in writer._root_object:
|
||||
writer._root_object["/AcroForm"][NameObject("/NeedAppearances")] = BooleanObject(True)
|
||||
writer._root_object["/AcroForm"][NameObject("/NeedAppearances")] = BooleanObject(
|
||||
bool(config.get("pdf_need_appearances", False))
|
||||
)
|
||||
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with output_path.open("wb") as fh:
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
"""Shared PDF rasterization helpers used by tests and CLI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pymupdf
|
||||
|
||||
|
||||
def render_pdf_pages(pdf_path: Path, dpi: int = 150) -> list[np.ndarray]:
|
||||
"""Render all pages of *pdf_path* to RGB numpy arrays using pymupdf."""
|
||||
zoom = dpi / 72.0
|
||||
mat = pymupdf.Matrix(zoom, zoom)
|
||||
doc = pymupdf.open(str(pdf_path))
|
||||
images: list[np.ndarray] = []
|
||||
for page in doc:
|
||||
pix = page.get_pixmap(matrix=mat)
|
||||
img = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.h, pix.w, pix.n)
|
||||
if pix.n == 4:
|
||||
img = img[:, :, :3]
|
||||
images.append(img.copy())
|
||||
doc.close()
|
||||
return images
|
||||
Reference in new issue
Block a user