"""Independent PDF retention/bounds and Poppler contact-sheet proof for 18 letters. Keeps the original four-letter corpus and all fidelity tolerances untouched. """ import argparse import hashlib import json import re import subprocess import unicodedata from collections import Counter from pathlib import Path import pdfplumber from PIL import Image, ImageDraw def normalize(text): return re.sub(r"\s+", "", unicodedata.normalize("NFKC", text)).casefold() def main(): parser = argparse.ArgumentParser() parser.add_argument("directory", type=Path) parser.add_argument("--poppler", type=Path, required=True) args = parser.parse_args() source = json.loads((args.directory / "measurements.json").read_text(encoding="utf-8")) fixtures = [] for fixture in source["fixtures"]: directory = args.directory / fixture["fixture"] filename = directory / "export.pdf" failures, pages, markers, texts = [], [], [], [] columns = [[], []] sidebar = fixture["measured"]["pageDisplay"] == "grid" with pdfplumber.open(filename) as document: for number, page in enumerate(document.pages, 1): text = page.extract_text() or "" texts.append(text) if sidebar: # PDF line extraction otherwise interleaves separate sidebar/body columns. boundary = (210 - 68 if fixture["settings"]["themeId"] == "nordic" else 68) * 72 / 25.4 for index, bounds in enumerate(((0, 0, boundary, page.height), (boundary, 0, page.width, page.height))): columns[index].append(page.crop(bounds).extract_text() or "") markers.extend(re.findall(r"\b[SEBPR]\d{2}\b", text)) words = page.extract_words() outside = [word for word in words if word["x0"] < -1.42 or word["top"] < -1.42 or word["x1"] > page.width + 1.42 or word["bottom"] > page.height + 1.42] if outside or not words: failures.append(f"page {number}: {len(outside)} outside words; {len(words)} total words") if abs(page.width * 25.4 / 72 - 210) > .5 or abs(page.height * 25.4 / 72 - 297) > .5: failures.append(f"page {number}: unexpected A4 dimensions") pages.append({"page": number, "widthPt": page.width, "heightPt": page.height, "words": len(words), "outsideWords": outside}) combined = normalize("\n".join("\n".join(column) for column in columns) if sidebar else "\n".join(texts)) for text in fixture["expectedParagraphs"] + [fixture["expectedRecipient"], fixture["expectedSender"], fixture["expectedSubject"]]: if normalize(text) not in combined: failures.append("Missing or changed source text: " + text[:90]) if Counter(markers) != Counter(fixture["expectedMarkers"]): failures.append("PDF/source marker multisets differ") if len(pages) != fixture["previewPages"]: failures.append("PDF/preview page count differs") for page in range(1, len(pages) + 1): subprocess.run([str(args.poppler), "-f", str(page), "-l", str(page), "-singlefile", "-scale-to", "1400", "-png", str(filename), str(directory / f"poppler-{page}")], check=True, capture_output=True) fixtures.append({"fixture": fixture["fixture"], "pass": not failures, "failures": failures, "pdfSha256": hashlib.sha256(filename.read_bytes()).hexdigest(), "pages": pages, "expectedMarkers": len(fixture["expectedMarkers"]), "actualMarkers": len(markers)}) report = {"total": len(fixtures), "passed": sum(item["pass"] for item in fixtures), "pages": sum(len(item["pages"]) for item in fixtures), "fixtures": fixtures, "qualification": "Finite local synthetic retention, page dimensions/count and physical word bounds; sidebar text extracted by physical columns to avoid line interleaving. Not human print acceptance."} (args.directory / "pdf-proof.json").write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding="utf-8") for language in ("short-en", "long-nb"): for edge in ("first", "last"): sheet = Image.new("RGB", (900, 1365), "#e5e7eb") draw = ImageDraw.Draw(sheet) for index, fixture in enumerate([item for item in fixtures if item["fixture"].startswith(language + "-")]): number = 1 if edge == "first" else len(fixture["pages"]) with Image.open(args.directory / fixture["fixture"] / f"poppler-{number}.png") as image: image.thumbnail((286, 414)) x, y = (index % 3) * 300 + 7, (index // 3) * 455 + 32 sheet.paste(image, (x, y)) draw.text((x, y - 25), f"{fixture['fixture']} p{number}/{len(fixture['pages'])}", fill="#111827") sheet.save(args.directory / f"contact-{language}-{edge}.png") print(f"Matching letter physical proof: {report['passed']}/{report['total']} documents, {report['pages']} pages") for item in fixtures: if not item["pass"]: print(item["fixture"], item["failures"]) raise SystemExit(0 if report["passed"] == report["total"] else 1) if __name__ == "__main__": main()