Add AC/BC RTCP programs and 3D machine manager

This commit is contained in:
2026-09-20 12:31:12 +08:00
parent 1c1e5f9672
commit 8aab531b02
363 changed files with 53213 additions and 58 deletions

View File

@@ -0,0 +1,54 @@
"""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))