在线教育中公式的使用小结

公式格式简述

  • Latex

    一种排版系统和标记语言,通常用于科学、技术、工程和数学(STEM)领域的文档,如学术论文、报告、书籍等。

    在学术界和专业领域中得到广泛应用。

    示例:\sqrt[y]{x} 渲染为 <math xmlns="http://www.w3.org/1998/Math/MathML"> x y \sqrt[y]{x} </math>yx

  • MML (MathML)

    全称为 Mathematical Markup Language(数学标记语言),一种用于在计算机中描述数学公式和数学内容的标记语言,它使用 XML 格式来在计算机系统中表示数学公式。

    示例:

    xml 复制代码
    <math xmlns="http://www.w3.org/1998/Math/MathML">
    	<mroot>
    		<mi>x</mi>
    		<mi>y</mi>
    	</mroot>
    </math>
  • OMML

    Office Math Markup Language(OMML)是由微软开发的一种标记语言,用于在Microsoft Office文档中表示和排版数学公式和数学内容。OMML 是一种基于XML的标记语言,旨在帮助用户在Microsoft Word、Microsoft PowerPoint 和 Microsoft Excel等Office应用程序中创建和编辑复杂的数学表达式。

    OMML 用于 Office Word 中渲染,这里不放示例了。

总结一下这个三种公式格式:

  • Latex 在大型论文领域应用的最广泛,所以它对各种奇形怪状的化学/物理公式支持的都较好;
  • MML 主要是为了在网页上渲染数学公式;
  • OMML 主要是为了在 Office 中渲染数学公式。

成熟的教育产品是如何使用公式的?

MML/OMML 都是侧重于 数学公式,而 Latex 对于理科公式都能良好的表达。

但 MML 便于网页渲染,Latex 则无法直接在网页上渲染。

目前的在线题库/教育资源等网站,一般都采用同时保存 MML/ Latex 公式,便于不同场景的使用。

学科网开放平台推题返回试题信息示例:

我们可以看到,仍在标签中保留了 latex 源码,主要可能用于后台试题编辑。

xml 复制代码
 <math latex="$xOy$">
                <mrow><mi>x</mi><mi>O</mi><mi>y</mi></mrow>
            </math>
            <span style="font-family: 宋体;">中,已知圆</span>
            <math latex="$C:5{{x}^{2}}+5{{y}^{2}}+10y-4=0$">
                <mrow>
                    <mi>C</mi><mo>:</mo><mn>5</mn><msup><mi>x</mi><mn>2</mn></msup><mo>+</mo><mn>5</mn><msup><mi>y</mi><mn>2</mn></msup><mo>+</mo><mn>10</mn><mi>y</mi><mo>−</mo><mn>4</mn><mo>=</mo><mn>0</mn>
                </mrow>
            </math>

学科网页面渲染,采用 将公式转为 SVG 的方式渲染

21世纪教育网暂无开放平台,页面渲染则采用将 MML 公式转为 PNG 的方式来渲染

公式格式转换

在线题库的题量需求很大,达到百万级也只是毛毛雨。

试题大部分都来源于批量导入,而不是教研老师一道题一道题在页面上编辑。

使用到最多的就是 Word 导入,而上面我们讲过,Word 中公式的格式为 OMML 格式,所以此时就涉及到 OMML --> Latex / MML 格式的转换。

公式之间的相互转换,不是一件简单的事。因为他们各自都有自己的标准。

有一件尴尬的事,目前并没有很好的直接转换 OMML 为 Latex 的方法,所以公式转换的路径一般为 OMML --> MML --> Latex。

比较幸运的,Office 中携带了一些映射文件,我们可以借助映射文件,比较方便的实现上述的路径。

使用方法具体如下:

  1. 将映射文件放入项目中

  2. 构建转换方法

    java 复制代码
        //设置xls依赖文件的路径
        public static URIResolver r = (href, base) -> {
            InputStream inputStream = FormulaConvertUtil.class.getResourceAsStream("/formula/xsl/" + href);
            return new StreamSource(inputStream);
        };
    
        /**
         * <p>Description: office mathml转为mml </p>
         *
         * @param xml
         * @return
         */
        public static String convertOMMLToMML(String xml) {
            String result = xslConvert(xml, "/formula/xsl/omml2mml.xsl", null);
            return result;
        }
    
        /**
         * mathml to latex
         *
         * @param mml mathml 公式源码
         * @return java.lang.String
         */
        public static String convertMMLToLatex(String mml) {
            String latex = xslConvert(mml, "/formula/xsl/mmltex.xsl", r);
            //latex = latex.replaceAll("△"," \\\\triangle ");
            return latex;
        }
    
        /**
         * xsl Convert
         *
         * @param formula     公式
         * @param uriResolver 转换器
         * @return java.lang.String
         * @author YangXinFu
         */
        public static String xslConvert(String formula, String xslPath, URIResolver uriResolver) {
            TransformerFactory tFac = TransformerFactory.newInstance();
            if (uriResolver != null) tFac.setURIResolver(uriResolver);
    //        InputStream inputStream = FormulaConvertUtil.class.getResourceAsStream("/formula/xsl/mmltex.xsl");
            InputStream inputStream = FormulaConvertUtil.class.getResourceAsStream(xslPath);
            StreamSource xslSource = new StreamSource(inputStream);
            StringWriter writer = new StringWriter();
            try {
                Transformer t = tFac.newTransformer(xslSource);
                Source source = new StreamSource(new StringReader(formula));
                Result result = new StreamResult(writer);
                t.transform(source, result);
            } catch (TransformerException e) {
                log.warn("公式转换失败。",e);
                return "";
            }
            return writer.getBuffer().toString();
        }

