52 lines
3.5 KiB
Python
52 lines
3.5 KiB
Python
"""Check actual API-exported European-name controls; keep human visual approval separate."""
|
|
import argparse
|
|
from collections import Counter
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import re
|
|
import subprocess
|
|
import unicodedata
|
|
import pdfplumber
|
|
|
|
parser=argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('directory',type=Path)
|
|
parser.add_argument('--kind',choices=['cv','letter'],required=True)
|
|
parser.add_argument('--poppler',type=Path,required=True)
|
|
args=parser.parse_args()
|
|
source=json.loads((args.directory/'measurements.json').read_text(encoding='utf-8'))
|
|
rows=source['europeanControls']
|
|
assert len(rows)==9 and {row['settings']['themeId'] for row in rows}=={'modern','minimal','executive','technical','ats-classic','nordic','elegant','creative','code'}
|
|
results=[]
|
|
for row in rows:
|
|
assert re.fullmatch(r'european-(no-photo|en)-[a-z-]+',row['fixture'])
|
|
folder=args.directory/row['fixture'];file=folder/'export.pdf'
|
|
assert file.stat().st_size<=8*1024*1024
|
|
assert hashlib.sha256(file.read_bytes()).hexdigest()==row['pdfSha256']
|
|
assert hashlib.sha256((folder/'render.html').read_bytes()).hexdigest()==row['htmlSha256']
|
|
assert row['nameInkClearancePx']>=0
|
|
if args.kind=='cv':assert row['settings']['showPhoto'] is False and row['measured']['photo'] is False
|
|
name=row['expectedFullName'] if args.kind=='cv' else row['expectedSender']
|
|
failures=[];texts=[];pages=[]
|
|
with pdfplumber.open(file) as document:
|
|
assert len(document.pages)==row['previewPages'] and 1<=len(document.pages)<=64
|
|
for number,page in enumerate(document.pages,1):
|
|
text=unicodedata.normalize('NFC',page.extract_text() or '')
|
|
texts.append(text)
|
|
outside=[word for word in page.extract_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 not text.strip() or outside:failures.append(f'Page{number} blank or text outside physical page bounds')
|
|
if abs(page.width*25.4/72-210)>.5 or abs(page.height*25.4/72-297)>.5:failures.append('Unexpected page dimensions')
|
|
pages.append(dict(page=number,widthPt=page.width,heightPt=page.height))
|
|
text='\n'.join(texts)
|
|
if Counter(re.findall(r'\b[SEBPR]\d{2}\b',text))!=Counter(row['expectedMarkers']):failures.append('Source marker multiset differs')
|
|
for word in name.split():
|
|
if word not in text.split():failures.append('Missing European name word: '+word)
|
|
for number in sorted({1,len(pages)}):
|
|
subprocess.run([str(args.poppler),'-f',str(number),'-l',str(number),'-singlefile','-scale-to','1400','-png',str(file),str(folder/f'poppler-{number}')],check=True,capture_output=True,timeout=30)
|
|
results.append(dict(fixture=row['fixture'],pass_=not failures,failures=failures,pdfSha256=row['pdfSha256'],expectedName=name,previewGlyphClearancePx=row['nameInkClearancePx'],pages=pages))
|
|
report=dict(kind=args.kind,total=9,passed=sum(row['pass_'] for row in results),pages=sum(len(row['pages']) for row in results),
|
|
qualification='Actual API native PDF source-word/marker retention and physical bounds; separate real-preview glyph extents. Text extraction alone cannot certify painted diacritics; native raster/owner approval remains separate.',fixtures=results)
|
|
(args.directory/'european-proof.json').write_bytes((json.dumps(report,ensure_ascii=False,indent=2)+'\n').encode())
|
|
print(f"European {args.kind} controls: {report['passed']}/9 native documents; {report['pages']} pages")
|
|
raise SystemExit(0 if report['passed']==9 else 1)
|