any-file to PDF (Phase 1): converters/ dispatcher, images/text/md/csv/json/xml/svg/pdf-passthrough, 112 tests green
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""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 <head> 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"<!DOCTYPE html><html><head><style>\n{app.BASE_CSS}\n</style>"
|
||||
f"</head><body>{body_html}</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,
|
||||
)
|
||||
Reference in New Issue
Block a user