From 7e5946d65a51e1ad5624ffed33fcaad39f20d486 Mon Sep 17 00:00:00 2001 From: justin Date: Wed, 10 Jun 2026 12:20:45 -0500 Subject: [PATCH] fix(mcs150 pdf): set /NeedAppearances so filled field values actually render Customer saw the MCS-150 looking blank / 'data covered by the form fields': the values were correctly written to the AcroForm /V, but pypdf left the template's empty /AP appearance streams in place and NeedAppearances was false, so viewers rendered the blank widget over the value. Setting AcroForm /NeedAppearances=true makes viewers regenerate appearances from the values. (The missing signature was a downstream effect of the separate fobj_put MinIO-upload bug, now fixed -- with no PDF in MinIO the anchor extraction + signature stamping both failed.) --- .../document_gen/templates/mcs150_pdf_filler.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/scripts/document_gen/templates/mcs150_pdf_filler.py b/scripts/document_gen/templates/mcs150_pdf_filler.py index 9a9dd01..f52936e 100644 --- a/scripts/document_gen/templates/mcs150_pdf_filler.py +++ b/scripts/document_gen/templates/mcs150_pdf_filler.py @@ -245,6 +245,20 @@ def fill_mcs150(intake: dict, order_number: str = "") -> str: except Exception as e: LOG.debug("Checkbox %s set failed: %s", field_name, e) + # Force viewers to (re)generate field appearance streams from the values we + # set. Without /NeedAppearances, pypdf leaves the template's blank /AP streams + # in place, so the typed values are present in /V but the viewer renders the + # empty widget on top -- the data looks missing / "covered up by the form + # field". Setting NeedAppearances=true on the AcroForm fixes the rendering. + try: + catalog = writer._root_object + if "/AcroForm" in catalog: + acro = catalog["/AcroForm"] + acro_obj = acro.get_object() if hasattr(acro, "get_object") else acro + acro_obj[NameObject("/NeedAppearances")] = BooleanObject(True) + except Exception as exc: + LOG.warning("Could not set NeedAppearances: %s", exc) + # Save work_dir = tempfile.mkdtemp(prefix="pw_mcs150_") dot = intake.get("dot_number", "unknown")