blob: e9d566c9883e376fe2cf90ec15dbc10f24eae9a6 [file]
"""Merge print batches, restore cross-batch links and add document bookmarks."""
import json
import sys
from pathlib import Path
from pypdf import PdfReader, PdfWriter
from pypdf.generic import (
ArrayObject, DecodedStreamObject, DictionaryObject, FloatObject,
NameObject, NumberObject,
)
job = json.loads(Path(sys.argv[1]).read_text())
writer = PdfWriter()
for batch in job['batches']:
writer.append(PdfReader(batch), import_outline=False)
prefix = 'https://archive.invalid/'
targets = {'contents': (1, 40, 800)}
for index, page in enumerate(writer.pages):
for ref in page.get('/Annots', []):
link = ref.get_object()
uri = str(link.get('/A', {}).get('/URI', ''))
if uri.startswith(prefix + 'start/'):
rect = link['/Rect']
targets[uri[len(prefix + 'start/'):]] = (index, float(rect[0]), float(rect[3]))
for alias, canonical in job.get('headingAliases', {}).items():
if canonical in targets:
targets.setdefault(alias, targets[canonical])
for page in writer.pages:
for ref in page.get('/Annots', []):
link = ref.get_object()
uri = str(link.get('/A', {}).get('/URI', ''))
if not uri.startswith(prefix):
continue
key = uri[len(prefix):].removeprefix('start/')
target = targets.get(key) or targets.get(key.split('#')[0])
if target is None:
raise RuntimeError(f'Unresolved internal PDF link: {key}')
destination = ArrayObject([writer.pages[target[0]].indirect_reference,
NameObject('/XYZ'), FloatObject(target[1]),
FloatObject(target[2]), NumberObject(0)])
link[NameObject('/A')] = DictionaryObject({NameObject('/S'): NameObject('/GoTo'),
NameObject('/D'): destination})
missing = [doc['key'] for doc in job['docs'] if doc['key'] not in targets]
if missing:
raise RuntimeError(f'Missing document headings: {missing}')
writer.add_outline_item(job['contentsTitle'], 1)
for doc in job['docs']:
writer.add_outline_item(doc['title'], targets[doc['key']][0])
writer.add_metadata({'/Title': job['title'], '/Author': 'Apache Doris',
'/Subject': f"Archived documentation, source commit {job['commit']}",
'/Keywords': f"Apache Doris,{job['version']},{job['locale']},archive"})
font = writer._add_object(DictionaryObject({NameObject('/Type'): NameObject('/Font'),
NameObject('/Subtype'): NameObject('/Type1'), NameObject('/BaseFont'): NameObject('/Helvetica')}))
for index in range(1, len(writer.pages)):
page = writer.pages[index]
resources = page['/Resources'].get_object()
if '/Font' not in resources:
resources[NameObject('/Font')] = DictionaryObject()
resources['/Font'][NameObject('/ArchiveFooter')] = font
stream = DecodedStreamObject()
stream.set_data(f'\nQ\nq BT /ArchiveFooter 8 Tf 0.3 0.35 0.32 rg 1 0 0 1 495 22 Tm ({index + 1} / {len(writer.pages)}) Tj ET Q\n'.encode('ascii'))
old = page.get('/Contents')
contents = old.get_object() if old is not None else None
values = list(contents) if isinstance(contents, ArrayObject) else ([old] if old is not None else [])
start = DecodedStreamObject()
start.set_data(b'q\n')
page[NameObject('/Contents')] = ArrayObject([writer._add_object(start)] + values + [writer._add_object(stream)])
writer.write(job['output'])
print(json.dumps({'pages': len(writer.pages), 'documents': len(job['docs']), 'bookmarks': len(job['docs']) + 1}))