"""Verify A4 page count, CJK text, embedded glyphs, bookmarks and source evidence.""" import hashlib import json import unicodedata from pathlib import Path from pypdf import PdfReader ROOT=Path(__file__).resolve().parents[1] OUT=ROOT/'docs/teaching/rtcp-beginners' pdf=OUT/'五轴RTCP与工件坐标-初学者完整讲义.pdf' r=PdfReader(pdf) outline=json.loads((OUT/'outline.json').read_text()) layout=json.loads((OUT/'verification/layout.json').read_text()) numeric=json.loads((OUT/'gcode-evidence.json').read_text()) assert len(r.pages)==len(outline)==72 assert numeric['status']=='verified' and numeric['executed_program']['status']=='completed' assert not layout['errors'] and all(not p['violations'] and not p['overflow'] for p in layout['pages']) pages=[];fonts={} normalize=lambda s:''.join(unicodedata.normalize('NFKC',s).split()) for i,page in enumerate(r.pages): text=page.extract_text();assert len(text)>120,(i,len(text)) assert normalize(outline[i]['title']) in normalize(text),(i,outline[i]['title']) assert abs(float(page.mediabox.width)-595.28)<1 and abs(float(page.mediabox.height)-841.89)<1 assert '\ufffd' not in text for entry in page['/Resources'].get('/Font',{}).values(): font=entry.get_object() for item in font.get('/DescendantFonts',[font]): value=item.get_object();desc=value.get('/FontDescriptor') if desc: desc=desc.get_object();name=str(desc.get('/FontName')) embedded=bool(value.get('/Subtype')=='/Type3' and value.get('/CharProcs')) or any(k in desc for k in ['/FontFile','/FontFile2','/FontFile3']) fonts[name]=embedded pages.append({'page':i+1,'characters':len(text),'title':outline[i]['title']}) assert fonts and all(fonts.values()) and any('CJK' in k for k in fonts) def count_bookmarks(items): return sum(count_bookmarks(item) if isinstance(item,list) else 1 for item in items) bookmarks=count_bookmarks(r.outline) assert bookmarks>=72 report={'report_id':'rtcp-beginners-pdf-v1','status':'verified','page_count':len(r.pages),'pdf':pdf.name,'bytes':pdf.stat().st_size,'sha256':hashlib.sha256(pdf.read_bytes()).hexdigest(),'pages':pages,'embedded_fonts':fonts,'bookmarks':bookmarks,'numeric_report':numeric['report_id'],'executed_program_status':numeric['executed_program']['status'],'wasm_sha256':numeric['wasm_sha256'],'evidence':[{'path':str(p.relative_to(OUT)),'sha256':hashlib.sha256(p.read_bytes()).hexdigest()} for p in [OUT/'handbook.html',OUT/'outline.json',OUT/'gcode-evidence.json',OUT/'verification/layout.json',*sorted((OUT/'assets').iterdir()),*sorted((OUT/'verification').glob('pdf-contact-*.png'))]]} (OUT/'verification/acceptance.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n') print(json.dumps({k:report[k] for k in ['report_id','status','page_count','bytes','bookmarks']},ensure_ascii=False))