javascript 在Android中显示好看的数学公式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17029780/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 06:50:24  来源:igfitidea点击:

Display Good-looking Math Formula in Android

javascriptandroidmathmathjaxjqmath

提问by Neoh

It is sad to see that science and math is never given enough attention in Android platform. Maybe this is an easy problem, but I'm a total dudein javascript so I'm struggling to understand how to solve this problem.

很遗憾看到科学和数学在 Android 平台上从未得到足够的重视。也许这是一个简单的问题,但我完全是 javascript 方面的老手,所以我很难理解如何解决这个问题。

What I want to do is simple: I just need to use render mathematic equation. It can be MathJax or JqMath or anything that is small and fast. If I have to do it in a webview, how can I show plain text in paragraph and formatted equation within the same webview?

我想做的很简单:我只需要使用渲染数学方程。它可以是 MathJax 或 JqMath 或任何小而快的东西。如果我必须在 webview 中执行此操作,如何在同一 webview 中的段落和格式化方程中显示纯文本?

(anyone comes out with other methods which obviously works, I'll consider that as solution also)

(任何人都提出了其他显然有效的方法,我也将其视为解决方案)

WHAT I'VE DONE SO FAR:

到目前为止我做了什么:

I started with using MathJax to put formula into webview (I dunno if there is any successful jqMath use case out there? Anyone care to share their experience?) I can reproduce all the functionalities just like that of the standalone MathJax App. In that app, a user keys in a string into EditText field and MathJax will render the formula in Latex once user presses a button to confirm.

我开始使用 MathJax 将公式放入 webview(我不知道是否有任何成功的 jqMath 用例?有人愿意分享他们的经验吗?)我可以像独立的MathJax App一样重现所有功能。在该应用程序中,用户在 EditText 字段中输入一个字符串,一旦用户按下按钮进行确认,MathJax 就会在 Latex 中呈现公式。

I don't need user to confirm. I thought this should be easier because there is no user-interaction, but somehow the equation never gets displayed. I know I must be missing something because the codes in the standalone MathJax is meant for user input. Which part of the code should I modify to directly render an equation from a string, say, \sqrt{b^2-4ac} ? Here is the initial header for webview:

我不需要用户确认。我认为这应该更容易,因为没有用户交互,但不知何故,方程式永远不会显示。我知道我一定遗漏了一些东西,因为独立 MathJax 中的代码用于用户输入。我应该修改代码的哪一部分以直接从字符串渲染方程,例如 \sqrt{b^2-4ac} ?这是 webview 的初始标题:

WebView w = (WebView) findViewById(R.id.webview);
w.getSettings().setJavaScriptEnabled(true);
w.getSettings().setBuiltInZoomControls(true);
w.loadDataWithBaseURL("http://bar", "<script type='text/x-mathjax-config'>"
                        +"MathJax.Hub.Config({ showMathMenu: false, "
                        +"jax: ['input/TeX','output/HTML-CSS'], "
                        +"extensions: ['tex2jax.js'], " 
                        +"TeX: { extensions: ['AMSmath.js','AMSsymbols.js',"
                        +"'noErrors.js','noUndefined.js'] } "
                        +"});</script>"
                        +"<script type='text/javascript' "
                        +"src='file:///android_asset/MathJax/MathJax.js'"
                        +"></script><span id='math'></span>","text/html","utf-8","");

And here is how the original webview loads when user presses a button after input to edittext:

这是当用户在输入到 edittext 后按下按钮时原始 webview 的加载方式:

WebView w = (WebView) findViewById(R.id.webview);
EditText e = (EditText) findViewById(R.id.edit);
w.loadUrl("javascript:document.getElementById('math').innerHTML='\\["
                  +doubleEscapeTeX(e.getText().toString())
                  +"\\]';");
w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");

private static String doubleEscapeTeX(String s) {
    String t="";
    for (int i=0; i < s.length(); i++) {
        if (s.charAt(i) == '\'') t += '\';
        if (s.charAt(i) != '\n') t += s.charAt(i);
        if (s.charAt(i) == '\') t += "\";
    }
    return t;
}

I attempted to replace with a hard-coded string

我试图用硬编码的字符串替换

w.loadUrl("javascript:document.getElementById('math').innerHTML='\\["
                  +doubleEscapeTeX("sample string")
                  +"\\]';");

but it doesn't work, the text "sample string"won't even show up in the webview.

但它不起作用,文本"sample string"甚至不会显示在 web 视图中。

[SOLUTION] see Joe's Answer

[解决方案] 见乔的回答

To elaborate on his answer, this is an example on how to set a sentence in plain text and then display a formatted equation in display form (all in a single webview):

为了详细说明他的回答,这是一个关于如何以纯文本形式设置句子,然后以显示形式显示格式化方程的示例(全部在单个 web 视图中):

Replace the last line above at

将上面的最后一行替换为

+"></script><span id='math'></span>","text/html","utf-8","");

with

+"></script><span id='text'>This is plain text.</span> <span id='math'></span>", "text/html", "utf-8", "");

