55 lines
3.1 KiB
Python
55 lines
3.1 KiB
Python
"""Verify the standalone PDF, embedded fonts, text, page dimensions and evidence."""
|
|
import hashlib
|
|
import json
|
|
import unicodedata
|
|
from pathlib import Path
|
|
from pypdf import PdfReader
|
|
|
|
root = Path(__file__).resolve().parents[1]
|
|
output = root / 'docs/teaching/rtcp-five-axis'
|
|
pdf = output / '五轴机床与RTCP-教学课件.pdf'
|
|
reader = PdfReader(pdf)
|
|
outline = json.loads((output / 'outline.json').read_text())
|
|
layout = json.loads((output / 'verification/layout.json').read_text())
|
|
numerics = json.loads((output / 'numeric-evidence.json').read_text())
|
|
assert len(reader.pages) == len(outline) == 31
|
|
assert numerics['status'] == 'verified' and not layout['errors']
|
|
assert all(not p['clipped'] and not p['content_overflow'] for p in layout['pages'])
|
|
pages = []
|
|
fonts = {}
|
|
for index, page in enumerate(reader.pages):
|
|
text = unicodedata.normalize('NFKC', page.extract_text())
|
|
assert len(text) > 100, (index, len(text))
|
|
assert abs(float(page.mediabox.width) / float(page.mediabox.height) - 16/9) < .001
|
|
assert str(index+1).zfill(2) in text
|
|
assert '\ufffd' not in text
|
|
for font in page['/Resources'].get('/Font', {}).values():
|
|
value = font.get_object()
|
|
descendants = value.get('/DescendantFonts', [value])
|
|
for item in descendants:
|
|
descriptor = item.get_object().get('/FontDescriptor')
|
|
if descriptor:
|
|
descriptor = descriptor.get_object()
|
|
# Chrome embeds CFF CJK glyphs as Type 3 CharProcs instead of
|
|
# a FontFile stream; both forms are self-contained PDF fonts.
|
|
type3 = item.get_object().get('/Subtype') == '/Type3' and bool(item.get_object().get('/CharProcs'))
|
|
embedded = type3 or any(key in descriptor for key in ['/FontFile', '/FontFile2', '/FontFile3'])
|
|
name = str(descriptor.get('/FontName'))
|
|
fonts[name] = embedded
|
|
pages.append({'page': index+1, 'characters': len(text), 'width_pt': float(page.mediabox.width), 'height_pt': float(page.mediabox.height)})
|
|
assert fonts and all(fonts.values()), fonts
|
|
assert any('CJK' in font for font in fonts), fonts
|
|
assert len(reader.outline) >= 31
|
|
report = {
|
|
'report_id': 'rtcp-teaching-pdf-v1', 'status': 'verified', 'pages': pages,
|
|
'pdf': pdf.name, 'bytes': pdf.stat().st_size,
|
|
'sha256': hashlib.sha256(pdf.read_bytes()).hexdigest(),
|
|
'bookmarks': len(reader.outline), 'embedded_fonts': fonts,
|
|
'numeric_report_id': numerics['report_id'], 'wasm_sha256': numerics['wasm_sha256'],
|
|
'layout_report': 'verification/layout.json',
|
|
'evidence': [{'path': str(p.relative_to(output)), 'sha256': hashlib.sha256(p.read_bytes()).hexdigest()}
|
|
for p in [output/'course.html', output/'numeric-evidence.json', output/'outline.json', output/'verification/layout.json', *sorted((output/'assets').glob('*.png'))]],
|
|
}
|
|
(output / 'verification/acceptance.json').write_text(json.dumps(report, ensure_ascii=False, indent=2)+'\n')
|
|
print(json.dumps({'report_id':report['report_id'], 'status':report['status'], 'pages':len(pages), 'bytes':report['bytes'], 'fonts':len(fonts), 'bookmarks':report['bookmarks']},ensure_ascii=False))
|