fix: markdown post handel
This commit is contained in:
314
docs/LATEX_RENDERING_ISSUE.md
Normal file
314
docs/LATEX_RENDERING_ISSUE.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# LaTeX 字符渲染问题诊断与解决方案
|
||||
|
||||
## 问题描述
|
||||
|
||||
识别完成后,某些 LaTeX 字符(如 `\lambda`、`\vdots`)没有被成功渲染。
|
||||
|
||||
## 问题诊断
|
||||
|
||||
### 1. LaTeX 语法检查 ✅
|
||||
|
||||
`\lambda` 和 `\vdots` 都是标准的 LaTeX 命令,语法完全正确:
|
||||
- `\lambda` - 希腊字母 λ (Unicode: U+03BB)
|
||||
- `\vdots` - 垂直省略号 ⋮ (Unicode: U+22EE)
|
||||
|
||||
### 2. 后处理管道分析 ✅
|
||||
|
||||
经过代码审查,OCR 后处理管道(`app/services/ocr_service.py`)**不会**破坏这些字符:
|
||||
|
||||
#### Stage 0: 数字错误修复
|
||||
```python
|
||||
_fix_ocr_number_errors(expr)
|
||||
```
|
||||
- **影响范围**: 仅处理数字和小数点
|
||||
- **对 `\lambda` 和 `\vdots` 的影响**: ✅ 无影响
|
||||
|
||||
#### Stage 1: 粘连命令拆分
|
||||
```python
|
||||
_split_glued_command_token(token)
|
||||
```
|
||||
- **影响范围**: 仅处理 `_COMMANDS_NEED_SPACE` 白名单中的命令
|
||||
- **白名单内容**: `cdot`, `times`, `div`, `pm`, `mp`, `int`, `sum`, `sin`, `cos`, 等
|
||||
- **`\lambda` 和 `\vdots` 是否在白名单中**: ❌ 不在
|
||||
- **对 `\lambda` 和 `\vdots` 的影响**: ✅ 无影响(直接返回原始值)
|
||||
|
||||
#### Stage 2: 微分规范化
|
||||
```python
|
||||
_DIFFERENTIAL_UPPER_PATTERN.sub(r"\\mathrm{d} \1", expr)
|
||||
_DIFFERENTIAL_LOWER_PATTERN.sub(r"d \1", expr)
|
||||
```
|
||||
- **影响范围**: 匹配非转义的 `d` 字符(使用 `(?<!\\)` 负向后查找)
|
||||
- **对 `\lambda` 和 `\vdots` 的影响**: ✅ 无影响(都不包含非转义的 `d`)
|
||||
|
||||
**结论**: 后处理管道不会修改 `\lambda` 和 `\vdots`。
|
||||
|
||||
### 3. 可能的问题来源 ⚠️
|
||||
|
||||
既然后处理没有问题,问题可能出在以下环节:
|
||||
|
||||
#### A. Pandoc 转换问题
|
||||
|
||||
**位置**: `app/services/converter.py` → `_latex_to_mathml_cached()`
|
||||
|
||||
```python
|
||||
mathml_html = pypandoc.convert_text(
|
||||
f"${latex_formula}$",
|
||||
"html",
|
||||
format="markdown+tex_math_dollars",
|
||||
extra_args=["--mathml"],
|
||||
)
|
||||
```
|
||||
|
||||
**可能的问题**:
|
||||
1. Pandoc 版本过低,不支持某些 Unicode 字符
|
||||
2. Pandoc 的 MathML 输出使用实体编码而非 Unicode 字符
|
||||
3. 字体映射表缺失
|
||||
|
||||
#### B. MathML 后处理问题
|
||||
|
||||
**位置**: `app/services/converter.py` → `_postprocess_mathml_for_word()`
|
||||
|
||||
这个函数对 MathML 进行了大量后处理,可能误删了某些内容:
|
||||
|
||||
```python
|
||||
# Step 1: Remove <semantics> and <annotation> wrappers
|
||||
# Step 2: Remove unnecessary attributes
|
||||
# Step 3: Remove redundant single <mrow> wrapper
|
||||
# Step 7: Decode common Unicode entities
|
||||
```
|
||||
|
||||
**问题点**: Step 7 的 Unicode 实体解码可能不完整:
|
||||
|
||||
```python
|
||||
unicode_map = {
|
||||
'+': '+',
|
||||
'-': '-',
|
||||
# ... more mappings
|
||||
'λ': 'λ', # lambda
|
||||
'μ': 'μ',
|
||||
# ...
|
||||
}
|
||||
```
|
||||
|
||||
**发现**: 代码中已经包含了 `λ` (U+03BB) 的映射,但**没有** `⋮` (U+22EE, vdots) 的映射!
|
||||
|
||||
#### C. 前端渲染问题
|
||||
|
||||
如果后端返回的 LaTeX/MathML 是正确的,但前端显示不出来:
|
||||
|
||||
1. **MathJax/KaTeX 配置问题**
|
||||
- 可能使用的是旧版本
|
||||
- 宏定义缺失
|
||||
- 字体加载失败
|
||||
|
||||
2. **字体文件缺失**
|
||||
- 希腊字母需要数学字体支持
|
||||
- 可能缺少 STIX、Latin Modern Math 等字体
|
||||
|
||||
3. **前端二次处理**
|
||||
- 前端可能对特殊字符进行了转义或过滤
|
||||
- 可能使用了不当的正则表达式替换
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 方案 1: 扩展 Unicode 实体映射(后端修复)
|
||||
|
||||
如果问题在于 MathML 后处理阶段,需要扩展 `unicode_map`:
|
||||
|
||||
```python
|
||||
# 在 app/services/converter.py 的 _postprocess_mathml_for_word() 中添加:
|
||||
unicode_map = {
|
||||
# ... 现有映射 ...
|
||||
|
||||
# 希腊字母(小写)
|
||||
'α': 'α', # alpha
|
||||
'β': 'β', # beta
|
||||
'γ': 'γ', # gamma
|
||||
'δ': 'δ', # delta
|
||||
'ε': 'ε', # epsilon
|
||||
'ζ': 'ζ', # zeta
|
||||
'η': 'η', # eta
|
||||
'θ': 'θ', # theta
|
||||
'ι': 'ι', # iota
|
||||
'κ': 'κ', # kappa
|
||||
'λ': 'λ', # lambda
|
||||
'μ': 'μ', # mu
|
||||
'ν': 'ν', # nu
|
||||
'ξ': 'ξ', # xi
|
||||
'ο': 'ο', # omicron
|
||||
'π': 'π', # pi
|
||||
'ρ': 'ρ', # rho
|
||||
'σ': 'σ', # sigma
|
||||
'τ': 'τ', # tau
|
||||
'υ': 'υ', # upsilon
|
||||
'φ': 'φ', # phi
|
||||
'χ': 'χ', # chi
|
||||
'ψ': 'ψ', # psi
|
||||
'ω': 'ω', # omega
|
||||
|
||||
# 希腊字母(大写)
|
||||
'Γ': 'Γ', # Gamma
|
||||
'Δ': 'Δ', # Delta
|
||||
'Θ': 'Θ', # Theta
|
||||
'Λ': 'Λ', # Lambda
|
||||
'Ξ': 'Ξ', # Xi
|
||||
'Π': 'Π', # Pi
|
||||
'Σ': 'Σ', # Sigma
|
||||
'Υ': 'Υ', # Upsilon
|
||||
'Φ': 'Φ', # Phi
|
||||
'Ψ': 'Ψ', # Psi
|
||||
'Ω': 'Ω', # Omega
|
||||
|
||||
# 数学符号
|
||||
'⋮': '⋮', # vdots (垂直省略号)
|
||||
'⋯': '⋯', # cdots (中间省略号)
|
||||
'⋰': '⋰', # addots (对角省略号)
|
||||
'⋱': '⋱', # ddots (对角省略号)
|
||||
'…': '…', # ldots (水平省略号)
|
||||
'∅': '∅', # emptyset
|
||||
'∈': '∈', # in
|
||||
'∉': '∉', # notin
|
||||
'∋': '∋', # ni
|
||||
'∑': '∑', # sum
|
||||
'∏': '∏', # prod
|
||||
'√': '√', # sqrt
|
||||
'∞': '∞', # infty
|
||||
'∩': '∩', # cap
|
||||
'∪': '∪', # cup
|
||||
'⊂': '⊂', # subset
|
||||
'⊃': '⊃', # supset
|
||||
'⊆': '⊆', # subseteq
|
||||
'⊇': '⊇', # supseteq
|
||||
'≤': '≤', # leq
|
||||
'≥': '≥', # geq
|
||||
'≠': '≠', # neq
|
||||
'≈': '≈', # approx
|
||||
'≡': '≡', # equiv
|
||||
'×': '×', # times
|
||||
'÷': '÷', # div
|
||||
'±': '±', # pm
|
||||
}
|
||||
```
|
||||
|
||||
### 方案 2: 检查前端渲染(前端修复)
|
||||
|
||||
如果后端返回正确,需要检查前端:
|
||||
|
||||
#### 步骤 1: 验证后端输出
|
||||
|
||||
使用诊断工具检查后端返回的内容:
|
||||
|
||||
```bash
|
||||
python diagnose_latex_rendering.py "$\lambda + \vdots$"
|
||||
```
|
||||
|
||||
或者直接调用 API 并检查响应:
|
||||
|
||||
```bash
|
||||
curl -X POST "http://localhost:8000/api/v1/image/ocr" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"image_url": "...", "model_name": "paddle"}' | jq
|
||||
```
|
||||
|
||||
检查返回的 `latex`、`mathml`、`mml` 字段是否包含正确的字符。
|
||||
|
||||
#### 步骤 2: 检查前端配置
|
||||
|
||||
如果使用 MathJax:
|
||||
|
||||
```javascript
|
||||
MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['$', '$'], ['\\(', '\\)']],
|
||||
displayMath: [['$$', '$$'], ['\\[', '\\]']],
|
||||
processEscapes: true,
|
||||
processEnvironments: true,
|
||||
},
|
||||
svg: {
|
||||
fontCache: 'global'
|
||||
},
|
||||
options: {
|
||||
enableMenu: false
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
如果使用 KaTeX:
|
||||
|
||||
```javascript
|
||||
renderMathInElement(document.body, {
|
||||
delimiters: [
|
||||
{left: '$$', right: '$$', display: true},
|
||||
{left: '$', right: '$', display: false},
|
||||
{left: '\\[', right: '\\]', display: true},
|
||||
{left: '\\(', right: '\\)', display: false}
|
||||
],
|
||||
throwOnError: false
|
||||
});
|
||||
```
|
||||
|
||||
#### 步骤 3: 检查字体加载
|
||||
|
||||
确保加载了数学字体:
|
||||
|
||||
```html
|
||||
<!-- MathJax -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
|
||||
|
||||
<!-- 或 KaTeX -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
|
||||
```
|
||||
|
||||
### 方案 3: 禁用有问题的后处理(临时解决)
|
||||
|
||||
如果确认是 MathML 后处理导致的问题,可以临时禁用部分后处理:
|
||||
|
||||
```python
|
||||
# 在 app/services/converter.py 中
|
||||
@staticmethod
|
||||
def _postprocess_mathml_for_word(mathml: str) -> str:
|
||||
# 跳过所有后处理,直接返回原始 MathML
|
||||
return mathml
|
||||
```
|
||||
|
||||
## 使用诊断工具
|
||||
|
||||
我已经创建了一个诊断工具 `diagnose_latex_rendering.py`,使用方法:
|
||||
|
||||
```bash
|
||||
# 测试单个字符
|
||||
python diagnose_latex_rendering.py "$\lambda$"
|
||||
python diagnose_latex_rendering.py "$\vdots$"
|
||||
|
||||
# 测试组合
|
||||
python diagnose_latex_rendering.py "$$\lambda_1, \lambda_2, \vdots, \lambda_n$$"
|
||||
|
||||
# 测试矩阵
|
||||
python diagnose_latex_rendering.py "$\begin{pmatrix} a \\ \vdots \\ z \end{pmatrix}$"
|
||||
```
|
||||
|
||||
工具会输出:
|
||||
1. 字符检测结果
|
||||
2. 每个后处理阶段的变化
|
||||
3. 最终输出
|
||||
4. 问题定位建议
|
||||
|
||||
## 推荐的调试流程
|
||||
|
||||
1. **运行诊断工具**,确认后处理阶段是否修改了输入
|
||||
2. **检查 API 响应**,确认后端返回的内容是否正确
|
||||
3. **检查前端渲染**,使用浏览器开发者工具查看实际渲染的内容
|
||||
4. **根据问题位置**,应用相应的解决方案
|
||||
|
||||
## 总结
|
||||
|
||||
根据代码分析:
|
||||
- ✅ LaTeX 语法正确
|
||||
- ✅ OCR 后处理不会破坏这些字符
|
||||
- ⚠️ 可能的问题:
|
||||
- MathML Unicode 实体映射不完整(缺少 `\vdots` 等字符)
|
||||
- Pandoc 转换配置问题
|
||||
- 前端渲染或二次处理问题
|
||||
|
||||
建议先使用诊断工具确定问题位置,然后应用相应的解决方案。
|
||||
Reference in New Issue
Block a user