到这里基本可以使用了,但还需要注意转换并不是 100% 映射,不常用的符号有可能失败。

Latex转图

在 Java 中 Latex 公式转图片,一般使用 jlatexmath

但是在 Maven 仓库中 1.0.8 版本,不支持 \ce 命令,使用时需要注意。

\ce 命令被包含在 Latex 的 Mhchem 扩展中,所以需要额外功能支持。例如:\ce{SO4^2- + Ba^2+ -> BaSO4 v} 应渲染为 :

但,其实早在 2018 年,已经有人提过 issue:

对于 mhchem 的支持,已经被合并到 github.com/opencollab/... 实验分支上,并未推送正式版 jar 包到 Maven Resp 中。

所以想要使用该特性,我们只需要将 experimental 分支代码拉到本地,进行简单的修改,打包推送到自己的 Maven 私服中便可使用。

在此分支中,包含一些 GWT 的代码

GWT 是 Google Web Toolkit 的缩写,它是一个用于构建富客户端 Web 应用程序的开源开发工具集。GWT 最初由 Google 开发并发布,旨在帮助开发人员使用 Java 编程语言来构建现代的、高性能的、交互式的 Web 应用程序。

开发人员可以使用熟悉的 Java 编程语言来构建客户端代码,然后使用 GWT 编译器将 Java 代码转换为高效的 JavaScript 代码。

但是,对在服务转换的情况,基本没什么用。我们只需要"酌情"把 GWT 相关的代码,全部干掉就行了。

打完包,使用方法如下:

java 复制代码
    // 这里需要初始化一个 Provider。
    static {
        FactoryProvider.setInstance(new FactoryProviderDesktop());
    }

    public static Image latexToImg(String latex, Float size) {

        int style = TeXConstants.STYLE_DISPLAY; // 样式 符号以最大的尺寸呈现
        float fontSize = Objects.isNull(size) ? 16 : size ; // 生成公式图片的字体大小
        // 字体颜色,黑色
        ColorD fg = new ColorD(Color.BLACK);

        TeXFormula.setDPITarget(72);

        return (BufferedImage) TeXFormula.createBufferedImage(latex, style, fontSize * 3, fg, null);
    }

另外,还有 Aspose.TeX 工具可以转图,但是暂不支持中文,即公式中包含中文时,会乱码。

小结

在线题库项目中,公式可存储为 Latex/MML 两种格式,MML 进一步可转为 SVG/PNG 等格式在线渲染,Latex 格式可用于在线编辑。

MML目前没有友好的可视化编辑器,而 Latex 公式有 LatexLive 等很优秀的可视化编辑器。

另外,公式转换想一劳永逸是不可能的事,因为总会有一些奇奇怪怪的符号没发转换,这时只能手动的进行符号对应。

举个例子:

Latex 中有长等号 $\xlongequal[b]{a}$ 表现为 <math xmlns="http://www.w3.org/1998/Math/MathML"> = b a \xlongequal[b]{a} </math>a b

但是 OMML 并没有对应的符号可以表示,只能通过其他形式处理。

相关推荐
ღ᭄ꦿ࿐Never say never꧂几秒前
微服务架构中的负载均衡与服务注册中心(Nacos)
java·spring boot·后端·spring cloud·微服务·架构·负载均衡
所待.3832 分钟前
小小扑克牌算法
java·算法
.生产的驴9 分钟前
SpringBoot 消息队列RabbitMQ 消息确认机制确保消息发送成功和失败 生产者确认
java·javascript·spring boot·后端·rabbitmq·负载均衡·java-rabbitmq
.生产的驴10 分钟前
SpringBoot 消息队列RabbitMQ在代码中声明 交换机 与 队列使用注解创建
java·spring boot·分布式·servlet·kafka·rabbitmq·java-rabbitmq
海里真的有鱼17 分钟前
Spring Boot 中整合 Kafka
后端
idealzouhu23 分钟前
Java 并发编程 —— AQS 抽象队列同步器
java·开发语言
布瑞泽的童话23 分钟前
无需切换平台?TuneFree如何搜罗所有你爱的音乐
前端·vue.js·后端·开源
听封27 分钟前
Thymeleaf 的创建
java·spring boot·spring·maven
写bug写bug33 分钟前
6 种服务限流的实现方式
java·后端·微服务