ObsiGate/backend/pdf_export.py

93 lines
2.7 KiB
Python

"""
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>"""