183 lines
5.1 KiB
Python
183 lines
5.1 KiB
Python
"""
|
||
PDF 生成工具模块
|
||
使用 Pandoc 将 Markdown 文本转换为 PDF 文件
|
||
"""
|
||
import os
|
||
import base64
|
||
import subprocess
|
||
import tempfile
|
||
from datetime import datetime
|
||
|
||
|
||
class PDFGenerator:
|
||
"""PDF 生成器类(Pandoc 版)"""
|
||
|
||
def __init__(self, main_font: str = "Noto Serif CJK SC"):
|
||
"""
|
||
初始化 PDF 生成器
|
||
|
||
Args:
|
||
main_font: Pandoc / XeLaTeX 使用的中文字体名
|
||
"""
|
||
self.main_font = main_font
|
||
|
||
def generate_pdf_from_text(
|
||
self,
|
||
text_content: str,
|
||
output_path: str = None,
|
||
title: str = "维修报告",
|
||
return_base64: bool = True
|
||
) -> dict:
|
||
"""
|
||
将 Markdown 文本内容生成 PDF 文件(方法名不变)
|
||
|
||
Args:
|
||
text_content: Markdown 文本内容
|
||
output_path: PDF 输出路径(可选)
|
||
title: PDF 标题
|
||
return_base64: 是否返回 base64 编码内容
|
||
"""
|
||
try:
|
||
# 输出路径
|
||
if output_path is None:
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
output_path = f"temp_report_{timestamp}.pdf"
|
||
|
||
# 生成临时 Markdown 文件
|
||
with tempfile.NamedTemporaryFile(
|
||
mode="w",
|
||
suffix=".md",
|
||
delete=False,
|
||
encoding="utf-8"
|
||
) as md_file:
|
||
md_path = md_file.name
|
||
|
||
# 写入标题 + 内容(Pandoc 标准)
|
||
if title:
|
||
md_file.write(f"# {title}\n\n")
|
||
md_file.write(text_content.strip())
|
||
|
||
# Pandoc 命令
|
||
cmd = [
|
||
"pandoc",
|
||
md_path,
|
||
"--pdf-engine=xelatex",
|
||
"-V", f"mainfont={self.main_font}",
|
||
"-o", output_path
|
||
]
|
||
|
||
# 执行转换
|
||
subprocess.run(
|
||
cmd,
|
||
check=True,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE
|
||
)
|
||
|
||
result = {
|
||
"success": True,
|
||
"file_path": output_path
|
||
}
|
||
|
||
# 返回 base64
|
||
if return_base64:
|
||
with open(output_path, "rb") as f:
|
||
result["base64_content"] = base64.b64encode(
|
||
f.read()
|
||
).decode("utf-8")
|
||
|
||
return result
|
||
|
||
except subprocess.CalledProcessError as e:
|
||
return {
|
||
"success": False,
|
||
"error": e.stderr.decode("utf-8")
|
||
}
|
||
except Exception as e:
|
||
return {
|
||
"success": False,
|
||
"error": str(e)
|
||
}
|
||
finally:
|
||
# 清理临时 md 文件
|
||
if "md_path" in locals() and os.path.exists(md_path):
|
||
os.remove(md_path)
|
||
|
||
|
||
def generate_report_pdf(
|
||
report_content: str,
|
||
device_name: str = "未知设备",
|
||
output_dir: str = None
|
||
) -> dict:
|
||
"""
|
||
生成维修报告 PDF(方法名不变)
|
||
"""
|
||
generator = PDFGenerator()
|
||
|
||
# 文件名规则不变
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
safe_device_name = device_name.replace(" ", "_").replace("/", "_")
|
||
file_name = f"{safe_device_name}_维修报告_{timestamp}.pdf"
|
||
|
||
if output_dir:
|
||
os.makedirs(output_dir, exist_ok=True)
|
||
output_path = os.path.join(output_dir, file_name)
|
||
else:
|
||
output_path = file_name
|
||
|
||
result = generator.generate_pdf_from_text(
|
||
text_content=report_content,
|
||
output_path=output_path,
|
||
title="设备维修报告",
|
||
return_base64=True
|
||
)
|
||
|
||
if result.get("success"):
|
||
result["file_name"] = file_name
|
||
|
||
return result
|
||
|
||
|
||
# 测试代码
|
||
if __name__ == "__main__":
|
||
test_md = """
|
||
|
||
爱因斯坦质能方程:
|
||
|
||
$$E = mc^2$$
|
||
|
||
该公式揭示了质量与能量的等价性。在核反应中,微小的质量亏损 $\Delta m$ 会释放巨大能量:
|
||
|
||
$$\Delta E = \Delta m \cdot c^2$$
|
||
|
||
其中 $c = 3 \times 10^8 \, \text{m/s}$ 为真空光速。
|
||
|
||
## 实验数据对比
|
||
|
||
下表展示了不同材料在相同条件下的能量释放效率:
|
||
|
||
| 材料类型 | 质量亏损 (kg) | 释放能量 (J) | 能量密度 (J/kg) |
|
||
|------------|----------------|---------------------|----------------------|
|
||
| 铀-235 | $1.0 \times 10^{-3}$ | $9.0 \times 10^{13}$ | $9.0 \times 10^{16}$ |
|
||
| 氘氚聚变 | $3.1 \times 10^{-3}$ | $2.8 \times 10^{14}$ | $9.0 \times 10^{16}$ |
|
||
| 化学燃烧 | $1.0 \times 10^{-3}$ | $3.0 \times 10^{4}$ | $3.0 \times 10^{7}$ |
|
||
|
||
|
||
## 结论
|
||
|
||
- 核反应的能量密度远高于化学反应(约 $10^9$ 倍)。
|
||
- 公式 $a^2 + b^2 = c^2$ 虽为几何关系,但在相对论四维时空中有深刻类比。
|
||
"""
|
||
|
||
result = generate_report_pdf(
|
||
test_md,
|
||
device_name="测试设备"
|
||
)
|
||
|
||
if result["success"]:
|
||
print(f"✓ PDF 生成成功:{result['file_name']}")
|
||
print(f"✓ 文件路径:{result['file_path']}")
|
||
print(f"✓ Base64 长度:{len(result['base64_content'])}")
|
||
else:
|
||
print(f"✗ PDF 生成失败:{result['error']}")
|