文件预览

preview.py

查看 word-latex-formula 技能包中的文件内容。

文件内容

resources/latex_convert_project/latex_convert/preview.py

from __future__ import annotations

from .math_parser import Delim, Frac, Node, Script, Seq, Text, parse_formula


GREEK_TO_LATEX = {
    "α": r"\alpha",
    "β": r"\beta",
    "γ": r"\gamma",
    "δ": r"\delta",
    "ε": r"\epsilon",
    "ζ": r"\zeta",
    "η": r"\eta",
    "θ": r"\theta",
    "ϑ": r"\vartheta",
    "κ": r"\kappa",
    "λ": r"\lambda",
    "μ": r"\mu",
    "ν": r"\nu",
    "ξ": r"\xi",
    "π": r"\pi",
    "ρ": r"\rho",
    "σ": r"\sigma",
    "τ": r"\tau",
    "φ": r"\phi",
    "χ": r"\chi",
    "ψ": r"\psi",
    "ω": r"\omega",
    "Γ": r"\Gamma",
    "Δ": r"\Delta",
    "Θ": r"\Theta",
    "Λ": r"\Lambda",
    "Ξ": r"\Xi",
    "Π": r"\Pi",
    "Σ": r"\Sigma",
    "Φ": r"\Phi",
    "Ψ": r"\Psi",
    "Ω": r"\Omega",
    "ℓ": r"\ell",
    "ℛ": r"\mathcal{R}",
    "𝓡": r"\mathcal{R}",
}

SYMBOL_TO_LATEX = {
    "≤": r"\le",
    "≥": r"\ge",
    "≈": r"\approx",
    "∈": r"\in",
    "∞": r"\infty",
    "∂": r"\partial",
    "∑": r"\sum",
    "∏": r"\prod",
    "±": r"\pm",
    "×": r"\times",
    "·": r"\cdot",
    "−": "-",
}


def formula_to_latex(text: str) -> str:
    """Best-effort LaTeX preview for the Web UI; Word output still uses OMML."""
    try:
        return node_to_latex(parse_formula(text))
    except Exception:
        return escape_latex_text(text)


def node_to_latex(node: Node) -> str:
    if isinstance(node, Seq):
        return "".join(node_to_latex(item) for item in node.items)
    if isinstance(node, Text):
        return escape_latex_text(node.value)
    if isinstance(node, Frac):
        return r"\frac{" + node_to_latex(node.num) + "}{" + node_to_latex(node.den) + "}"
    if isinstance(node, Script):
        base = _wrap_script_base(node.base)
        suffix = ""
        if node.sub is not None:
            suffix += "_{" + node_to_latex(node.sub) + "}"
        if node.sup is not None:
            suffix += "^{" + node_to_latex(node.sup) + "}"
        return base + suffix
    if isinstance(node, Delim):
        return node.begin + node_to_latex(node.body) + node.end
    return ""


def escape_latex_text(value: str) -> str:
    out: list[str] = []
    for index, ch in enumerate(value):
        if ch in GREEK_TO_LATEX:
            command = GREEK_TO_LATEX[ch]
            out.append(command)
            if _needs_command_separator(command, value[index + 1 : index + 2]):
                out.append(" ")
        elif ch in SYMBOL_TO_LATEX:
            out.append(SYMBOL_TO_LATEX[ch])
        elif ch == "\\":
            out.append(r"\backslash ")
        elif ch in {"&", "%", "$", "#"}:
            out.append("\\" + ch)
        else:
            out.append(ch)
    return "".join(out)


def _needs_command_separator(command: str, next_char: str) -> bool:
    return bool(next_char and next_char.isalpha() and command.startswith("\\") and command[-1].isalpha())


def _wrap_script_base(node: Node) -> str:
    if isinstance(node, Text):
        return node_to_latex(node)
    return "{" + node_to_latex(node) + "}"