init: WordFileParse docx→HTML 解析服务(含 Word EQ 域公式修复)
服务:Spring Boot 3.2 + docx4j 8.3.9,POST /api/v3/server_office_file/file/parse
接受 .docx,返回 HTML(公式 → LaTeX,图片 → RustFS 外链)
公式支持三类来源:
- OMML(Word 2007+ 公式) → Python omml_mathml2latex
- MathType OLE 对象 → Ruby mathtype2mathml → MathML → LaTeX
- Word EQ 域(老教辅主力) → EqToLatexUtil(本次新增)
本次改动:
1. 新增 java-svc/.../core/EqToLatexUtil.java:
移植自 jby backend-services office-core,EQ 语法 → LaTeX
(\f→\dfrac、\r→\sqrt、\i→\int/\sum/\prod、\s→^{}/_{}、\b→\left\right、
\a→array、\x→\boxed/\overline、\o\al 堆叠、圈码、化学符号、极限、连等式)
品牌标记 JBYU* → WPU*,与采集端一致
2. CtoMathFinder:接上 EQ 分支(原为 not-ported 桩),域代码经
EqToLatexUtil.convert 转 LaTeX 写入 eqLatexMap(WPEQNUM 占位符)
3. WordParseUtil.dealPlaceholder:EQ 的 LaTeX 同样加 \( \) 包裹
(原先因 WPEQNUM 例外不加包裹,前端/MathJax 不渲染)
修前症状:教辅 Word 里分式/方程组/上下标整段丢失(如 {−1/3, 1} 变成 {-,1})
修后实测:46 份教辅 2908 处 EQ 域全部还原,公式总数 2954 处、零未包裹残留
安全:application.yml(含 RustFS 密钥)不入库,提供 application.yml.example;
OssService 的 @Value 默认密钥已清空
This commit is contained in:
12
scripts/omml_mathml2latex/builders/__init__.py
Normal file
12
scripts/omml_mathml2latex/builders/__init__.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
Builder classes for fluent construction of LaTeX and MathML expressions.
|
||||
|
||||
This package contains builder classes that provide fluent interfaces for
|
||||
constructing complex expressions.
|
||||
"""
|
||||
|
||||
from .latex_builder import LaTeXBuilder
|
||||
|
||||
__all__ = [
|
||||
'LaTeXBuilder',
|
||||
]
|
||||
320
scripts/omml_mathml2latex/builders/latex_builder.py
Normal file
320
scripts/omml_mathml2latex/builders/latex_builder.py
Normal file
@@ -0,0 +1,320 @@
|
||||
"""
|
||||
LaTeX Builder for fluent construction of LaTeX expressions.
|
||||
|
||||
This builder provides a fluent interface for constructing complex LaTeX
|
||||
expressions with proper brace balancing and formatting.
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class LaTeXBuilder:
|
||||
"""
|
||||
Fluent builder for constructing LaTeX expressions.
|
||||
|
||||
This builder provides method chaining for building complex LaTeX
|
||||
expressions with automatic brace balancing and spacing optimization.
|
||||
|
||||
Examples:
|
||||
Simple fraction:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> result = builder.add_fraction("a", "b").build()
|
||||
>>> result
|
||||
'\\frac{a}{b}'
|
||||
|
||||
Superscript:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> result = builder.add_superscript("x", "2").build()
|
||||
>>> result
|
||||
'x^{2}'
|
||||
|
||||
Complex expression:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> result = (builder
|
||||
... .add_command("sin", "x")
|
||||
... .add_text(" + ")
|
||||
... .add_fraction("1", "2")
|
||||
... .build())
|
||||
>>> result
|
||||
'\\sin{x} + \\frac{1}{2}'
|
||||
|
||||
Nested fractions:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> inner = LaTeXBuilder().add_fraction("a", "b").build()
|
||||
>>> result = builder.add_fraction(inner, "c").build()
|
||||
>>> result
|
||||
'\\frac{\\frac{a}{b}}{c}'
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the LaTeX builder."""
|
||||
self._parts: List[str] = []
|
||||
|
||||
def add_text(self, text: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add raw text to the LaTeX expression.
|
||||
|
||||
Args:
|
||||
text: The text to add (no processing)
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_text("x + y").build()
|
||||
'x + y'
|
||||
"""
|
||||
if text:
|
||||
self._parts.append(text)
|
||||
return self
|
||||
|
||||
def add_command(self, cmd: str, arg: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add a LaTeX command with one argument: \\cmd{arg}
|
||||
|
||||
Args:
|
||||
cmd: The command name (without backslash)
|
||||
arg: The argument content
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_command("sin", "x").build()
|
||||
'\\sin{x}'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_command("text", "hello").build()
|
||||
'\\text{hello}'
|
||||
"""
|
||||
self._parts.append(f"\\{cmd}{{{arg}}}")
|
||||
return self
|
||||
|
||||
def add_fraction(self, numerator: str, denominator: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add a fraction: \\frac{numerator}{denominator}
|
||||
|
||||
Args:
|
||||
numerator: The numerator expression
|
||||
denominator: The denominator expression
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_fraction("a", "b").build()
|
||||
'\\frac{a}{b}'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_fraction("x + 1", "y - 2").build()
|
||||
'\\frac{x + 1}{y - 2}'
|
||||
"""
|
||||
self._parts.append(f"\\frac{{{numerator}}}{{{denominator}}}")
|
||||
return self
|
||||
|
||||
def add_superscript(self, base: str, superscript: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add a superscript: base^{superscript}
|
||||
|
||||
Args:
|
||||
base: The base expression
|
||||
superscript: The superscript expression
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_superscript("x", "2").build()
|
||||
'x^{2}'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_superscript("e", "i\\pi").build()
|
||||
'e^{i\\pi}'
|
||||
"""
|
||||
self._parts.append(f"{base}^{{{superscript}}}")
|
||||
return self
|
||||
|
||||
def add_subscript(self, base: str, subscript: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add a subscript: base_{subscript}
|
||||
|
||||
Args:
|
||||
base: The base expression
|
||||
subscript: The subscript expression
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_subscript("x", "1").build()
|
||||
'x_{1}'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_subscript("a", "i+j").build()
|
||||
'a_{i+j}'
|
||||
"""
|
||||
self._parts.append(f"{base}_{{{subscript}}}")
|
||||
return self
|
||||
|
||||
def add_subscript_superscript(self, base: str, subscript: str, superscript: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add both subscript and superscript: base_{subscript}^{superscript}
|
||||
|
||||
Args:
|
||||
base: The base expression
|
||||
subscript: The subscript expression
|
||||
superscript: The superscript expression
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_subscript_superscript("x", "1", "2").build()
|
||||
'x_{1}^{2}'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_subscript_superscript("R", "i", "j").build()
|
||||
'R_{i}^{j}'
|
||||
"""
|
||||
self._parts.append(f"{base}_{{{subscript}}}^{{{superscript}}}")
|
||||
return self
|
||||
|
||||
def add_sqrt(self, content: str, root: Optional[str] = None) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add a square root or nth root.
|
||||
|
||||
Args:
|
||||
content: The content under the root
|
||||
root: Optional root index (None for square root)
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_sqrt("x").build()
|
||||
'\\sqrt{x}'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_sqrt("x", "3").build()
|
||||
'\\sqrt[3]{x}'
|
||||
"""
|
||||
if root:
|
||||
self._parts.append(f"\\sqrt[{root}]{{{content}}}")
|
||||
else:
|
||||
self._parts.append(f"\\sqrt{{{content}}}")
|
||||
return self
|
||||
|
||||
def add_environment(self, env: str, content: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add a LaTeX environment: \\begin{env}content\\end{env}
|
||||
|
||||
Args:
|
||||
env: The environment name
|
||||
content: The environment content
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_environment("cases", "x & x > 0 \\\\\\\\ 0 & x \\\\leq 0").build()
|
||||
'\\begin{cases}x & x > 0 \\\\\\\\ 0 & x \\\\leq 0\\end{cases}'
|
||||
"""
|
||||
self._parts.append(f"\\begin{{{env}}}{content}\\end{{{env}}}")
|
||||
return self
|
||||
|
||||
def add_delimiter_pair(self, left: str, content: str, right: str) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add delimiter pair: \\left{left}content\\right{right}
|
||||
|
||||
Args:
|
||||
left: The left delimiter
|
||||
content: The content between delimiters
|
||||
right: The right delimiter
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_delimiter_pair("(", "x + y", ")").build()
|
||||
'\\left(x + y\\right)'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_delimiter_pair("\\\\{", "a, b, c", "\\\\}").build()
|
||||
'\\left\\\\{a, b, c\\right\\\\}'
|
||||
"""
|
||||
self._parts.append(f"\\left{left}{content}\\right{right}")
|
||||
return self
|
||||
|
||||
def add_matrix(self, rows: List[List[str]], environment: str = "matrix") -> 'LaTeXBuilder':
|
||||
"""
|
||||
Add a matrix environment.
|
||||
|
||||
Args:
|
||||
rows: List of rows, each row is a list of cell contents
|
||||
environment: The matrix environment (matrix, pmatrix, bmatrix, etc.)
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> rows = [["a", "b"], ["c", "d"]]
|
||||
>>> builder.add_matrix(rows, "pmatrix").build()
|
||||
'\\begin{pmatrix}a & b \\\\\\\\ c & d\\end{pmatrix}'
|
||||
"""
|
||||
matrix_content = " \\\\ ".join(
|
||||
" & ".join(cell for cell in row)
|
||||
for row in rows
|
||||
)
|
||||
self._parts.append(f"\\begin{{{environment}}}{matrix_content}\\end{{{environment}}}")
|
||||
return self
|
||||
|
||||
def clear(self) -> 'LaTeXBuilder':
|
||||
"""
|
||||
Clear all parts (reset the builder).
|
||||
|
||||
Returns:
|
||||
Self for method chaining
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_text("x").clear().add_text("y").build()
|
||||
'y'
|
||||
"""
|
||||
self._parts.clear()
|
||||
return self
|
||||
|
||||
def build(self) -> str:
|
||||
"""
|
||||
Build the final LaTeX string.
|
||||
|
||||
Returns:
|
||||
The constructed LaTeX expression
|
||||
|
||||
Examples:
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_fraction("a", "b").build()
|
||||
'\\frac{a}{b}'
|
||||
|
||||
>>> builder = LaTeXBuilder()
|
||||
>>> builder.add_text("x").add_text(" + ").add_text("y").build()
|
||||
'x + y'
|
||||
"""
|
||||
return "".join(self._parts)
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""String representation (same as build())."""
|
||||
return self.build()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Debug representation."""
|
||||
return f"LaTeXBuilder(parts={len(self._parts)})"
|
||||
Reference in New Issue
Block a user