new-site/scripts/tests/test_mcs150_signature_placement.py
justin 7ed06780bb trucking: stamp e-signature exactly on form signature lines + state authorization gate
Capture-to-form signature placement so the customer's drawn or typed
signature lands right on the signature rule of the actual form, not in a
sidecar page.

- migration 085: esign_records.signature_anchors (JSONB exact PDF coords,
  lower-left origin, points) + signed_document_minio_key
- signature_stamper.py: signature_box() anchors; anchors_from_acroform()
  pulls the signature field /Rect from a real AcroForm (e.g. MCS-150
  certifySignature); stamp_signature() overlays PNG (auto-trimmed so ink
  rests on the rule) or typed name, scaled to actual page size
- state_trucking_authorization.py: renders the Limited Authorization to
  File PDF and returns (pdf_bytes, anchors)
- esign_stamp.py: stamp_esign_document() downloads unsigned PDF, stamps,
  uploads _signed.pdf, sets signed_document_minio_key (idempotent)
- dot_esign.py: extract certifySignature anchor for MCS-150/closeout forms
  so the federal perjury cert is signed on the line
- state_trucking.py: authorization gate — first run emails signing link
  and PAUSES; resumes with client_approved after signing
- job_server handle_esign_completed: stamp then re-dispatch
- tests: test_signature_placement.py (custom form), and
  test_mcs150_signature_placement.py (official AcroForm) both assert the
  signature lands inside the recorded signature box (verified visually)
2026-06-02 16:44:19 -05:00

117 lines
4 KiB
Python

"""Verify the captured signature lands on the official MCS-150 signature line.
Fills the real MCS-150 AcroForm, extracts the ``certifySignature`` anchor from
the form's own field rectangle, stamps a synthetic drawn signature, renders the
signature page, and asserts a meaningful number of dark pixels fall inside the
recorded signature box (i.e. the signature is on the rule, not floating).
Requires: reportlab, pypdf, pillow, and (for rendering) pdftoppm.
Run from the repo root with a venv that has those installed.
"""
from __future__ import annotations
import base64
import importlib.util
import io
import os
import subprocess
import sys
import tempfile
REPO = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
def _load(name: str, rel: str):
spec = importlib.util.spec_from_file_location(name, os.path.join(REPO, rel))
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod) # type: ignore[union-attr]
return mod
def _synthetic_signature_png() -> str:
from PIL import Image, ImageDraw
im = Image.new("RGBA", (600, 140), (0, 0, 0, 0))
d = ImageDraw.Draw(im)
d.line(
[(20, 110), (120, 30), (220, 110), (340, 40), (480, 100), (580, 50)],
fill=(10, 20, 60, 255),
width=6,
)
buf = io.BytesIO()
im.save(buf, "PNG")
return base64.b64encode(buf.getvalue()).decode()
def main() -> int:
sys.path.insert(0, REPO)
mcs = _load("mcs150_pdf_filler", "scripts/document_gen/templates/mcs150_pdf_filler.py")
stamper = _load("signature_stamper", "scripts/workers/services/signature_stamper.py")
intake = {
"legal_name": "ADAMS LUMBER INC",
"dot_number": "1157913",
"entity_type": "corporation",
"carrier_operation": "authorized_for_hire",
"interstate_intrastate": "interstate",
"hazmat": "no",
"power_units": "5",
"drivers": "6",
"annual_miles": "250000",
"cargo_types": ["general"],
"signer_name": "Mark Adams",
"signer_title": "President",
"address_state": "OR",
"email": "m@a.com",
}
pdf_path = mcs.fill_mcs150(intake, order_number="TEST-MCS150-SIG")
pdf_bytes = open(pdf_path, "rb").read()
anchors = stamper.anchors_from_acroform(pdf_bytes, {"certifySignature": "signer"})
assert anchors, "no signature anchor extracted from MCS-150 AcroForm"
box = anchors[0]
print("signature anchor:", box)
signed = stamper.stamp_signature(
pdf_bytes,
anchors,
signature_type="drawn",
signature_data=_synthetic_signature_png(),
)
with tempfile.TemporaryDirectory() as td:
signed_path = os.path.join(td, "signed.pdf")
open(signed_path, "wb").write(signed)
page_no = box["page"] + 1
prefix = os.path.join(td, "render")
subprocess.run(
["pdftoppm", "-png", "-f", str(page_no), "-l", str(page_no), "-r", "150", signed_path, prefix],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
# pdftoppm zero-pads the page number; find the produced PNG.
png = next(
os.path.join(td, f) for f in sorted(os.listdir(td)) if f.startswith("render") and f.endswith(".png")
)
from PIL import Image
im = Image.open(png).convert("L")
W, H = im.size
px = im.load()
sc = 150 / 72.0
bx, by, bw, bh = box["x"], box["y"], box["w"], box["h"]
dark = 0
for X in range(int(bx * sc), int((bx + bw) * sc)):
for Y in range(int(H - (by + bh) * sc), int(H - by * sc)):
if 0 <= X < W and 0 <= Y < H and px[X, Y] < 120:
dark += 1
print("dark signature pixels inside MCS-150 signature box:", dark)
assert dark > 500, f"signature not on the line (only {dark} dark px in box)"
print("PASS: signature stamped on the MCS-150 signature line")
return 0
if __name__ == "__main__":
raise SystemExit(main())