Add WeasyPrint PDF export for markdown files
This commit is contained in:
@@ -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>"""
|
||||
Reference in New Issue
Block a user