voice-normalization / scripts /md_to_pdf.py
RishiKar210's picture
Initial backend deploy
99df98c
Raw
History Blame Contribute Delete
3.28 kB
"""Convert a markdown file to PDF using Playwright (Chromium headless).
Chromium has full CSS3 support including proper page-break handling,
which xhtml2pdf (the previous converter) did not. The trade-off is a
~150 MB Chromium binary dependency, installed once via:
pip install playwright
playwright install chromium
"""
from __future__ import annotations
import sys
from pathlib import Path
import markdown
from playwright.sync_api import sync_playwright
CSS = """
@page {
size: letter;
margin: 0.5in;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 9pt;
line-height: 1.35;
color: #222;
}
h1 { font-size: 17pt; color: #1a1a1a; margin: 0.3em 0 0.2em;
border-bottom: 2px solid #333; padding-bottom: 3px;
page-break-after: avoid; break-after: avoid; }
h2 { font-size: 13pt; color: #1a1a1a; margin: 0.8em 0 0.2em;
border-bottom: 1px solid #999; padding-bottom: 2px;
page-break-after: avoid; break-after: avoid; }
h3 { font-size: 11pt; color: #1a1a1a; margin: 0.6em 0 0.15em;
page-break-after: avoid; break-after: avoid; }
p, li { font-size: 9pt; margin: 0.25em 0; }
ul, ol { margin: 0.2em 0; padding-left: 1.3em; }
table {
border-collapse: collapse; width: 100%;
margin: 0.4em 0; font-size: 8.5pt;
page-break-inside: avoid;
break-inside: avoid;
}
th, td {
border: 1px solid #aaa; padding: 3px 5px;
text-align: left; vertical-align: top;
}
th { background: #eee; font-weight: bold; }
thead { display: table-header-group; }
tr { page-break-inside: avoid; break-inside: avoid; }
code {
background: #f0f0f0;
padding: 1px 3px;
border-radius: 2px;
font-family: Consolas, "Courier New", monospace;
font-size: 8.5pt;
}
pre {
background: #f5f5f5;
border: 1px solid #ddd;
padding: 6px;
border-radius: 3px;
font-family: Consolas, "Courier New", monospace;
font-size: 8pt;
line-height: 1.25;
white-space: pre-wrap;
page-break-inside: avoid;
break-inside: avoid;
}
blockquote {
border-left: 3px solid #888;
margin-left: 0;
padding-left: 10px;
color: #555;
font-style: italic;
}
hr { border: 0; border-top: 1px solid #ccc; margin: 0.6em 0; }
div { break-inside: avoid; page-break-inside: avoid; }
"""
def convert(md_path: Path, pdf_path: Path) -> None:
md_text = md_path.read_text(encoding="utf-8")
html_body = markdown.markdown(
md_text,
extensions=["tables", "fenced_code", "sane_lists", "md_in_html"],
)
html = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>{CSS}</style>
</head>
<body>
{html_body}
</body>
</html>"""
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.set_content(html, wait_until="networkidle")
page.pdf(
path=str(pdf_path),
format="Letter",
margin={"top": "0.5in", "bottom": "0.5in", "left": "0.5in", "right": "0.5in"},
print_background=True,
)
browser.close()
print(f"Wrote {pdf_path}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: md_to_pdf.py <input.md> <output.pdf>")
sys.exit(1)
convert(Path(sys.argv[1]), Path(sys.argv[2]))