Then call

然后打电话

w.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
        if (!url.startsWith("http://bar"))
            return;
        w.loadUrl("javascript:document.getElementById('math').innerHTML='\\["
                    +doubleEscapeTeX("\sqrt{b^2-4ac}") + "\\]';");
        w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
    }
});

This will set a string This is plain textin normal font and center-display the formula foo+barin webview.

这将设置一个字符串这是普通字体的纯文本,并foo+bar在 webview 中居中显示公式。

EDIT (Android 4.4 issue)

编辑(Android 4.4 问题)

Starting with Android KitKat, you have to replace all the lines loadUrl(...)with evaluateJavascript(...). But this is only available for KitKat (API 19 or above), so you need to do an SDK version check first. For example:

从 Android KitKat 开始,您必须将所有行替换loadUrl(...)evaluateJavascript(...). 但这仅适用于 KitKat(API 19 或更高版本),因此您需要先进行 SDK 版本检查。例如:

if (android.os.Build.VERSION.SDK_INT < 19) {
    w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
} else {
    w.evaluateJavascript("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);",null);
}

UPDATE (AS OF MATHJAX 2.5)

更新(从 MATHJAX 2.5 开始)

Because there are folders changes, previous code won't work on latest version of MathJax. Besides, the recommended way to minimalize the size of local MathJax is now using Grunt. The template to reduce size of MathJax can be found here.

由于文件夹发生变化,以前的代码不适用于最新版本的 MathJax。此外,最小化本地 MathJax 大小的推荐方法现在是使用Grunt。可以在此处找到减小 MathJax 大小的模板。

Assuming you have successfully reduced the size of MathJax, and assuming output of HTML-CSS, here is a simple version that can load MathJax:

假设你已经成功地减小了 MathJax 的大小,并假设输出的是 HTML-CSS,这里是一个可以加载 MathJax 的简单版本:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final WebView w = (WebView) findViewById(R.id.webview);
    w.getSettings().setJavaScriptEnabled(true);
    w.getSettings().setBuiltInZoomControls(true);

    String Url = "</style>"
                 + "<script type='text/x-mathjax-config'>"
                 + "  MathJax.Hub.Config({" + "    showMathMenu: false,"
                 + "             jax: ['input/TeX','output/HTML-CSS', 'output/CommonHTML'],"
                 + "      extensions: ['tex2jax.js','MathMenu.js','MathZoom.js', 'CHTML-preview.js'],"
                 + "         tex2jax: { inlineMath: [ ['$','$'] ], processEscapes: true },"
                 + "             TeX: {" + "               extensions:['AMSmath.js','AMSsymbols.js',"
                 + "                           'noUndefined.js']" + "             }"
                 + "  });"
                 + "</script>"
                 + "<script type='text/javascript' src='file:///android_asset/MathJax/MathJax.js'>"
                 + "</script>"
                 + "<p style=\"line-height:1.5; padding: 16 16\" align=\"justify\">"
                 + "<span >";

    // Demo display equation
    url += "This is a display equation: $$P=\frac{F}{A}$$";

    url += "This is also an identical display equation with different format:\[P=\frac{F}{A}\]";

    // equations aligned at equal sign
    url += "You can also put aligned equations just like Latex:";
    String align = "\begin{aligned}"
                + "F\; &= P \times A \\ "
                + "&= 4000 \times 0.2\\"
                + "&= 800\; \text{N}\end{aligned}"; 
    url += align;

    url += "This is an inline equation \sqrt{b^2-4ac}.";

    // Finally, must enclose the brackets
    url += "</span></p>";

    mWebView.loadDataWithBaseURL("http://bar", url, "text/html", "utf-8", "");
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (!url.startsWith("http://bar")) return;
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                view.loadUrl(JAVASCRIPT);
            } else {
                view.evaluateJavascript(JAVASCRIPT, null);
            }
        }
    });
}

