From dffe1618d5fc5975e5befaf01ef1a346a03927ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=A9=AC=E4=B8=80=E4=B8=81?= <1769123563@qq.com>
Date: Tue, 18 Nov 2025 01:13:25 +0800
Subject: [PATCH] Add an "Export to PDF" Button and Define the Font for
Exporting to PDF
---
ReportEngine/renderers/html_renderer.py | 40 ++++++++++++++++++++++++-
1 file changed, 39 insertions(+), 1 deletion(-)
diff --git a/ReportEngine/renderers/html_renderer.py b/ReportEngine/renderers/html_renderer.py
index 97ca48d..72198b1 100644
--- a/ReportEngine/renderers/html_renderer.py
+++ b/ReportEngine/renderers/html_renderer.py
@@ -10,6 +10,7 @@ import html
import json
import os
import re
+import base64
from pathlib import Path
from typing import Any, Dict, List
from loguru import logger
@@ -74,6 +75,7 @@ class HTMLRenderer:
self.toc_rendered = False
self.hero_kpi_signature: tuple | None = None
self._lib_cache: Dict[str, str] = {}
+ self._pdf_font_base64: str | None = None
# 初始化图表验证和修复器
self.chart_validator = create_chart_validator()
@@ -97,6 +99,11 @@ class HTMLRenderer:
"""获取第三方库文件的目录路径"""
return Path(__file__).parent / "libs"
+ @staticmethod
+ def _get_font_path() -> Path:
+ """返回PDF导出所需字体的路径"""
+ return Path(__file__).parent / "assets" / "fonts" / "SourceHanSerifSC-Medium.otf"
+
def _load_lib(self, filename: str) -> str:
"""
加载指定的第三方库文件内容
@@ -123,6 +130,22 @@ class HTMLRenderer:
print(f"警告: 读取库文件 {filename} 时出错: {e}")
return ""
+ def _load_pdf_font_data(self) -> str:
+ """加载PDF字体的Base64数据,避免重复读取大型文件"""
+ if self._pdf_font_base64 is not None:
+ return self._pdf_font_base64
+ font_path = self._get_font_path()
+ try:
+ data = font_path.read_bytes()
+ self._pdf_font_base64 = base64.b64encode(data).decode("ascii")
+ return self._pdf_font_base64
+ except FileNotFoundError:
+ logger.warning("PDF字体文件缺失:%s", font_path)
+ except Exception as exc:
+ logger.warning("读取PDF字体文件失败:%s (%s)", font_path, exc)
+ self._pdf_font_base64 = ""
+ return self._pdf_font_base64
+
# ====== 公共入口 ======
def render(self, document_ir: Dict[str, Any]) -> str:
@@ -221,6 +244,8 @@ class HTMLRenderer:
str: head片段HTML。
"""
css = self._build_css(theme_tokens)
+ pdf_font_b64 = self._load_pdf_font_data()
+ pdf_font_literal = json.dumps(pdf_font_b64)
# 加载第三方库
chartjs = self._load_lib("chart.js")
@@ -262,6 +287,10 @@ class HTMLRenderer:
+