"""generator layout + pagination. Per BR2.1/BR2.3, US1/NFR2: flows articles into a classic column-based multi-page A4 layout with page breaks at boundaries, page numbers and running headers. Deterministic, stdlib-only. """ from __future__ import annotations from .model import Article, Column, ColumnItem, Page class ColumnFlow: """Flows placed items into columns/pages with running headers.""" def __init__(self, columns_per_page: int = 3, items_per_column: int = 4) -> None: self.columns_per_page = columns_per_page self.items_per_column = items_per_column def flow(self, article_items: list[ColumnItem]) -> list[Page]: pages: list[Page] = [] current: Column | None = None page: Page | None = None idx = 0 for item in article_items: if page is None or current is None or len(current.items) >= self.items_per_column: current = Column(column_index=0 if current is None else ((current.column_index + 1) % self.columns_per_page)) if page is None or (current.column_index == 0 and len(page.columns) >= self.columns_per_page): page_number = len(pages) + 1 page = Page(page_number=page_number, columns=[], running_header=f"Page {page_number}") pages.append(page) page.columns.append(current) idx += 1 current.items.append(item) return pages or [Page(page_number=1, columns=[Column(column_index=0)], running_header="Page 1")] def build_layout(articles: list[Article]) -> list[Page]: """Map articles into column items and flow them (BR2.1).""" items: list[ColumnItem] = [] for a in articles: items.append(ColumnItem(item_id=a.article_id, kind="article", source_ref=a.article_id)) flow = ColumnFlow() return flow.flow(items)