Unlike Joes's answer, there is no need to separate text or math types, MathJax will just format them accordingly.

与 Joes 的回答不同,不需要分隔文本或数学类型,MathJax 只会相应地格式化它们。

However, it appears that there is a bug that webview in KitKat devices cannot render capital letter "A". Anyone who knows a way is welcome to provide a workaround for this.

但是,似乎存在一个错误,即 KitKat 设备中的 webview 无法呈现大写字母“A”。欢迎任何知道方法的人为此提供解决方法。

采纳答案by Joe

If I understand your problem correctly, it seems the issue is due to the MathJax library has not completed loading (from the loadDataWithBaseURL()call) when you call the additional loadUrl()s to display the formula. The simplest fix is to wait for the onPageFinished()callback to make the call.

如果我正确理解您的问题,问题似乎是由于 MathJax 库loadDataWithBaseURL()在您调用附加loadUrl()s 以显示公式时尚未完成加载(从调用中)。最简单的解决方法是等待onPageFinished()回调进行调用。

For example, the following code seems to work fine on mine:

例如,下面的代码在我的似乎工作正常:

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    final WebView w = (WebView) findViewById(R.id.webview);
    w.getSettings().setJavaScriptEnabled(true);
    w.getSettings().setBuiltInZoomControls(true);
    w.loadDataWithBaseURL("http://bar", "<script type='text/x-mathjax-config'>" 
            + "MathJax.Hub.Config({ "
            + "showMathMenu: false, " 
            + "jax: ['input/TeX','output/HTML-CSS'], " 
            + "extensions: ['tex2jax.js'], "
            + "TeX: { extensions: ['AMSmath.js','AMSsymbols.js'," 
            + "'noErrors.js','noUndefined.js'] } "
            + "});</script>" 
            + "<script type='text/javascript' " 
            + "src='file:///android_asset/MathJax/MathJax.js'"
            + "></script><span id='math'></span>", "text/html", "utf-8", "");
    w.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (!url.startsWith("http://bar")) return;
            w.loadUrl("javascript:document.getElementById('math').innerHTML='\\["
                    + doubleEscapeTeX("sample string") + "\\]';");
            w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
        }
    });
}

Update: To accommodate additional output in the WebView, I would suggest adding HTML elements to the initial loadDataWithBaseURL()call. So for the example above, instead of this line at the end:

更新:为了在 WebView 中容纳额外的输出,我建议在初始loadDataWithBaseURL()调用中添加 HTML 元素。所以对于上面的例子,而不是最后的这一行:

            + "></script><span id='math'></span>", "text/html", "utf-8", "");

We can do something like the following:

我们可以执行以下操作:

            + "></script><span id='text'>Formula:</span>"
            + "<span id='math'></span>", "text/html", "utf-8", "");

Additionally, if you need to update that part interactively afterward, you can use the same mechanism that we used for the "math" part, something like:

此外,如果您之后需要以交互方式更新该部分,您可以使用我们用于“数学”部分的相同机制,例如:

            w.loadUrl("javascript:document.getElementById('text').innerHTML='"
                    + newText + "';");

You can also use other HTML elements (instead of the <span>that we use above) if you want to have specific styling/formatting.

