最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實(shí)現(xiàn)LaTeX轉(zhuǎn)為OMML并寫入word文檔

 更新時(shí)間:2026年04月03日 09:27:42   作者:咘嚕biu  
本文主要為大家詳細(xì)介紹了如何將LaTeX格式的數(shù)學(xué)公式轉(zhuǎn)換為Word文檔中的OMML格式,以解決直接轉(zhuǎn)為圖片導(dǎo)致的清晰度和可編輯性問(wèn)題,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下

1.需求

我有一些數(shù)學(xué)考題試卷數(shù)據(jù)存在數(shù)據(jù)庫(kù),考題數(shù)據(jù)是以大文本的形式存儲(chǔ),其中公式部分的格式為L(zhǎng)aTeX,現(xiàn)在需要將這些數(shù)據(jù)生成word文檔供用戶下載,要求公式能夠正確在文檔中顯示,一種方案是將LaTex轉(zhuǎn)換成圖片,然后寫入word文檔,缺點(diǎn)是清晰度不高,而且不可編輯。所以需要一個(gè)原生的解決方案,自然就想到word對(duì)LaTex的支持,遺憾的是word不支持LaTex, 而是有自己的格式OMML, 所以需要一個(gè)工具來(lái)將LaTex轉(zhuǎn)換成OMML,現(xiàn)有的資料顯示需要先將LaTex轉(zhuǎn)換成MathML,然后將MathML轉(zhuǎn)換成OMML。

2.代碼結(jié)構(gòu)

3.pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.8</version>
        <relativePath/>
    </parent>
    <groupId>com.latex</groupId>
    <artifactId>latex</artifactId>
    <version>1.0.0</version>
    <name>latex</name>
    <description>latex</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 以下為轉(zhuǎn)換和word操作需要的依賴 -->
        <dependency>
            <groupId>de.rototor.snuggletex</groupId>
            <artifactId>snuggletex-core</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.11.0</version>
        </dependency>
    </dependencies>
    <build>
        <!--
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>MML2OMML.XSL</include>
                </includes>
            </resource>
        </resources>
        -->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4.轉(zhuǎn)換工具:LatexUtil

package com.latex.util;

import com.latex.enums.TextCircledEnum;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.officeDocument.x2006.math.CTOMath;
import org.openxmlformats.schemas.officeDocument.x2006.math.CTOMathPara;
import org.openxmlformats.schemas.officeDocument.x2006.math.CTR;
import uk.ac.ed.ph.snuggletex.SnuggleEngine;
import uk.ac.ed.ph.snuggletex.SnuggleInput;
import uk.ac.ed.ph.snuggletex.SnuggleSession;

import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;

public class LatexUtil {
    private static Transformer transformer;
    private static final SnuggleEngine snuggleEngine =  new SnuggleEngine();

    /**
     * 將latex公式添加到poi段落中
     * @param paragraph 段落
     * @param latex 公式
     */
    public static void addToParagraph(XWPFParagraph paragraph, String latex) throws Exception{
        latex = "$" + filter(latex) + "$"; //處理特殊符號(hào)
        paragraph.getCTP().addNewOMath().set(getCTOMath(latex));
    }

    /**
     * 將Latex公式轉(zhuǎn)換成CTOMath(可直接寫入word)
     * @param latex 公式
     * 這里的latex表達(dá)式 必須用$$包裹,例如:$\sin^2 \theta + \cos^2 \theta = 1$
     */
    public static CTOMath getCTOMath(String latex) throws Exception {
        System.out.println("Latex: " + latex);
        String mathML = toMathML(latex);
        System.out.println("MathML: " + mathML);
        String OMML = toOMML(mathML);
        System.out.println("OMML: " + OMML);
        return toCTOMath(OMML);
    }

    /**
     * latex轉(zhuǎn)MathML
     */
    public static String toMathML(String latex) throws IOException {
        SnuggleSession session = snuggleEngine.createSession();
        session.parseInput(new SnuggleInput(latex));
        return session.buildXMLString();
    }

    /**
     * MathML轉(zhuǎn)OMML
     */
    public static String toOMML(String mathML) throws TransformerException, IOException {
        Transformer transformer = getTransformer();
        StreamSource source = new StreamSource(new StringReader(mathML));
        StringWriter stringwriter = new StringWriter();
        StreamResult result = new StreamResult(stringwriter);
        transformer.transform(source, result);
        String OMML = stringwriter.toString();
        stringwriter.close();
        return OMML;
    }

