feat(cv): qualify templates and reliable local import processing
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
"""Independent byte-bound completeness/bounds report; no presentation-quality goldens.
|
||||
|
||||
Run after the producer and unchanged scripts/verify-cv-fidelity.py. All reported
|
||||
whitespace/name-wrap observations remain measurements for human review.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
import pdfplumber
|
||||
|
||||
|
||||
def inspect(root, fixture):
|
||||
filename = root / fixture["fixture"] / "export.pdf"
|
||||
failures, pages, markers = [], [], []
|
||||
with pdfplumber.open(filename) as document:
|
||||
for index, page in enumerate(document.pages):
|
||||
words = page.extract_words()
|
||||
text = page.extract_text() or ""
|
||||
markers.extend(re.findall(r"\b[SEBPR]\d{2}\b", text))
|
||||
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:
|
||||
failures.append(f"page {index + 1}: {len(outside)} words outside physical page by more than 0.5mm")
|
||||
if not words:
|
||||
failures.append(f"page {index + 1}: blank output")
|
||||
top = min((word["top"] for word in words), default=0)
|
||||
bottom = max((word["bottom"] for word in words), default=0)
|
||||
pages.append({"page": index + 1, "wordCount": len(words), "widthPt": page.width,
|
||||
"heightPt": page.height, "textTopPt": top, "textBottomPt": bottom,
|
||||
"occupiedVerticalFraction": (bottom - top) / page.height,
|
||||
"trailingWhitespaceFraction": (page.height - bottom) / page.height,
|
||||
"outsideWords": outside, "text": text,
|
||||
"embeddedImages": len(page.images)})
|
||||
if Counter(markers) != Counter(fixture["expectedMarkers"]):
|
||||
failures.append("PDF marker multiset differs from independent source fixture")
|
||||
if len(pages) != fixture["previewPages"]:
|
||||
failures.append("PDF/preview page count differs")
|
||||
return {"fixture": fixture["fixture"], "pass": not failures, "failures": failures,
|
||||
"pdfSha256": hashlib.sha256(filename.read_bytes()).hexdigest(), "expectedMarkerCount": len(fixture["expectedMarkers"]),
|
||||
"actualMarkerCount": len(markers), "nameLineCount": fixture["measured"]["nameLineCount"],
|
||||
"splitNameWords": fixture["measured"]["splitNameWords"], "pages": pages}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("directory", type=Path)
|
||||
args = parser.parse_args()
|
||||
measurements = json.loads((args.directory / "measurements.json").read_text(encoding="utf-8"))
|
||||
fixtures = [inspect(args.directory, item) for item in measurements["fixtures"]]
|
||||
result = {"measurementSha256": hashlib.sha256((args.directory / "measurements.json").read_bytes()).hexdigest(),
|
||||
"designVersion": measurements["designVersion"], "total": len(fixtures),
|
||||
"passed": sum(item["pass"] for item in fixtures), "pages": sum(len(item["pages"]) for item in fixtures),
|
||||
"qualification": "Finite synthetic completeness and physical bounds only; whitespace and name wrapping require visual review.",
|
||||
"fixtures": fixtures}
|
||||
(args.directory / "pdf-proof.json").write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
print(f"Source-bound PDF completeness/bounds: {result['passed']}/{result['total']} documents, {result['pages']} pages")
|
||||
for item in fixtures:
|
||||
if not item["pass"]:
|
||||
print(item["fixture"], item["failures"])
|
||||
raise SystemExit(0 if result["passed"] == result["total"] else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user