"""Markdown (.md / .markdown) -> PDF. Rendered with the `markdown` library (tables, fenced code, nl2br) and typeset by WeasyPrint with app.BASE_CSS injected at the top of so the fallback layer sits under any user CSS. The first ATX H1 becomes the export name and PDF /Title (it stays in the rendered document for visual fidelity); without an H1 the file stem is used. Empty input renders 0 pages and fails in finalize with "no renderable content: ..." — unpinned, acceptable. """ from __future__ import annotations import re from pathlib import Path from . import common _FIRST_H1_RE = re.compile(r"^\s*#\s+(.+?)\s*#*\s*$", re.MULTILINE) def _extract_h1(md_text: str) -> str | None: m = _FIRST_H1_RE.search(md_text) if not m: return None title = re.sub(r"[#*`_]", "", m.group(1)) title = " ".join(title.split()) return title or None def convert(path: Path, job_id: str, progress_cb=None, output_dir=None, source_name=None) -> str: """Render a Markdown file to a typeset PDF.""" import app # lazy: BASE_CSS lives with the EPUB pipeline import markdown text = common.decode_text(path.read_bytes()) title = _extract_h1(text) body_html = markdown.markdown( text, extensions=["tables", "fenced_code", "nl2br"] ) html_doc = ( f"" f"{body_html}" ) def _build(): return html_doc, path.name return common.convert_single( path, job_id, build_html=_build, title=title, css="", progress_cb=progress_cb, output_dir=output_dir, name_src=source_name or path.name, )