#!/usr/bin/env python3 """Generate synthetic read-only comment keyboard fixtures, without dependencies.""" from pathlib import Path objects = [ b'<< /Type /Catalog /Pages 2 0 R >>', b'<< /Type /Pages /Kids [3 0 R] /Count 1 >>', b'<< /Type /Page /Parent 2 0 R /MediaBox [0 0 240 260] /CropBox [20 30 220 230] /Annots [4 0 R 5 0 R] /Resources << >> >>', b'<< /Type /Annot /Subtype /Text /Rect [100 100 120 120] /Contents (Literal comment) /F 4 >>', b'<< /Type /Annot /Subtype /Text /Rect [1000 1000 1020 1020] /Contents (Outside CropBox comment) /F 4 >>', ] data = bytearray(b'%PDF-1.7\n%\xe2\xe3\xcf\xd3\n') offsets = [0] for index, obj in enumerate(objects, 1): offsets.append(len(data)) data += f'{index} 0 obj\n'.encode() + obj + b'\nendobj\n' xref = len(data) data += f'xref\n0 {len(offsets)}\n0000000000 65535 f \n'.encode() for offset in offsets[1:]: data += f'{offset:010d} 00000 n \n'.encode() data += f'trailer\n<< /Size {len(offsets)} /Root 1 0 R >>\nstartxref\n{xref}\n%%EOF\n'.encode() Path(__file__).with_name('comments.pdf').write_bytes(data) # Sub-pixel page at 25% zoom: keyboard focus must not invert scroll bounds. tiny = bytes(data) for original, replacement in [(b'[0 0 240 260]', b'[0 0 1 1]'), (b'[20 30 220 230]', b'[0 0 1 1]'), (b'[100 100 120 120]', b'[0 0 0.5 0.5]'), (b'[1000 1000 1020 1020]', b'[0 0 0.5 0.5]')]: tiny = tiny.replace(original, replacement.ljust(len(original), b' ')) # Preserve fixed byte widths so the original xref remains exact. assert len(tiny) == len(data) Path(__file__).with_name('tiny-comments.pdf').write_bytes(tiny)