服务: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 默认密钥已清空
321 lines
9.3 KiB
Python
321 lines
9.3 KiB
Python
"""
|
|
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)})"
|