Add WeasyPrint PDF export for markdown files

This commit is contained in:
2026-05-26 21:22:02 -04:00
parent 9776311c20
commit c79202716c
5 changed files with 172 additions and 6 deletions
+92
View File
@@ -0,0 +1,92 @@
"""
PDF export utility using WeasyPrint.
Generates PDF documents from rendered markdown HTML.
Used by both the authenticated API and public share views.
"""
import logging
from pathlib import Path
from typing import Optional
from weasyprint import HTML
logger = logging.getLogger("obsigate.pdf")
def generate_pdf(html_content: str, title: str = "document", base_url: Optional[str] = None) -> bytes:
"""Generate a PDF from HTML content.
Args:
html_content: Full HTML document string.
title: Document title (used for metadata).
base_url: Base URL for resolving relative URLs in the HTML.
Returns:
PDF file as bytes.
"""
html = HTML(string=html_content, base_url=base_url or "")
return html.write_pdf()
def build_pdf_html(body_html: str, title: str, theme: str = "light") -> str:
"""Wrap rendered markdown HTML in a complete print-friendly HTML document.
Args:
body_html: Rendered markdown HTML (without <html>/<body> wrappers).
title: Document title.
theme: 'light' or 'dark' — light is recommended for PDF.
Returns:
Complete HTML document string.
"""
return f"""<!DOCTYPE html>
<html lang="fr">
<head><meta charset="utf-8"><title>{title}</title>
<style>
body {{
font-family: Georgia, "Times New Roman", serif;
max-width: 720px;
margin: 40px auto;
padding: 0 20px;
line-height: 1.7;
color: #1a1a2e;
font-size: 13px;
}}
h1 {{ font-size: 22px; border-bottom: 2px solid #333; padding-bottom: 6px; margin-top: 0; }}
h2 {{ font-size: 18px; margin-top: 24px; }}
h3 {{ font-size: 15px; margin-top: 20px; }}
h4, h5, h6 {{ font-size: 14px; margin-top: 16px; }}
p {{ margin: 8px 0; }}
pre {{
background: #f5f5f5;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 10px 14px;
font-size: 11px;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
}}
code {{ font-size: 12px; background: #f5f5f5; padding: 1px 4px; border-radius: 2px; }}
pre code {{ background: none; padding: 0; }}
a {{ color: #4f46e5; text-decoration: none; }}
img {{ max-width: 100%; border-radius: 4px; }}
blockquote {{
border-left: 3px solid #ccc;
padding-left: 14px;
color: #555;
margin: 12px 0;
}}
table {{ border-collapse: collapse; width: 100%; margin: 12px 0; page-break-inside: avoid; }}
th, td {{ border: 1px solid #ddd; padding: 6px 10px; text-align: left; font-size: 12px; }}
th {{ background: #f0f0f0; font-weight: 600; }}
ul, ol {{ margin: 8px 0; padding-left: 24px; }}
li {{ margin: 2px 0; }}
hr {{ border: none; border-top: 1px solid #ddd; margin: 20px 0; }}
@page {{ margin: 2cm; size: A4; }}
@media print {{ body {{ margin: 0; }} }}
</style></head>
<body><h1>{title}</h1>
{body_html}
</body></html>"""