nex_docus/backend/app/services/pdf_service.py

114 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
PDF 生成服务 - 基于 WeasyPrint + 系统字体
"""
import markdown
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration
import io
class PDFService:
def __init__(self):
self.font_config = FontConfiguration()
def get_css(self):
return """
@page {
margin: 2cm;
@bottom-right {
content: counter(page);
font-size: 9pt;
}
}
/* 全局macOS 用 Hiragino Sans GB / Heiti SCDocker 用 WenQuanYi Micro Hei */
* {
font-family: "Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei",
"Arial Unicode MS", sans-serif !important;
}
body {
font-size: 11pt;
line-height: 1.6;
color: #333;
background-color: #fff;
}
h1, h2, h3, h4, h5, h6 {
border-bottom: 1px solid #eee;
padding-bottom: 0.3em;
margin-top: 1.5em;
margin-bottom: 1em;
font-weight: bold;
}
/* 代码块:等宽字体优先,中文回退到对应平台的 CJK 字体 */
pre, code, pre *, code * {
font-family: "Menlo", "DejaVu Sans Mono", "Courier New",
"Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei", monospace !important;
background-color: transparent;
}
pre {
background-color: #f6f8fa !important;
padding: 16px;
border-radius: 6px;
white-space: pre-wrap;
word-break: break-all;
display: block;
}
code {
background-color: rgba(175, 184, 193, 0.2);
padding: 0.2em 0.4em;
border-radius: 6px;
}
table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1em;
}
th, td {
border: 1px solid #dfe2e5;
padding: 6px 13px;
}
tr:nth-child(2n) {
background-color: #f6f8fa;
}
img {
max-width: 100%;
}
blockquote {
border-left: 0.25em solid #dfe2e5;
color: #6a737d;
padding: 0 1em;
margin-left: 0;
}
"""
async def md_to_pdf(self, md_content: str, title: str = "Document", base_url: str = None) -> io.BytesIO:
"""将 Markdown 转换为 PDF 字节流"""
html_content = markdown.markdown(
md_content,
extensions=['extra', 'codehilite', 'toc', 'tables']
)
full_html = f"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{title}</title>
</head>
<body class="markdown-body">
{html_content}
</body>
</html>
"""
pdf_buffer = io.BytesIO()
css = CSS(string=self.get_css(), font_config=self.font_config)
HTML(string=full_html, base_url=base_url).write_pdf(
pdf_buffer,
stylesheets=[css],
font_config=self.font_config
)
pdf_buffer.seek(0)
return pdf_buffer
pdf_service = PDFService()