Показать красивую математическую формулу в Android

Грустно видеть, что науке и математике никогда не уделяется достаточно внимания на платформе Android. Может быть, это простая проблема, но я полный чувак в javascript, поэтому я изо всех сил пытаюсь понять, как решить эту проблему.

Я хочу сделать очень просто: мне просто нужно использовать математическое уравнение рендеринга. Это может быть MathJax или JqMath или что-то маленькое и быстрое. Если мне нужно сделать это в веб-просмотре, как я могу отобразить простой текст в абзаце и форматированное уравнение в одном и том же веб-просмотре?

(кто-нибудь предлагает другие методы, которые, очевидно, работают, я также рассмотрю это как решение)

ЧТО Я СДЕЛАЛ ДО СИХ ПОР:

Я начал с использования MathJax, чтобы поместить формулу в веб-просмотр (Я не знаю, есть ли какой-либо успешный пример использования jqMath? Кто-нибудь хочет поделиться своим опытом?). Я могу воспроизвести все функции так же, как это из автономного приложения MathJax. В этом приложении пользователь вводит строку в поле EditText, и MathJax отображает формулу в Latex, как только пользователь нажимает кнопку для подтверждения.

Мне не нужно, чтобы пользователь подтверждал. Я думал, что это должно быть проще, потому что нет взаимодействия с пользователем, но почему-то уравнение никогда не отображается. Я знаю, что должен что-то упустить, потому что коды в автономном MathJax предназначены для ввода пользователем. Какую часть кода мне следует изменить, чтобы напрямую отображать уравнение из строки, скажем, \sqrt{b^2-4ac}? Вот начальный заголовок для веб-просмотра:

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","");

А вот как исходное веб-представление загружается, когда пользователь нажимает кнопку после ввода текста редактирования:

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;
}

Я попытался заменить жестко запрограммированной строкой

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

но это не работает, текст "sample string" даже не отображается в веб-просмотре.

[РЕШЕНИЕ] см. ответ Джо

Чтобы уточнить его ответ, это пример того, как установить предложение в виде простого текста, а затем отобразить отформатированное уравнение в форме отображения (все в одном веб-просмотре):

Замените последнюю строку выше на

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

с участием

+"></script><span id='text'>This is plain text.</span> <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("\\sqrt{b^2-4ac}") + "\\\\]';");
        w.loadUrl("javascript:MathJax.Hub.Queue(['Typeset',MathJax.Hub]);");
    }
});

Это установит строку Это обычный текст обычным шрифтом и по центру отобразит формулу foo+bar в веб-просмотре.

ИЗМЕНИТЬ (выпуск Android 4.4)

Начиная с 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);
}

ОБНОВЛЕНИЕ (НАЧИНАЯ С MATHJAX 2.5)

Поскольку есть изменения в папках, предыдущий код не будет работать в последней версии MathJax. Кроме того, рекомендуемый способ минимизировать размер локального MathJax теперь — использовать Grunt. Шаблон для уменьшения размера 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);
            }
        }
    });
}

В отличие от ответа Джоса, нет необходимости разделять текстовые или математические типы, MathJax просто отформатирует их соответствующим образом.

Однако, похоже, существует ошибка, из-за которой веб-просмотр на устройствах KitKat не может отображать заглавную букву A. Любой, кто знает способ, может предложить обходной путь для этого.


person Community    schedule 10.06.2013    source источник
comment
Это не работает на андроиде 4.4. У вас есть какое-нибудь решение для этого?   -  person Shakti Malik    schedule 14.12.2013
comment
См. новое редактирование в сообщении.   -  person Neoh    schedule 14.04.2014
comment
можно ли отредактировать полученный результат следующим образом: mathquill.com? @Неох   -  person HinoHara    schedule 13.09.2014


Ответы (6)


Если я правильно понимаю вашу проблему, похоже, проблема связана с тем, что библиотека MathJax не завершила загрузку (из вызова loadDataWithBaseURL()), когда вы вызываете дополнительные loadUrl() для отображения формулы. Самое простое решение — дождаться обратного вызова onPageFinished(), чтобы сделать вызов.

Например, следующий код отлично работает на моем:

