math_to_svg.py
5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
"""
LaTeX 数学公式转 SVG 渲染器
使用 matplotlib 将 LaTeX 公式渲染为 SVG 格式,用于 PDF 导出
"""
import io
from typing import Optional
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import mathtext
from loguru import logger
# 使用非交互式后端
matplotlib.use('Agg')
class MathToSVG:
"""将 LaTeX 数学公式转换为 SVG 的转换器"""
def __init__(self, font_size: int = 14, color: str = 'black'):
"""
初始化公式转换器
Args:
font_size: 字体大小(点)
color: 文字颜色
"""
self.font_size = font_size
self.color = color
def convert_to_svg(self, latex: str, display_mode: bool = True) -> Optional[str]:
"""
将 LaTeX 公式转换为 SVG 字符串
Args:
latex: LaTeX 公式字符串(不包含 $$ 或 $ 符号)
display_mode: True 为显示模式(块级公式),False 为行内模式
Returns:
SVG 字符串,如果转换失败则返回 None
"""
try:
# 清理 LaTeX 字符串
latex = latex.strip()
if not latex:
logger.warning("空的 LaTeX 公式")
return None
# 创建图形
fig = plt.figure(figsize=(10, 2) if display_mode else (6, 1))
fig.patch.set_alpha(0) # 透明背景
# 渲染 LaTeX
# 使用 mathtext 进行渲染
if display_mode:
# 显示模式:居中,较大字体
text = fig.text(
0.5, 0.5,
f'${latex}$',
fontsize=self.font_size * 1.2,
color=self.color,
ha='center',
va='center',
usetex=False # 使用 matplotlib 内置的 mathtext 而非完整 LaTeX
)
else:
# 行内模式:左对齐,正常字体
text = fig.text(
0.1, 0.5,
f'${latex}$',
fontsize=self.font_size,
color=self.color,
ha='left',
va='center',
usetex=False
)
# 获取文本边界框
fig.canvas.draw()
bbox = text.get_window_extent(renderer=fig.canvas.get_renderer())
# 转换为英寸(matplotlib 使用的单位)
bbox_inches = bbox.transformed(fig.dpi_scale_trans.inverted())
# 调整图形大小以适应文本,添加边距
margin = 0.1 # 英寸
fig.set_size_inches(
bbox_inches.width + 2 * margin,
bbox_inches.height + 2 * margin
)
# 重新定位文本到中心
text.set_position((0.5, 0.5))
# 保存为 SVG
svg_buffer = io.StringIO()
plt.savefig(
svg_buffer,
format='svg',
bbox_inches='tight',
pad_inches=0.1,
transparent=True,
dpi=300
)
plt.close(fig)
# 获取 SVG 内容
svg_content = svg_buffer.getvalue()
svg_buffer.close()
return svg_content
except Exception as e:
logger.error(f"LaTeX 公式转换失败: {latex[:100]}... 错误: {str(e)}")
return None
def convert_inline_to_svg(self, latex: str) -> Optional[str]:
"""
将行内 LaTeX 公式转换为 SVG
Args:
latex: LaTeX 公式字符串
Returns:
SVG 字符串,如果转换失败则返回 None
"""
return self.convert_to_svg(latex, display_mode=False)
def convert_display_to_svg(self, latex: str) -> Optional[str]:
"""
将显示模式 LaTeX 公式转换为 SVG
Args:
latex: LaTeX 公式字符串
Returns:
SVG 字符串,如果转换失败则返回 None
"""
return self.convert_to_svg(latex, display_mode=True)
def convert_math_block_to_svg(
latex: str,
font_size: int = 16,
color: str = 'black'
) -> Optional[str]:
"""
便捷函数:将数学公式块转换为 SVG
Args:
latex: LaTeX 公式字符串
font_size: 字体大小
color: 文字颜色
Returns:
SVG 字符串,如果转换失败则返回 None
"""
converter = MathToSVG(font_size=font_size, color=color)
return converter.convert_display_to_svg(latex)
def convert_math_inline_to_svg(
latex: str,
font_size: int = 14,
color: str = 'black'
) -> Optional[str]:
"""
便捷函数:将行内数学公式转换为 SVG
Args:
latex: LaTeX 公式字符串
font_size: 字体大小
color: 文字颜色
Returns:
SVG 字符串,如果转换失败则返回 None
"""
converter = MathToSVG(font_size=font_size, color=color)
return converter.convert_inline_to_svg(latex)
if __name__ == "__main__":
# 测试代码
import sys
# 测试公式
test_formulas = [
r"E = mc^2",
r"\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}",
r"\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}",
r"\sum_{i=1}^{n} i = \frac{n(n+1)}{2}",
]
converter = MathToSVG(font_size=16)
for i, formula in enumerate(test_formulas):
logger.info(f"测试公式 {i+1}: {formula}")
svg = converter.convert_display_to_svg(formula)
if svg:
# 保存到文件
filename = f"test_math_{i+1}.svg"
with open(filename, 'w', encoding='utf-8') as f:
f.write(svg)
logger.info(f"成功保存到 {filename}")
else:
logger.error(f"公式 {i+1} 转换失败")
logger.info("测试完成")