"""generator HTML emitter. Per BR5.1/BR5.3 (US5/US6): writes a self-contained zero-network newspaper.html, file:// printable to A4 with margins, clean print. BR5.2: friendly guided empty-state when no content. """ from __future__ import annotations import html from pathlib import Path from .model import Article, Issue, MastheadConfig, Page def _render_masthead(m: MastheadConfig) -> str: return ( "
" f"

{html.escape(m.title)}

" f"

{html.escape(m.date_line)} · Issue {html.escape(m.issue_number)} · Volume {html.escape(m.volume)}

" "
" ) def _render_article(a: Article) -> str: by = f"{html.escape(a.byline)}" if a.byline else "" return ( f"

{html.escape(a.headline)}

{by}" f"

{html.escape(a.body)}

" ) def _render_page(page: Page, articles_by_id: dict[str, Article]) -> str: cols = [] for col in page.columns: cells = [] for item in col.items: art = articles_by_id.get(item.source_ref) if art: cells.append(_render_article(art)) else: cells.append(f"

({html.escape(item.kind)})

") cols.append(f"
{''.join(cells)}
") return f"
{html.escape(page.running_header)}
{''.join(cols)}
" def emit_newspaper(issue: Issue, out_path: Path) -> None: """Write the self-contained, zero-network newspaper.html (BR5.1).""" articles_by_id = {a.article_id: a for a in issue.articles} pages = "".join(_render_page(p, articles_by_id) for p in issue.pages) css = ( "@page { size: A4; margin: 15mm; }" "body { font-family: serif; color: #111827; background: #fbf8f1; margin:0; }" ".masthead { border-bottom: 2px solid #000; margin-bottom: 12px; }" ".masthead h1 { margin: 0; font-size: 2.2em; }" ".meta { margin: 4px 0 8px; }" ".page { page-break-after: always; }" ".col { width: 32%; display: inline-block; vertical-align: top; padding-right: 8px; }" ".running { font-size: .8em; border-bottom: 1px solid #999; margin-bottom: 8px; }" "@media print { .page { break-after: page; } }" ) doc = ( "" "Wedding Newspaper" + _render_masthead(issue.masthead) + pages + "" ) out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(doc, encoding="utf-8") def emit_empty_state(out_path: Path, masthead: MastheadConfig | None = None) -> None: """BR5.2/US10: friendly guided empty-state page with a sample-issue pointer.""" title = masthead.title if masthead else "The Wedding Times" doc = ( "" "Welcome — Wedding Newspaper" "

Welcome to " + html.escape(title) + "

" "

Add a content/ folder of markdown or txt files and a " "config.json, then run newspaper generate to " "produce your paper. Try the sample issue first to see the layout.

" "" ) out_path.parent.mkdir(parents=True, exist_ok=True) out_path.write_text(doc, encoding="utf-8")