"""Page numbering of the merged document. Two ways to number pages: css CSS counters do the work during the render. Correct and cheap, but it only works when the whole document is rendered in one pass, because each chunk restarts the page counter. overlay A transparent numbering layer with the same page size is rendered once and merged onto the finished PDF. This is the only option for a chunked document and for Chromium, which has no usable page counters. """ from __future__ import annotations import logging from pathlib import Path from ..errors import EngineUnavailableError from ..models import PageNumbers, PageSettings from .styles import build_overlay_css logger = logging.getLogger(__name__) def build_overlay( total_pages: int, width_pt: float, height_pt: float, page: PageSettings, page_numbers: PageNumbers, output_path: Path, ) -> Path: """Render the numbering layer, one empty page per page of the document.""" try: from weasyprint import HTML except ImportError as exc: raise EngineUnavailableError( "Cislovani stranek vyzaduje nainstalovany WeasyPrint, ktery kresli cislovaci vrstvu.", ) from exc css = build_overlay_css(width_pt, height_pt, page, page_numbers, total_pages) slots = '
' * total_pages html = ( "" f"{slots}" ) HTML(string=html).write_pdf(target=str(output_path)) logger.info("Numbering overlay rendered", extra={"pages": total_pages}) return output_path def apply_overlay(document_path: Path, overlay_path: Path, output_path: Path) -> None: """Stamp the numbering layer onto every page of the document.""" from pypdf import PdfReader, PdfWriter writer = PdfWriter(clone_from=str(document_path)) try: with overlay_path.open("rb") as handle: overlay = PdfReader(handle) available = len(overlay.pages) if available < len(writer.pages): logger.warning( "Numbering overlay has fewer pages than the document, tail will stay unnumbered", extra={"overlay_pages": available, "document_pages": len(writer.pages)}, ) for index, page in enumerate(writer.pages): if index >= available: break page.merge_page(overlay.pages[index]) with output_path.open("wb") as target: writer.write(target) finally: writer.close()