    /**
     * 基于OMML創(chuàng)建POI組件:CTOMath
     */
    public static CTOMath toCTOMath(String OMML) throws XmlException {
        CTOMathPara ctOMathPara = CTOMathPara.Factory.parse(OMML);
        CTOMath ctOMath = ctOMathPara.getOMathArray(0);
        //for making this to work with Office 2007 Word also, special font settings are necessary
        XmlCursor xmlcursor = ctOMath.newCursor();
        while (xmlcursor.hasNextToken()) {
            XmlCursor.TokenType tokentype = xmlcursor.toNextToken();
            if (tokentype.isStart()) {
                if (xmlcursor.getObject() instanceof CTR) {
                    CTR cTR = (CTR) xmlcursor.getObject();
                    cTR.addNewRPr2().addNewRFonts().setAscii("Cambria Math");
                    cTR.getRPr2().getRFonts().setHAnsi("Cambria Math"); // up to apache poi 4.1.2
                    //cTR.getRPr2().getRFontsArray(0).setHAnsi("Cambria Math"); // since apache poi 5.0.0
                }
            }
        }
        return ctOMath;
    }

    /**
     * 發(fā)現(xiàn)存在無(wú)法識(shí)別的符號(hào),因此單獨(dú)處理,提前過(guò)濾識(shí)別掉
     * ①②③④⑤等符合無(wú)法識(shí)別,即latex表達(dá)式是 \textcircled
     * @param latex latex表達(dá)式
     * @return 處理結(jié)果
     */
    public static String filter(String latex){
        if(!latex.contains("textcircled")){
            return latex;
        }
        return TextCircledEnum.replaceTextCircled(latex);
    }

    private static Transformer getTransformer() throws TransformerException {
        if(transformer == null){
            //實(shí)測(cè)發(fā)現(xiàn)JDK21需要修改以下配置,不然newTransformer會(huì)報(bào)錯(cuò):
            //javax.xml.transform.TransformerConfigurationException: JAXP0801001
            //System.setProperty("jdk.xml.xpathExprGrpLimit", "0"); // 0表示無(wú)限制
            //System.setProperty("jdk.xml.xpathExprOpLimit", "0");  // 同時(shí)設(shè)置操作符限制

            //如果讀取不到MML2OMML.XSL文件,請(qǐng)檢查pom文件
            //build->resources->resource->includes下添加:<include>MML2OMML.XSL</include>
            InputStream in = LatexUtil.class.getClassLoader().getResourceAsStream("MML2OMML.XSL");
            TransformerFactory tFactory = TransformerFactory.newInstance();
            transformer = tFactory.newTransformer(new StreamSource(in));
        }
        return transformer;
    }
}

5.特殊符號(hào)處理:TextCircledEnum

package com.latex.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum TextCircledEnum {
    Zero("\\\\textcircled\\{0\\}","?"),
    One("\\\\textcircled\\{1\\}","①"),
    Two("\\\\textcircled\\{2\\}","②"),
    Three("\\\\textcircled\\{3\\}","③"),
    Four("\\\\textcircled\\{4\\}","④"),
    Five("\\\\textcircled\\{5\\}","⑤"),
    Six("\\\\textcircled\\{6\\}","⑥"),
    Seven("\\\\textcircled\\{7\\}","⑦"),
    Eight("\\\\textcircled\\{8\\}","⑧"),
    Nine("\\\\textcircled\\{9\\}","⑨"),
    Ten("\\\\textcircled\\{10\\}","⑩");

    private final String code;
    private final String value;

    public static String replaceTextCircled(String latex){
        for (TextCircledEnum c : values()) {
            latex = latex.replaceAll(c.getCode(), c.getValue());
        }
        return latex;
    }
}

6.測(cè)試類:LatexUtilTest

package com.latex.test;

import com.latex.util.LatexUtil;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;

import java.io.FileOutputStream;

public class LatexUtilTest {
    public static void main(String[] args) throws Exception {
        XWPFDocument document = new XWPFDocument();
        XWPFParagraph paragraph = document.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.LEFT);
        paragraph.setFontAlignment(ParagraphAlignment.LEFT.getValue());

        paragraph.createRun().setText("前置文本:");
        LatexUtil.addToParagraph(paragraph, "\\sin^2 \\theta + \\cos^2 \\theta = 1");
        paragraph.createRun().setText("后置文本");


        // 保存文檔到文件
        String filePath = "D:\\Test\\math_document_"+System.currentTimeMillis()+".docx";
        try (FileOutputStream out = new FileOutputStream(filePath)) {
            document.write(out);
            System.out.println("Word文檔已成功保存");
        }

        // 關(guān)閉文檔
        document.close();
    }
}

7.測(cè)試輸出

Latex: $\sin^2 \theta + \cos^2 \theta = 1$
MathML: <math xmlns="http://www.w3.org/1998/Math/MathML"><msup><mi>sin</mi><mn>2</mn></msup><mi>θ</mi><mo>+</mo><msup><mi>cos</mi><mn>2</mn></msup><mi>θ</mi><mo>=</mo><mn>1</mn></math>
OMML: <?xml version="1.0" encoding="UTF-8"?><m:oMath xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:mml="http://www.w3.org/1998/Math/MathML"><m:sSup><m:e><m:r><m:rPr><m:sty m:val="p"/></m:rPr><m:t>sin</m:t></m:r></m:e><m:sup><m:r><m:t>2</m:t></m:r></m:sup></m:sSup><m:r><m:t>θ</m:t></m:r><m:r><m:t>+</m:t></m:r><m:sSup><m:e><m:r><m:rPr><m:sty m:val="p"/></m:rPr><m:t>cos</m:t></m:r></m:e><m:sup><m:r><m:t>2</m:t></m:r></m:sup></m:sSup><m:r><m:t>θ</m:t></m:r><m:r><m:t>=</m:t></m:r><m:r><m:t>1</m:t></m:r></m:oMath>
Word文檔已成功保存