/** 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]);");
        }
    });
}

Обновление. Чтобы разместить дополнительный вывод в WebView, я бы предложил добавить элементы HTML к начальному вызову loadDataWithBaseURL(). Итак, для приведенного выше примера вместо этой строки в конце:

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

Мы можем сделать что-то вроде следующего:

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

Кроме того, если вам нужно впоследствии обновить эту часть в интерактивном режиме, вы можете использовать тот же механизм, который мы использовали для «математической» части, например:

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

Вы также можете использовать другие элементы HTML (вместо <span>, которые мы использовали выше), если хотите иметь определенный стиль/форматирование.

person Joe    schedule 14.06.2013
comment
Это одно из возможных решений. Я попробую это и отчитаюсь. - person Neoh; 17.06.2013
comment
Это показывает. И последний вопрос: как поместить обычный текст в тот же веб-просмотр? Например, предложение шрифтом по умолчанию, за которым следует форматированное уравнение (встроенное или отображаемое)? - person Neoh; 18.06.2013
comment
Рад помочь :) Я обновил свой ответ, добавив несколько предложений о том, как добавить вывод текста. - person Joe; 18.06.2013
comment
После того, как я установил обычный текст, w.loadUrl() действительно перезаписывает текст и в результате показывает только формулу. Я немного не понимаю, как добавить текст до или после формулы? Спасибо! - person Neoh; 18.06.2013
comment
У меня неправильное представление о том, как работает тег ‹span›. Теперь я могу сделать простой текст и формулу для отображения в веб-просмотре. Спасибо за вашу помощь. - person Neoh; 18.06.2013
comment
Спасибо, это отличное решение, и оно работает намного ниже Android 4.4. После Android 4.4 я получаю сообщение об ошибке Uncaught ReferenceError: MathJax не определен, источник: bar (1). См. Вопрос stackoverflow.com/questions/20582282/ - person Shakti Malik; 14.12.2013

Вместо того, чтобы использовать javascript и библиотеки javascript для рендеринга клиентской части LaTeX, почему бы не сделать следующее?

  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.

Это освобождает вас от попыток заставить все работать на стороне клиента с помощью javascript. И я бы сказал (хотя некоторые могут не согласиться), что это намного чище. Все, что вы отправляете клиенту, это:

  1. тег HTML <img/>
  2. фактическое содержимое изображения, когда браузер разрешает атрибут src

Очень легкий и быстрый!

person Timothy Shields    schedule 14.06.2013
comment
Ах, я забыл упомянуть, что мне нужно автономное решение. Я использую webview только потому, что этого требует mathjax. Спасибо за ваше предложение. - person Neoh; 15.06.2013
comment
@Neoh О, ладно - возможно, я должен был догадаться об этом из вопроса. :) - person Timothy Shields; 15.06.2013

Проблема «заглавной буквы А» — известная ошибка MathJax в Android WebView. Вы можете заменить файлы шрифтов измененными файлами шрифтов< /а>. Исправление будет работать при использовании вывода HTML-CSS со шрифтами TeX. Помимо MathJax, вы можете попробовать KaTeX, менее красивый, но более быстрый.

На самом деле я упаковал их обоих в пользовательское представление Android, взгляните на MathView.

person Community    schedule 02.12.2015
comment
Спасибо @kexanie за библиотеку. Это действительно помогло в рендеринге латексного кода. Я столкнулся с проблемой, когда размер MathView не изменяется (я понимаю, что это неотъемлемая проблема с веб-просмотром из-за этой проблемы code.google.com/p/android/issues/detail?id=18726 ). Есть ли какой-либо обходной путь, который вы предлагаете для этого? - person RmK; 26.01.2016

Опаздываю на вечеринку, но думаю, у меня есть решение получше, чем у Джо. Первоначальный вопрос задавался:

как я могу показать простой текст в абзаце и форматированное уравнение в одном и том же веб-просмотре?

В исходном примере у нас было:

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

Вы заметили \\\\[ и \\\\]? Эти четыре обратных слэша интерпретируются LaTeX как один, а \[ и \] в LaTeX является сокращением for \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}. \\]"
    )+"';");

WebView становится: введите здесь описание изображенияПримечания:

  • Нам нужны только две обратные косые черты вместо четырех, потому что мы находимся внутри doubleEscapeTeX(), а эта функция дублирует обратную косую черту.

  • Также текст за пределами математической среды отображается с помощью HTML (внутри тега <span id='math'>).

  • Шрифт LaTeX больше, чем шрифт html, поэтому добавление \\small в каждую математическую среду сделает их примерно одинакового размера. Здесь я добавил команду \\small только к первому уравнению, чтобы показать разницу.

person Community    schedule 13.04.2014

jqMath — это библиотека JavaScript, похожая на MathJax, но намного меньше, проще и быстрее. Если вам не нужен весь LaTeX, она может вам понравиться. Он должен нормально работать на Android.

Это просто предложение, я понятия не имею, какие ошибки или преимущества вы можете получить, если будете использовать MathJax.

MathJax намного больше и сложнее, чем jqMath, и примерно в 5 раз медленнее.

Рабочий JSFIDDLE: http://jsfiddle.net/sinhayash/gWWB5/1/

Для \sqrt{b^2-4ac} вы можете получить этот код: √(b^2-4ac)

Вы можете легко использовать обработку строк в javascript для преобразования строк в формат jqMath и легко отображать его.

person sinhayash    schedule 17.06.2013
comment
Некоторое время назад я пытался использовать Jqmath, но не смог заставить его работать. Как я уже сказал, у меня нет знаний о javascript, и то, что для вас звучит как кусок пирога, на самом деле является барьером, который такие новички, как мы, не могут преодолеть. В частности, я никогда не видел пример кода jqmath, который используется в Android. Вот почему я не могу принять это как ответ. - person Neoh; 17.06.2013

Вы можете легко использовать математическую формулу в 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", "");
}
person Community    schedule 02.07.2016
comment
Примечание из будущего: срок службы cdn.mathjax.org подходит к концу, проверьте mathjax .org/cdn-shutting-down для советов по миграции. - person Peter Krautzberger; 12.04.2017