<span>如果您想要特定的样式/格式,您还可以使用其他 HTML 元素(而不是我们上面使用的元素)。

回答by Timothy Shields

Instead of using javascript and javascript libraries to render the LaTeX client-side, why not do the following?

与其使用 javascript 和 javascript 库来呈现 LaTeX 客户端,为什么不执行以下操作?

  1. Write a server-side function that takes some LaTeX string and produces an image file. You can do this with the "out of the box" standard LaTeX command line tool. (It seems like this is something you're capable of doing, and that the problem is in getting the rendering to take place client-side.)

  2. Create an endpoint on your web site/service that looks like the following:
    GET /latex?f={image format}&s={latex string}

  3. On your web pages, use a simple HTML <img/>tag to render your LaTeX. For example:
    <img src="/latex?f=jpeg&s=f%40x%41%61x%942"/>
    will render the f(x)=x^2equation as a JPEG.

  4. (optional) Create a simple server-side cache for (f,s)pairs so that you only render a particular LaTeX string on the first request ever made for that particular string (by any client). As a first prototype, you could just have a server directory in which you have files named {hash(s)}.{f}, where hashis just some hash function like SHA1.

  1. 编写一个服务器端函数,它接受一些 LaTeX 字符串并生成一个图像文件。您可以使用“开箱即用”的标准 LaTeX 命令行工具执行此操作。(这似乎是您有能力做的事情,而问题在于让渲染发生在客户端。)

  2. 在您的网站/服务上创建一个端点,如下所示:
    GET /latex?f={image format}&s={latex string}

  3. 在您的网页上,使用一个简单的 HTML<img/>标签来呈现您的 LaTeX。例如:
    <img src="/latex?f=jpeg&s=f%40x%41%61x%942"/>
    f(x)=x^2方程呈现为 JPEG。

  4. (可选)为(f,s)对创建一个简单的服务器端缓存,以便您仅在(由任何客户端)为该特定字符串发出的第一个请求中呈现特定的 LaTeX 字符串。作为第一个原型,您可以只有一个服务器目录,其中包含名为 的文件{hash(s)}.{f},其中hash只有一些散列函数,例如SHA1.

This relieves you from trying to make everything work client-side with javascript. And, I would argue (although some may disagree), that this is a lot cleaner. All you're sending to the client is:

这使您无需尝试使用 javascript 使一切都在客户端工作。而且,我会争辩说(尽管有些人可能不同意),这要干净得多。您发送给客户端的所有内容是:

  1. the HTML <img/>tag
  2. the actual image contents when the browser resolves the srcattribute
  1. HTML<img/>标签
  2. 浏览器解析src属性时的实际图片内容

Very lightweight and fast!

非常轻便和快速!

回答by kexanie

The "capital A" problem is a konwn bug of MathJax on Android WebView. You can replace the font files with tweaked font files. The fix will work when using HTML-CSS output with TeX-fonts. Besides MathJax, you can try KaTeX, which is less beautiful but faster.

“大写 A”问题是 Android WebView 上 MathJax 的一个已知错误。您可以用调整后的字体文件替换字体文件。当使用带有 TeX 字体的 HTML-CSS 输出时,此修复将起作用。除了 MathJax,你还可以试试KaTeX,它不那么漂亮但速度更快。

Actually I've packed them both into an Android custom view, have a look on MathView.

实际上我已经将它们都打包到一个 Android 自定义视图中,看看MathView.

回答by Roberto Canogar

Late to the party but I think I have a better solution than Joe's. The original question asked:

聚会迟到了,但我认为我有比乔更好的解决方案。原问题问:

how can I show plain text in paragraph and a formatted equation within the same webview?

如何在同一网页视图中显示段落中的纯文本和格式化的方程?

In the original example we had:

在最初的例子中,我们有:

w.loadUrl("javascript:document.getElementById('math').innerHTML='\\["
                  +doubleEscapeTeX("sample string")
                  +"\\]';");

Do you notice the \\\\[and \\\\]? Those four backslashes are interpreted by LaTeX as only one, and \[and \]in LaTeX is a shorthand for\begin{equation}... \end{equation}which produces a block of displayed mathematics. If we want a paragraph of text with some equations within the paragraph we need to get rid of the \\\\[and use the LaTeX command \(and \). The code for an example with paragraph mathematics and displayed mathematics would be:

你注意到\\\\[和 了\\\\]吗?这四个反斜线被乳胶解释为唯一的一个,并\[\]在乳胶为速记\begin{equation}... \end{equation}产生显示数学块。如果我们想要一段文本在段落中带有一些方程,我们需要去掉\\\\[并使用 LaTeX 命令\(\)。具有段落数学和显示数学的示例的代码是:

w.loadUrl("javascript:document.getElementById('math').innerHTML='"
   +doubleEscapeTeX(
           "This is a second degree equation \( \small ax^2+bx+c=0\) and its "
          +"roots are \[x=\frac{-b\pm \sqrt{b^2-4ac}}{2a}. \]"
    )+"';");

The WebView becomes: enter image description hereNotes:

WebView 变为: 在此处输入图片说明注:

  • We only need two backslashes instead of four, this is because we are inside doubleEscapeTeX()and this function duplicates the backslashes.

  • Also the text outside the math environments is rendered with HTML (inside the <span id='math'>tag).

  • The LaTeX font is bigger that the html font, so adding a \\smallin each math environment will make them roughly the same size. Here I added the \\smallcommand only to the first equation to show the difference.

  • 我们只需要两个反斜杠而不是四个,这是因为我们在里面doubleEscapeTeX()并且这个函数复制了反斜杠。

  • 数学环境之外的文本也用 HTML 呈现(在<span id='math'>标签内)。

  • LaTeX 字体比 html 字体大,因此\\small在每个数学环境中添加 a将使它们的大小大致相同。在这里,我\\small仅将命令添加到第一个方程以显示差异。

回答by Vasant

You can easily use math formula in android using below code.

您可以使用以下代码在 android 中轻松使用数学公式。

public void setView(String data,WebView w){
    WebSettings webSettings = w.getSettings();
    webSettings.setJavaScriptEnabled(true);
    String path="file:///android_asset/v/";
    String js="<html><head>"
       + "<link rel='stylesheet' href='file:///android_asset/v/jqmath.css'>"
       + "<script src = 'file:///android_asset/v/jquery-3.0.0.min.js'></script>"
       + "<script src = 'file:///android_asset/v/jqmath-etc-0.4.3.min.js'></script>"
       + "<script src = 'http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML'></script>"
       + "</head><body>"
       + "<script>var s ='"+data+"';M.parseMath(s);document.write(s);</script> </body>";
    w.loadDataWithBaseURL("", js, "text/html", "UTF-8", "");
}

回答by sinhayash

jqMath is a JavaScript library like MathJax, but much smaller, simpler, and faster.If you don't need all of LaTeX, you might like it. It should work fine on Android.

jqMath 是一个类似于 MathJax 的 JavaScript 库,但更小、更简单、更快。如果您不需要所有的 LaTeX,您可能会喜欢它。它应该可以在 Android 上正常工作。

It is just a suggestion, I have no idea what errors or advantages you might have if you use MathJax.

这只是一个建议,我不知道如果您使用 MathJax,您可能会遇到什么错误或优势。

MathJax is much bigger and more complicated than jqMath, and roughly 5 times slower.

MathJax 比 jqMath 大得多、复杂得多,大约慢 5 倍

Working JSFIDDLE: http://jsfiddle.net/sinhayash/gWWB5/1/

工作 JSFIDDLE:http: //jsfiddle.net/sinhayash/gWWB5/1/

For \sqrt{b^2-4ac}you can have this code: √(b^2-4ac)

因为\sqrt{b^2-4ac}你可以有这个代码:√(b^2-4ac)

You can easily use string handling in javascript to convert strings to the jqMath format and easily display it

您可以轻松地使用javascript中的字符串处理将字符串转换为jqMath格式并轻松显示