8.Word文檔內(nèi)容

9.MML2OMML.XSL文件獲取

MML2OMML.XSL文件的路徑一般在C盤:C:\Program Files\Microsoft Office\root\Office16

到此這篇關(guān)于Java實(shí)現(xiàn)LaTeX轉(zhuǎn)為OMML并寫入word文檔的文章就介紹到這了,更多相關(guān)Java LaTeX轉(zhuǎn)OMML并寫入word內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java并發(fā)編程之線程創(chuàng)建介紹

    Java并發(fā)編程之線程創(chuàng)建介紹

    這篇文章主要介紹了Java并發(fā)編程之線程創(chuàng)建,進(jìn)程是代碼在數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位,線程則是一個(gè)實(shí)體,一個(gè)進(jìn)程中至少有一個(gè)線程,下文更多相關(guān)內(nèi)容需要的小伙伴可以參考一下
    2022-04-04
  • 詳解多線程及Runable 和Thread的區(qū)別

    詳解多線程及Runable 和Thread的區(qū)別

    這篇文章主要介紹了多線程及Runable 和Thread的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • SpringBoot實(shí)現(xiàn)自定義注解用于文件驗(yàn)證的詳細(xì)過(guò)程(大小、擴(kuò)展名、MIME類型)

    SpringBoot實(shí)現(xiàn)自定義注解用于文件驗(yàn)證的詳細(xì)過(guò)程(大小、擴(kuò)展名、MIME類型)

    SpringBoot,Spring Cloud中經(jīng)常需要處理文件上傳的功能,為了確保上傳的文件滿足特定的要求(如擴(kuò)展名、MIME類型和文件大小),我們可以創(chuàng)建一個(gè)自定義注解來(lái)簡(jiǎn)化驗(yàn)證過(guò)程,需要的朋友可以參考下
    2024-08-08
  • SpringBoot2整合Redis多數(shù)據(jù)源步驟詳解

    SpringBoot2整合Redis多數(shù)據(jù)源步驟詳解

    這篇文章主要介紹了SpringBoot2整合Redis多數(shù)據(jù)源步驟詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 詳解Mybatis模板(已優(yōu)化)適合小白

    詳解Mybatis模板(已優(yōu)化)適合小白

    這篇文章主要介紹了Mybatis模板(已優(yōu)化)適合小白,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • spring的事務(wù)傳播屬性REQUIRED_NESTED原理

    spring的事務(wù)傳播屬性REQUIRED_NESTED原理

    這篇文章主要介紹了spring的事務(wù)傳播屬性REQUIRED_NESTED原理,在spring中,要想使用事務(wù)中的回滾點(diǎn),可以使用傳播屬性NESTED,需要的朋友可以參考下
    2023-05-05
  • java讀取某個(gè)文件夾下的所有文件實(shí)例代碼

    java讀取某個(gè)文件夾下的所有文件實(shí)例代碼

    這篇文章主要介紹了java讀取某個(gè)文件夾下的所有文件實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Spring boot攔截器實(shí)現(xiàn)IP黑名單的完整步驟

    Spring boot攔截器實(shí)現(xiàn)IP黑名單的完整步驟

    這篇文章主要給大家介紹了關(guān)于Spring boot攔截器實(shí)現(xiàn)IP黑名單的完整步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring boot攔截器具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • Java之源文件的執(zhí)行全過(guò)程

    Java之源文件的執(zhí)行全過(guò)程

    這篇文章主要介紹了Java之源文件的執(zhí)行全過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • java 實(shí)現(xiàn)線程同步的方式有哪些

    java 實(shí)現(xiàn)線程同步的方式有哪些

    當(dāng)使用多個(gè)線程來(lái)訪問(wèn)同一個(gè)數(shù)據(jù)時(shí),非常容易出現(xiàn)線程安全問(wèn)題,所以我們用同步機(jī)制來(lái)解決這些問(wèn)題,本文將詳細(xì)介紹,需要的朋友可以參考下
    2012-11-11

最新評(píng)論

施甸县| 长白| 宿迁市| 伊宁县| 仙居县| 新竹县| 孝感市| 无锡市| 新营市| 诏安县| 文山县| 磐安县| 乌鲁木齐市| 罗城| 紫阳县| 虹口区| 静宁县| 公安县| 临洮县| 达拉特旗| 长武县| 华池县| 视频| 石楼县| 通辽市| 慈溪市| 安溪县| 武穴市| 阳谷县| 罗平县| 长宁县| 丹江口市| 稷山县| 巩留县| 广安市| 雷州市| 聊城市| 武宣县| 香港 | 望江县| 莱阳市|