fix(pdf): preserve accented names in revised CV and letter headings

This commit is contained in:
cesnimda
2026-10-29 23:38:43 +01:00
parent 38d7133d20
commit 154a9e89f5
13 changed files with 235 additions and 13 deletions
@@ -0,0 +1,51 @@
"""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)