"""Compare cached source PDF pages and actual EPUB source anchors locally."""
import argparse
import hashlib
import json
import shutil
import sys
from pathlib import Path
from urllib.parse import quote
sys.path.insert(0,str(Path(__file__).resolve().parents[1]))
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('epub',type=Path);parser.add_argument('--work',type=Path,required=True)
parser.add_argument('--pages',default='1,4,16');parser.add_argument('--out',type=Path,required=True)
args=parser.parse_args()
from src.qa.headless import archive_server,book_inventory,LAYOUT_JS
from src.qa.epub_audit import audit_epub
from playwright.sync_api import sync_playwright
data=json.loads((args.work/'book.json').read_text())
pages={p['number']:p for p in data['pages']};selected=[int(p) for p in args.pages.split(',')]
if any(p not in pages for p in selected):parser.error('Requested page not present in the conversion IR')
args.out.mkdir(parents=True,exist_ok=True)
inventory=book_inventory(args.epub);evidence=[];clicks=[];sections=[]
with archive_server({'book':args.epub}) as base,sync_playwright() as pw:
browser=pw.chromium.launch(headless=True)
try:
context=browser.new_context(java_script_enabled=False,service_workers='block')
context.route('**/*',lambda route:route.continue_() if route.request.url.startswith(base+'/') else route.abort())
tab=context.new_page()
for number in selected:
anchor=f'pdf-page-{number}'
document=next((name for name,soup in inventory['documents'].items() if soup.find(id=anchor)),None)
if not document:raise ValueError(f'EPUB lost source anchor {anchor}')
source_name=f'pdf-{number:04d}.jpg';shutil.copy2(pages[number]['render'],args.out/source_name)
rendered=[]
for width in [900,390]:
tab.set_viewport_size({'width':width,'height':1100})
tab.goto(base+'/book/'+quote(document)+'#'+anchor,wait_until='networkidle')
tab.evaluate('() => document.fonts.ready')
tab.evaluate('(id) => {const e=document.getElementById(id); scrollTo(0,Math.max(0,e.getBoundingClientRect().top+scrollY-140));}',anchor)
name=f'epub-{number:04d}-{width}.png';tab.screenshot(path=str(args.out/name));rendered.append(name)
evidence.append({'source_page':number,'document':document,'viewport_width':width,'image':name,'layout_findings':tab.evaluate(LAYOUT_JS)})
sections.append(f'PDF 原页序 {number}
左:原扫描;中:EPUB;右:窄屏。截图生成不等于视觉或OCR验收。
'+''.join(sections)) print(f'PDF source comparisons: {len(selected)}; TOC target navigation: {len(clicks)}') return int(bool(report['epub_audit']['issues']) or any(not c['destination_reached'] for c in clicks) or any(e['layout_findings'] for e in evidence)) if __name__=='__main__':sys.exit(main())