Table of Contents
There are many ways to embed math formulas into a web page.
MathML is a W3C Recommendation [1], so you might guess it is the best choice, but it is not, because it is not supported well by the major web browsers [2] (see [3]).
If you want to use MathML, you still have a decision to make: do you want to write the MathML code by yourself?
If you decide to write your formulas in MathML directly, it's OK. However, you don't have to write it if you don't want to. Instead, you can write in \(\rm \LaTeX\) and convert it into MathML using tools such as itex2MML [4].
[1] | http://www.w3.org/Math/ |
[2] | http://en.wikipedia.org/wiki/MathML#Web_browsers |
[3] | http://www.w3.org/Math/testsuite/ |
[4] | http://golem.ph.utexas.edu/~distler/blog/itex2MML.html |
The fact that HTML is not \(\rm \LaTeX\)-friendly does not mean that web browsers are not \(\rm \LaTeX\)-friendly, because browsers are powered by JavaScript and CSS. JavaScript can parse \(\rm \LaTeX\) codes and render them with the help of CSS, which can display really fancy math in a browser. MathJax [5] is such a tool. All you have to do is to insert a line in your HTML:
<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> </script>
Then you can write Math in \(\rm \LaTeX\) now:
\[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } } \]
This will be rendered as
More demos can be found on the official MathJax website [6].
Please note that MathJax can convert \(\rm \LaTeX\) into MathML too, so choosing MathJax means you get the flexibility to switch between CSS and MathML.
In fact MathJax is not the only JS+CSS solution. There are other similar tools, such as jsMath [7] [8] and jqMath [9].
jsMath seems the predecessor of MathJax (from Wikipedia [10]):
The MathJax project started in 2009 as the successor to an earlier JavaScript mathematics formatting library, jsMath, and is managed by Design Science. The project is sponsored by the American Mathematical Society, Design Science, and the Society for Industrial and Applied Mathematics and is supported by the American Physical Society, Elsevier, and Project Euclid.
MathJax is used by web sites including MathSciNet, GitHub, n-category cafe, Math Overflow, Project Euclid journals, and the All-Russian Mathematical Portal.
[5] | http://www.mathjax.org |
[6] | http://www.mathjax.org/demos/tex-samples/ |
[7] | http://www.math.union.edu/~dpvc/jsmath/ |
[8] | http://en.wikipedia.org/wiki/JsMath |
[9] | http://mathscribe.com/author/jqmath.html |
[10] | http://en.wikipedia.org/wiki/Mathjax |
Almost all browsers support images, so if you don't trust anything (like MathML, MathJax, etc.), use images.
There are many tools that can convert \(\rm \LaTeX\) into images, such as Mathhack [11], LatexFormulaMacro [12], django_mathlatex [13] (if you use Django), etc.
[11] | http://docutils.sourceforge.net/sandbox/cben/rolehack/ |
[12] | http://trac-hacks.org/wiki/LatexFormulaMacro |
[13] | https://github.com/emesik/django_mathlatex |
option | web browser support | look the same in browsers? | usage | users |
---|---|---|---|---|
MathML | poor | no | embed MathML into HTML | currently no website really uses it |
Image | perfect | yes | make images and embed them in HTML |
|
MathJax (JS+CSS) | very good | almost yes | embed \(\rm \LaTeX\) in HTML |
[14] | (1, 2) http://math.stackexchange.com/ |
[15] | (1, 2) http://cdsweb.cern.ch/ |
[16] | http://cdsweb.cern.ch/record/1330928 |
[17] | (1, 2) http://annals.math.princeton.edu/ |
[18] | http://www.mathjax.org/community/mathjax-in-use/ |
So far MathML is obviously not a good choice. Between JS+CSS and image, I choose the former, because I want the formulas to be rendered at runtime. If I use image, I would have to convert the math into images, store it in some place and embed them in HTML, which would be less convenient.
MathJax and jsMath are very similar. Both MathJax and jsMath are used by many websites [19], but I prefer MathJax because it's newer and is supported by many famous commercial and academic sites such as [14], [15], [17].
jqMath is another option. It is said that it is much faster than MathJax [20], but I didn't try it. It seems fewer sites are using it. Maybe I should give it a try later.
[19] | http://www.math.union.edu/~dpvc/jsmath/gallery.html |
[20] | http://mathscribe.com/author/jqmath-mathjax-perf.html |
No hacking is needed to use MathJax in reStructuredText. We can just use the raw directive:
math.rst:: .. role:: raw-latex(raw) :format: latex html .. raw:: html <script type="text/javascript" src="http://localhost/mathjax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script> This: :raw-latex:`\((x+a)^3\)` this: :raw-latex:`\(W \approx \sum{f(x_k) \Delta x}\)` this: :raw-latex:`\(W = \int_{a}^{b}{f(x) dx}\)` and this: .. raw:: latex html \[ \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } } \] When :raw-latex:`\(a \ne 0\)`, there are two solutions to :raw-latex:`\(ax^2 + bx + c = 0\)` and they are :raw-latex:`\(x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\)`
You will get this:
This: \((x+a)^3\)
this: \(W \approx \sum{f(x_k) \Delta x}\)
this: \(W = \int_{a}^{b}{f(x) dx}\)
and this:
\[\frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } }\]When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\) and they are \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\)
I mentioned in my home page that all articles in this site are stored as reStructuredText format. Some of the articles contains math formula. Here's what I did in my Django project.
We will use some custom stuff, so let's create a custom module:
$ mkdir rsthack $ cd rsthack $ touch __init__.py $ ln -s /path/to/rsthack /path/to/a/module/search/path/
The code for new roles and new directive will be put in the __init__.py.
mil stands for math inline.
class_name = 'mathjaxlatex' # appears in <span class="mathjaxlatex"> and <div class="mathjaxlatex"> def MRole(role, rawtext, text, lineno, inliner, options={}, content=[]): t = re.sub(':%s:' % role, '', rawtext) assert t[:1] == '`' and t[-1:] == '`' t = r'\(%s\)' % t[1:-1] options={'format': 'html', 'classes': [class_name]} node = nodes.raw(rawtext, t, **options) return [node], [] roles.register_canonical_role('mil', MRole)
Then we can use :mil:`x = {-b \pm \sqrt{b^2-4ac} \over 2a}` to get \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}\).
The code for role eq is simple, but the code for directive math is a little more complicated. Most lines below are cloned from Sphinx source.
###################################################################### # role eq ###################################################################### # see: /usr/share/pyshared/sphinx/ext/mathbase.py class eqref(nodes.Inline, nodes.TextElement): pass def html_visit_eqref(self, node): self.body.append('<a href="#equation-%s">' % node['target']) def html_depart_eqref(self, node): self.body.append('</a>') def eq_role(role, rawtext, text, lineno, inliner, options={}, content=[]): text = utils.unescape(text) node = eqref('(?)', '(?)', target=text) return [node], [] roles.register_canonical_role('eq', eq_role) ###################################################################### # directive math ###################################################################### # see: # /usr/share/pyshared/sphinx/application.py # /usr/share/pyshared/sphinx/ext/mathbase.py # /usr/share/pyshared/sphinx/ext/jsmath.py # /usr/share/doc/python-sphinx/html/ext/math.html def massage_equations(doctree): num = 0 numbers = {} # generate the "label->number" map from directive ``math`` for node in doctree.traverse(displaymath): if node['number'] is None: continue numbers[node['label']] = node['number'] # set eqno in references for node in doctree.traverse(eqref): if node['target'] not in numbers: continue num = '(%d)' % numbers[node['target']] node[0] = nodes.Text(num, num) def html_visit_displaymath(self, node): if node['nowrap']: self.body.append(self.starttag(node, 'div', CLASS=class_name)) self.body.append(node['latex']) self.body.append('</div>') raise nodes.SkipNode for i, part in enumerate(node['latex'].split('\n\n')): part = self.encode(part) if i == 0: # necessary to e.g. set the id property correctly if node['number'] is not None: self.body.append('<span class="eqno">(%s)</span>' % node['number']) self.body.append(self.starttag(node, 'div', CLASS=class_name)) else: # but only once! self.body.append('<div class="%s">' % class_name) if '&' in part or '\\\\' in part: self.body.append('\\[\\begin{align}\n' + part + '\n\\end{align}\\]') else: self.body.append('\\[' + part + '\\]') self.body.append('</div>\n') raise nodes.SkipNode def add_node(node, **kwds): nodes._add_node_class_names([node.__name__]) for key, val in kwds.iteritems(): try: visit, depart = val except ValueError: raise ExtensionError('Value for key %r must be a ' '(visit, depart) function tuple' % key) assert key == 'html', 'accept html only' setattr(translator, 'visit_'+node.__name__, visit) if depart: setattr(translator, 'depart_'+node.__name__, depart) class displaymath(nodes.Part, nodes.Element): pass # custom directive ``math`` class MathDisplay(Directive): has_content = True required_arguments = 0 optional_arguments = 1 final_argument_whitespace = True option_spec = { 'label': directives.unchanged, 'nowrap': directives.flag, 'number': directives.nonnegative_int, } def run(self): latex = '\n'.join(self.content) if self.arguments and self.arguments[0]: latex = self.arguments[0] + '\n\n' + latex node = displaymath() node['latex'] = latex node['number'] = self.options.get('number', None) node['label'] = self.options.get('label', node['number']) node['nowrap'] = 'nowrap' in self.options ret = [node] if node['label']: tnode = nodes.target('', '', ids=['equation-' + node['label']]) self.state.document.note_explicit_target(tnode) ret.insert(0, tnode) return ret add_node(eqref, html=(html_visit_eqref, html_depart_eqref)) add_node(displaymath, html=(html_visit_displaymath, None)) directives.register_directive('math', MathDisplay)
The code above are in our custom module rsthack, so everytime we import rsthack, the code will run and we are ready to use the new roles, new directive and stuff.
One trick in the following filter is that we call rsthack.massage_equations to set the correct equation numbers in references.
############################## # filter enhancedrst ############################## from django.conf import settings from django.utils.encoding import smart_str, force_unicode from django.utils.safestring import mark_safe import rsthack # run rsthack/__init__.py def enhancedrst(value): try: from docutils import io from docutils.core import publish_doctree, Publisher from docutils.readers.doctree import Reader except ImportError: if settings.DEBUG: raise template.TemplateSyntaxError("Error in {% restructuredtext %} filter: The Python docutils library isn't installed.") return force_unicode(value) else: docutils_settings = getattr(settings, "RESTRUCTUREDTEXT_FILTER_SETTINGS", {}) # generate the doctree document = publish_doctree(source=smart_str(value), source_class=io.StringInput) # connect eqno and the reference rsthack.massage_equations(document) # apply the massaged doctree # (this is almost the same as docutils.core.publish_from_doctree) pub = Publisher(Reader(parser_name='null'), None, None, source=io.DocTreeInput(document), destination_class=io.StringOutput) pub.set_writer('html4css1') pub.process_programmatic_settings(None, docutils_settings, None) pub.set_destination(None, None) pub.publish() parts = pub.writer.parts return mark_safe(force_unicode(parts["fragment"])) enhancedrst.is_safe = True register.filter(enhancedrst)
A Django template sample:
{% load rst %} <link rel="stylesheet" type="text/css" href="{{ mediaurl }}css/rst.css" /> <script type="text/javascript" src="{{ mathjax }}"></script> {% for art in articles %} <blockquote> {{ art.content|enhancedrst }} </blockquote> {% endfor %}
in which
the filter source is part of file rst.py, so we use load rst to load it
span.eqno {
float: right;
}
the URL mathjax can be either the MathJax CDN or your own MathJax URL [21]
art.content should be in reStructuredText format
[21] | http://www.mathjax.org/docs/1.1/configuration.html |
Now everything's ready. Let's write math in our article:
When :mil:`a \ne 0`, there are two solutions to :mil:`ax^2 + bx + c = 0` and they are :mil:`x = {-b \pm \sqrt{b^2-4ac} \over 2a}.` Some *big* math: .. math:: :number: 123 :label: blah \frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } } Did you know equation :eq:`blah`?
We will get:
When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\) and they are \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\)
Some big math:
(123)\[\frac{1}{\Bigl(\sqrt{\phi \sqrt{5}}-\phi\Bigr) e^{\frac25 \pi}} = 1+\frac{e^{-2\pi}} {1+\frac{e^{-4\pi}} {1+\frac{e^{-6\pi}} {1+\frac{e^{-8\pi}} {1+\ldots} } } }\]Did you know equation (123)?
Most of the code above is available in Google Code. You can either read online or check it out from the repository.