80 lines
4.3 KiB
Python
80 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate finite, original high-resolution raster and transparent-vector pages."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import zlib
|
|
|
|
|
|
def generate(path):
|
|
path = Path(path)
|
|
if path.exists():
|
|
raise FileExistsError(path)
|
|
width, height, curves = 4960, 7016, 60000
|
|
compressor = zlib.compressobj(6)
|
|
chunks = []
|
|
raw_hash = hashlib.sha256()
|
|
# Bounded row buffer; 104,398,080 decoded RGB bytes, about A4 at 600 dpi.
|
|
paper = bytes((248, 246, 240)) * width
|
|
text_row = b''.join(bytes((35, 40, 48)) if 260 < x < width - 260 and x % 83 < 61
|
|
else bytes((248, 246, 240)) for x in range(width))
|
|
for y in range(height):
|
|
row = text_row if 500 < y < height - 500 and y % 123 < 35 else paper
|
|
raw_hash.update(row)
|
|
part = compressor.compress(row)
|
|
if part:
|
|
chunks.append(part)
|
|
chunks.append(compressor.flush())
|
|
image = b''.join(chunks)
|
|
vector = [b'q 20 20 555 802 re W n /GS gs\n']
|
|
for index in range(curves):
|
|
x, y = 25 + (index * 37) % 490, 30 + (index * 53) % 730
|
|
r, g, b = (index % 7) / 7, (index % 11) / 11, (index % 13) / 13
|
|
vector.append((f'{r:.3f} {g:.3f} {b:.3f} rg {x} {y} m '
|
|
f'{x+38} {y+67} {x+61} {y-19} {x+69} {y+22} c '
|
|
f'{x+25} {y+48} l h f\n').encode())
|
|
vector.append(b'Q\n0.85 0.1 0.65 rg 30 780 32 32 re f\n'
|
|
b'BT /F1 12 Tf 80 790 Td (DOCVIEW COMPLEX 60000 TRANSPARENT PATHS) Tj ET\n')
|
|
content = [b'q 555 0 0 782 20 20 cm /Scan Do Q\n'
|
|
b'0.1 0.3 0.6 rg 30 780 32 32 re f\n'
|
|
b'BT /F1 12 Tf 80 790 Td (DOCVIEW RASTER 4960 x 7016 RGB) Tj ET\n',
|
|
b''.join(vector)]
|
|
def stream(data, dictionary=b''):
|
|
return dictionary + b' /Length ' + str(len(data)).encode() + b' >>\nstream\n' + data + b'\nendstream'
|
|
objects = [b'<< /Type /Catalog /Pages 2 0 R >>',
|
|
b'<< /Type /Pages /Count 2 /Kids [7 0 R 9 0 R] >>',
|
|
b'<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>',
|
|
stream(image, f'<< /Type /XObject /Subtype /Image /Width {width} /Height {height} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode'.encode()),
|
|
b'<< /Type /ExtGState /ca 0.16 /CA 0.16 /BM /Multiply >>',
|
|
stream(zlib.compress(content[0]), b'<< /Filter /FlateDecode'),
|
|
b'<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 3 0 R >> /XObject << /Scan 4 0 R >> >> /Contents 6 0 R >>',
|
|
stream(zlib.compress(content[1]), b'<< /Filter /FlateDecode'),
|
|
b'<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 3 0 R >> /ExtGState << /GS 5 0 R >> >> /Contents 8 0 R >>']
|
|
offsets = [0]
|
|
with path.open('xb') as out:
|
|
out.write(b'%PDF-1.7\n%\xe2\xe3\xcf\xd3\n')
|
|
for index, data in enumerate(objects, 1):
|
|
offsets.append(out.tell())
|
|
out.write(f'{index} 0 obj\n'.encode() + data + b'\nendobj\n')
|
|
xref = out.tell()
|
|
out.write(f'xref\n0 {len(offsets)}\n0000000000 65535 f \n'.encode())
|
|
for offset in offsets[1:]:
|
|
out.write(f'{offset:010d} 00000 n \n'.encode())
|
|
out.write(f'trailer\n<< /Root 1 0 R /Size {len(offsets)} >>\nstartxref\n{xref}\n%%EOF\n'.encode())
|
|
return {'sha256': hashlib.sha256(path.read_bytes()).hexdigest(), 'sizeBytes': path.stat().st_size,
|
|
'pages': 2, 'rasterWidth': width, 'rasterHeight': height, 'decodedRasterBytes': width * height * 3,
|
|
'rasterSha256': raw_hash.hexdigest(), 'transparentPathCount': curves,
|
|
'rasterFilter': 'FlateDecode', 'vectorAlpha': 0.16, 'vectorBlendMode': 'Multiply',
|
|
'source': 'Original deterministic synthetic fixture; standard Helvetica is not embedded',
|
|
'scope': 'One 600-dpi-equivalent image and one dense transparent-vector page, not a full book or real scan corpus'}
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('output', type=Path)
|
|
args = parser.parse_args()
|
|
manifest = generate(args.output)
|
|
args.output.with_suffix('.json').write_text(json.dumps(manifest, indent=2) + '\n')
|
|
print(json.dumps(manifest))
|