summaryrefslogtreecommitdiff
path: root/docutils
diff options
context:
space:
mode:
authormilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2021-06-17 09:58:31 +0000
committermilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2021-06-17 09:58:31 +0000
commit5ffda575a88b108830a781de64e9bce8b69d95e9 (patch)
tree85645d939ffacb793989e1908854481ea3e9a242 /docutils
parent0c0aee2cff28cb9b820e45f8f1e0853b4e666ac6 (diff)
downloaddocutils-5ffda575a88b108830a781de64e9bce8b69d95e9.tar.gz
MathML: refactor and extend.
Only wrap displayed math in <mtable> if there are multiple rows. Fix alignment and spacing in multi-line equations. Fix size commands for extensible delimiters. Add support for commands \displaylimits, \mspace, \intop, \ointop. math.get(): new method, return attribute or optional default. tex_token(): don't skip leading whitespace, don't return groups tex_group(): new function -- return first group or token and remainder. tex_group_or_token(): new function git-svn-id: https://svn.code.sf.net/p/docutils/code/trunk@8760 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
Diffstat (limited to 'docutils')
-rw-r--r--docutils/docs/ref/rst/mathematics.txt238
-rw-r--r--docutils/docutils/utils/math/__init__.py12
-rw-r--r--docutils/docutils/utils/math/latex2mathml.py303
-rw-r--r--docutils/docutils/utils/math/tex2unichar.py6
-rw-r--r--docutils/test/functional/expected/math_output_mathml.html512
5 files changed, 620 insertions, 451 deletions
diff --git a/docutils/docs/ref/rst/mathematics.txt b/docutils/docs/ref/rst/mathematics.txt
index 715250826..ab4cadd20 100644
--- a/docutils/docs/ref/rst/mathematics.txt
+++ b/docutils/docs/ref/rst/mathematics.txt
@@ -6,34 +6,16 @@ LaTeX syntax for mathematics
.. default-role:: math
.. |latex| replace:: L\ :sup:`A`\ T\ :sub:`E`\ X
+:abstract: Docutils supports mathematical content with a `"math"
+ directive`__ and `role`__. The input format is *LaTeX math
+ syntax*\ [#math-syntax]_ with support for Unicode symbols.
+
.. sectnum::
.. contents::
-Introduction
-============
-
-Since version 0.8, Docutils supports mathematical content with a `"math"
-directive`__ and `role`__.
-The input format is *LaTeX math syntax*\ [#math-syntax]_ with support for
-Unicode symbols.
-
-
-.. [#math-syntax] The supported LaTeX commands include AMS extensions
- (see, e.g., the `Short Math Guide`_).
-
- The support is limited to a subset of *LaTeX math* by the conversion
- required for many output formats. For HTML, the `math_output`_
- configuration setting (or the corresponding ``--math-output`` command
- line option) selects between alternative output formats with different
- subsets of supported elements. If a writer does not support math
- typesetting, the content is inserted verbatim.
-
__ https://docutils.sourceforge.io/docs/ref/rst/directives.html#math
__ https://docutils.sourceforge.io/docs/ref/rst/roles.html#math
-.. _Short Math Guide:
- http://mirrors.ctan.org/info/short-math-guide/short-math-guide.pdf
-.. _math_output:
- https://docutils.sourceforge.io/docs/user/config.html#math-output
+
Inline formulas and displayed equations
=======================================
@@ -89,18 +71,34 @@ Displayed equations can use ``\\`` and ``&`` for line shifts and alignments::
.. math::
- a & = (x + y)^2 \\
- & = x^2 + 2xy + y^2
+ a &= (x + y)^2 &\qquad b &= (x - y)^2 \\
+ &= x^2 + 2xy + y^2 & &= x^2 - 2xy + y^2
LaTeX output will wrap it in an ``align*`` environment.
The result is:
.. math::
- a & = (x + y)^2 \\
- & = x^2 + 2xy + y^2
+ a &= (x + y)^2 & b &= (x - y)^2 \\
+ &= x^2 + 2xy + y^2 & &= x^2 - 2xy + y^2
+
+
+.. [#math-syntax] The supported LaTeX commands include AMS extensions
+ (see, e.g., the `Short Math Guide`_).
+
+ The support is limited to a subset of *LaTeX math* by the conversion
+ required for many output formats. For HTML, the `math_output`_
+ configuration setting (or the corresponding ``--math-output`` command
+ line option) selects between alternative output formats with different
+ subsets of supported elements. If a writer does not support math
+ typesetting, the content is inserted verbatim.
-.. _hyperlink references: ../ref/rst/restructuredtext.html#hyperlink-references
+.. _hyperlink references:
+ ../ref/rst/restructuredtext.html#hyperlink-references
+.. _Short Math Guide:
+ http://mirrors.ctan.org/info/short-math-guide/short-math-guide.pdf
+.. _math_output:
+ https://docutils.sourceforge.io/docs/user/config.html#math-output
Mathematical symbols
@@ -183,6 +181,8 @@ Use ``.`` for "empty" delimiters:
.. math:: A = \left . \frac{1}{1-n}\, \right |_{n=0}^\infty
+See also the commands for fixed `delimiter sizes`_ below.
+
The following symbols extend when used with ``\left`` and ``\right``:
Pairing delimiters
@@ -214,6 +214,8 @@ incorrect spacing, e.g., ``|k|=|-k|`` produces `|k| = |−k|` and
`\lvert -k\rvert` and `\lvert\sin(x)\rvert`, prevent this problem
(in LaTeX and MathJax).
+.. TODO: fix spacing before unary minus (see also cases example below).
+
Vertical Arrows
~~~~~~~~~~~~~~~
.. class:: colwidths-auto
@@ -261,7 +263,7 @@ Greek letters
-------------
In LaTeX, the default font for capital Greek letters is upright/roman.
-Italic capital Greek letters can be obtained by loading a `package
+*Italic* capital Greek letters can be obtained by loading a `package
providing the "ISO" math style`__. They are used by default in MathML.
Greek letters that have Latin look-alikes are rarely used in math
@@ -289,15 +291,14 @@ Letterlike symbols
------------------
.. class:: colwidths-auto
-========== ============ ============= =============== ========= =========== =========== =============
-`\aleph` ``\aleph`` `\forall` ``\forall`` `\hbar` ``\hbar`` `\ell` ``\ell``
-`\beth` ``\beth`` `\complement` ``\complement`` `\hslash` ``\hslash`` `\wp` ``\wp``
-`\gimel` ``\gimel`` `\exists` ``\exists`` `\Im` ``\Im`` `\Re` ``\Re``
-`\daleth` ``\daleth`` `\Finv` ``\Finv`` `\imath` ``\imath`` `\circledR` ``\circledR``
-`\partial` ``\partial`` `\Game` ``\Game`` `\Bbbk` ``\Bbbk`` `\circledS` ``\circledS``
-`\eth` ``\eth`` `\mho` ``\mho``
-========== ============ ============= =============== ========= =========== =========== =============
-
+ ============= =============== ========== ============ ========== ============ =========== =============
+ `\forall` ``\forall`` `\aleph` ``\aleph`` `\hbar` ``\hbar`` `\ell` ``\ell``
+ `\complement` ``\complement`` `\beth` ``\beth`` `\hslash` ``\hslash`` `\wp` ``\wp``
+ `\exists` ``\exists`` `\gimel` ``\gimel`` `\Im` ``\Im`` `\Re` ``\Re``
+ `\Finv` ``\Finv`` `\daleth` ``\daleth`` `\imath` ``\imath`` `\circledR` ``\circledR``
+ `\Game` ``\Game`` `\partial` ``\partial`` `\jmath` ``\jmath`` `\circledS` ``\circledS``
+ `\mho` ``\mho`` `\eth` ``\eth`` `\Bbbk` ``\Bbbk``
+ ============= =============== ========== ============ ========== ============ =========== =============
Mathematical Alphabets
----------------------
@@ -400,7 +401,6 @@ Punctuation
.. [#] Punctuation (not ratio):
Compare spacing in `a\colon b\to c` to `a:b = c`.
-
Relation symbols
----------------
@@ -577,12 +577,12 @@ The ``matrix`` and ``cases`` environments can also contain ``\\`` and
``&``::
.. math::
- \left(\begin{matrix} a & b \\ c & d \end{matrix}\right)
+ \left ( \begin{matrix} a & b \\ c & d \end{matrix}\right)
Result:
.. math::
- \left(\begin{matrix} a & b \\ c & d \end{matrix}\right)
+ \left ( \begin{matrix} a & b \\ c & d \end{matrix} \right)
The environments ``pmatrix``, ``bmatrix``, ``Bmatrix``, ``vmatrix``, and
``Vmatrix`` have (respectively) ( ), [ ], { }, \| \|, and `\Vert\ \Vert`
@@ -602,8 +602,8 @@ matrix.
For piecewise function definitions there is a ``cases`` environment:
.. math:: \mathrm{sgn}(x) = \begin{cases}
- -1 & x<0\\
- 1 & x>0
+ \, -1 & x<0\\
+ \,\phantom{-}1 & x>0
\end{cases}
Spacing commands
@@ -614,22 +614,25 @@ commands:
.. class:: colwidths-auto
- ===================== ======== =======================
- :m:`3\qquad8` ``3\qquad8`` (2em)
- :m:`3\quad8` ``3\quad8`` (1em)
- :m:`3~8` ``3~8`` ``3\nobreakspace8``
- :m:`3\ 8` ``3\ 8`` escaped space
- :m:`3\;8` ``3\;8`` ``3\thickspace8``
- :m:`3\:8` ``3\:8`` ``3\medspace8``
- :m:`3\,8` ``3\,8`` ``3\thinspace8``
- :m:`3 8` ``3 8`` regular space (ignored)
- :m:`3\!8` ``3\!8`` ``3\negthinspace8``
- :m:`3\negmedspace8` ``3\negmedspace8``
- :m:`3\negthickspace8` ``3\negthickspace8``
- `3\hspace{1ex}8` ``3\hspace{1ex}8``
- ===================== ======== =======================
-
-Whitespace is ignored in LaTeX math mode.
+ ====================== ======== ===================== ==================
+ :m:`3\qquad |` ``3\qquad |`` = 2em
+ :m:`3\quad |` ``3\quad |`` = 1em
+ :m:`3~|` ``3~|`` ``3\nobreakspace |``
+ :m:`3\ |` ``3\ |`` escaped space
+ :m:`3\;|` ``3\;|`` ``3\thickspace |``
+ :m:`3\:|` ``3\:|`` ``3\medspace |``
+ :m:`3\,|` ``3\,|`` ``3\thinspace |``
+ :m:`3 |` ``3 |`` regular space [#]_
+ :m:`3\!|` ``3\!|`` ``3\negthinspace |``
+ :m:`3\negmedspace |` ``3\negmedspace |``
+ :m:`3\negthickspace |` ``3\negthickspace |``
+ `3\hspace{1ex}|` ``3\hspace{1ex}|`` custom length
+ `3\mspace{20mu}|` ``3\mspace{20mu}|`` custom length [#]_
+ ====================== ======== ===================== ==================
+
+.. [#] Whitespace characters are ignored in LaTeX math mode.
+.. [#] Unit must be 'mu' (1 mu = 1/18em).
+
Negative spacing does not work with MathML (in Firefox 78).
There are also three commands that leave a space equal to the height and
@@ -638,8 +641,8 @@ wide and high as three X’s:
.. math:: \frac{\phantom{XXX}+1}{XXX-1}
-The commands ``\hphantom`` and ``\vphantom`` insert space with the heigth
-rsp. width of the argument. They are not supported with `math_output`_
+The commands ``\hphantom`` and ``\vphantom`` insert space with the
+width or height of the argument. They are not supported with `math_output`_
MathML.
Roots
@@ -668,7 +671,8 @@ Fractions and related constructions
===================================
The ``\frac`` command takes two ar guments, numerator and denominator,
-and typesets them in normal fraction form. Use ``\dfrac`` or ``\tfrac`` to
+and typesets them in normal fraction form. For example, ``U = \frac{R}{I}``
+produces `U = \frac{R}{I}`. Use ``\dfrac`` or ``\tfrac`` to
force text style and display style respectively.
.. math:: \frac{x+1}{x-1} \quad
@@ -689,15 +693,17 @@ prints
The ``\cfrac`` command for continued fractions uses displaystyle and
padding for sub-fractions:
-.. math:: \cfrac{1}{\sqrt{2}+
- \cfrac{1}{\sqrt{2}+
- \cfrac{1}{\sqrt{2}+\dotsb
- }}}
- \text{ vs. }
- \frac{1}{\sqrt{2}+
- \frac{1}{\sqrt{2}+
- \frac{1}{\sqrt{2}+\dotsb
- }}}
+.. math:: \frac{\pi}{4} = 1 + \cfrac{1^2}{
+ 2 + \cfrac{3^2}{
+ 2 + \cfrac{5^2}{
+ 2 + \cfrac{7^2}{2 + \cdots}
+ }}}
+ \qquad \text{vs.}\qquad
+ \frac{\pi}{4} = 1 + \frac{1^2}{
+ 2 + \frac{3^2}{
+ 2 + \frac{5^2}{
+ 2 + \frac{7^2}{2 + \cdots}
+ }}}
It supports the optional argument ``[l]`` or ``[r]`` for
left or right placement of the numerator:
@@ -775,11 +781,14 @@ Whitespace is kept inside the argument:
.. Math:: f_{[x_{i-1},x_i]} \text{ is monotonic for } i = 1,\,…,\,c+1
-``$`` signs can be used for math commands inside the text, e.g.
+The text may contain math commands wrapped in ``$`` signs, e.g.
+
+.. math:: (-1)^{n_i} = \begin{cases} -1 \quad \text{if $n_i$ is odd} \\
+ +1 \quad \text{if $n_i$ is even.}
+ \end{cases}
+
+.. TODO ignore {}, handle text-mode commands
-.. math:: (-1)^n = \begin{cases} -1 \quad \text{if $n$ is odd} \\
- +1 \quad \text{if $n$ is even.}
- \end{cases}
.. TODO: ``\mod`` and its relatives
--------------------------
@@ -806,8 +815,18 @@ similar symbols like
move to index postions: `\lim_{n\to\infty} \sum_1^n \frac{1}{n}`.
-The commands ``\limits`` and ``\nolimits`` override the automatic
-placement of the limits; ``\displaylimits`` means to use standard
+Altering the placement of limits
+--------------------------------
+
+The commands ``\intop`` and ``\ointop`` produce integral signs with
+limits as in sums and similar: `\intop_0^1`, `\ointop_c` and
+
+.. math:: \intop_0^1 \quad \ointop_c
+ \quad \text{vs.} \quad
+ \int^1_0 \quad \oint_c
+
+The commands ``\limits`` and ``\nolimits`` override the default placement
+of the limits for any operator; ``\displaylimits`` forces standard
positioning as for the \sum command. They should follow immediately after
the operator to which they apply.
@@ -859,11 +878,15 @@ yields
after the opening bracket.
-Appendix: Tests
-===============
+Appendix
+========
+
+Tests
+-----
+
Font changes
-------------
+~~~~~~~~~~~~
Math alphabet macros change the default alphabet ("mathvariant" in
MathML), leaving some symbols unchanged:
@@ -879,6 +902,7 @@ Unicode supports the following blackboard-bold characters:
`\mathbb{a \ldots z A \ldots Z 0 \ldots 9
\mathbb\Gamma \mathbb{\Pi} \mathbb {\Sigma} \mathbb\gamma \mathbb\pi}`.
+
Inferred <mrow>s in MathML
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -888,11 +912,11 @@ formed from all their children.
.. math:: a = \sqrt 2 + x, b = \sqrt{1+x^2}, c = \sqrt\frac{\sin(x)}{23},
-
inline: :math:`a = \sqrt 2 + x, b = \sqrt{1+x^2}, c = \sqrt\frac{\sin(x)}{23}`.
+
Scripts and Limits
-------------------
+~~~~~~~~~~~~~~~~~~
Accents should be nearer to the base (in MathML Firefox 78, it's vice versa!):
`\bar a \overline a, \bar l \overline l, \bar i \overline i`.
@@ -902,4 +926,52 @@ Sub- and superscript may be given in any order:
Double exponent: `x^{10^4}`, `r_{T_\mathrm{in}}` and `x_i^{n^2}`.
-Binary vs. unary minus operator: `a - b = -c`
+
+Nested groups
+~~~~~~~~~~~~~
+
+tex-token returns "{" for nested groups:
+
+.. math:: \text{das ist ein {toller} text (unescaped \{ and \} is
+ ignored by LaTeX)}
+
+Big delimiters
+~~~~~~~~~~~~~~
+
+Test ``\left``, ``\right``, and the \bigl/\bigr, … size commands
+with extensible delimiters.
+
+.. math::
+ \left.(b\right)\ \bigl(b\Bigr)\ \biggl(b\Biggr)
+ \quad
+ \left.[b\right]\ \bigl[b\Bigr]\ \biggl[b\Biggr]
+ \quad
+ \left.\{b\right \} \ \bigl\{b\Bigr \} \ \biggl\{b\Biggr \}
+ \quad
+ \left.\langle b\right\rangle\ \bigl\langle b\Bigr\rangle\ \biggl\langle b\Biggr\rangle
+ \quad
+ \left.\lceil b\right\rceil\ \bigl\lceil b\Bigr\rceil\ \biggl\lceil b\Biggr\rceil
+
+ \left.\lfloor b\right\rfloor\ \bigl\lfloor b\Bigr\rfloor\ \biggl\lfloor b\Biggr\rfloor
+ \quad
+ \left.\lgroup b\right\rgroup\ \bigl\lgroup b\Bigr\rgroup\ \biggl\lgroup b\Biggr\rgroup
+ \quad
+ \left.\lmoustache b\right\rmoustache\ \bigl\lmoustache b\Bigr\rmoustache\ \biggl\lmoustache b\Biggr\rmoustache
+ \quad
+ \left.\lvert b\right\rvert\ \bigl\lvert b\Bigr\rvert\ \biggl\lvert b\Biggr\rvert
+ \quad
+ \left.\lVert b\right\rVert\ \bigl\lVert b\Bigr\rVert\ \biggl\lVert b\Biggr\rVert
+
+ \left.|b\right\|\ \bigl|b\Bigr\|\ \biggl|b\Biggr\|
+ \quad
+ \left./b\right\backslash\ \bigl/b\Bigr\backslash\ \biggl/b\Biggr\backslash
+ \quad
+ \left.(b\right)\ \bigl(b\Bigr)\ \biggl(b\Biggr)
+ \quad
+ \left.\arrowvert b\right\Arrowvert\ \bigl\arrowvert b\Bigr\Arrowvert\ \biggl\arrowvert b\Biggr\Arrowvert
+
+ \left.\bracevert b\right\bracevert\ \bigl\bracevert b\Bigr\bracevert\ \biggl\bracevert b\Biggr\bracevert
+ \quad
+ \left.\vert \frac{b}{a}\right\Vert\ \bigl\vert b\Bigr\Vert\ \biggl\vert b\Biggr\Vert
+ \quad
+
diff --git a/docutils/docutils/utils/math/__init__.py b/docutils/docutils/utils/math/__init__.py
index 87e608dd7..3e18434b8 100644
--- a/docutils/docutils/utils/math/__init__.py
+++ b/docutils/docutils/utils/math/__init__.py
@@ -25,6 +25,12 @@ It contains various modules for conversion between different math formats
# helpers for Docutils math support
# =================================
+def toplevel_code(code):
+ """Return string (LaTeX math) `code` with environments stripped out."""
+ chunks = code.split(r'\begin{')
+ return r'\begin{'.join([chunk.split(r'\end{')[-1]
+ for chunk in chunks])
+
def pick_math_environment(code, numbered=False):
"""Return the right math environment to display `code`.
@@ -35,11 +41,7 @@ def pick_math_environment(code, numbered=False):
If `numbered` evaluates to ``False``, the "starred" versions are used
to suppress numbering.
"""
- # cut out environment content:
- chunks = code.split(r'\begin{')
- toplevel_code = ''.join([chunk.split(r'\end{')[-1]
- for chunk in chunks])
- if toplevel_code.find(r'\\') >= 0:
+ if toplevel_code(code).find(r'\\') >= 0:
env = 'align'
else:
env = 'equation'
diff --git a/docutils/docutils/utils/math/latex2mathml.py b/docutils/docutils/utils/math/latex2mathml.py
index eb46d7f05..1f7f69284 100644
--- a/docutils/docutils/utils/math/latex2mathml.py
+++ b/docutils/docutils/utils/math/latex2mathml.py
@@ -29,7 +29,7 @@ import sys
if sys.version_info >= (3, 0):
unicode = str # noqa
-from docutils.utils.math import tex2unichar
+from docutils.utils.math import tex2unichar, toplevel_code
# Character data
@@ -159,11 +159,10 @@ small_operators = {# mathsize='75%'
# Operators and functions with limits above/below in display formulas
# and in index position inline (movablelimits="true")
-displaylimits = [operators[name] for name in
- ('bigcap', 'bigcup', 'bigodot', 'bigoplus', 'bigotimes',
+displaylimits = ('bigcap', 'bigcup', 'bigodot', 'bigoplus', 'bigotimes',
'bigsqcup', 'biguplus', 'bigvee', 'bigwedge',
- 'coprod', 'prod', 'sum',
- 'lim', 'max', 'min', 'sup', 'inf')]
+ 'coprod', 'intop', 'ointop', 'prod', 'sum',
+ 'lim', 'max', 'min', 'sup', 'inf')
# Depending on settings, integrals may also be in this category.
# (e.g. if "amsmath" is loaded with option "intlimits", see
# http://mirror.ctan.org/macros/latex/required/amsmath/amsldoc.pdf)
@@ -171,8 +170,6 @@ displaylimits = [operators[name] for name in
# 'oint', 'ointctrclockwise', 'sqint',
# 'varointclockwise',))
-# >>> print(' '.join(displaylimits))
-# ⋂ ⋃ ⨀ ⨁ ⨂ ⨆ ⨄ ⋁ ⋀ ∐ ∏ ∑ lim max min sup inf
# pre-composed characters for negated symbols
# see https://www.w3.org/TR/xml-entity-names/#combining
@@ -188,6 +185,10 @@ stretchables = {'backslash': '\\',
'Uparrow': u'\u21d1', # ⇑ UPWARDS DOUBLE ARROW
'Downarrow': u'\u21d3', # ⇓ DOWNWARDS DOUBLE ARROW
'Updownarrow': u'\u21d5', # ⇕ UP DOWN DOUBLE ARROW
+ 'lmoustache': u'\u23b0', # ⎰ UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
+ 'rmoustache': u'\u23b1', # ⎱ UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
+ 'arrowvert': u'\u23d0', # ⏐ VERTICAL LINE EXTENSION
+ 'bracevert': u'\u23aa', # ⎪ CURLY BRACKET EXTENSION
}
stretchables.update(tex2unichar.mathfence)
stretchables.update(tex2unichar.mathopen)
@@ -195,8 +196,7 @@ stretchables.update(tex2unichar.mathclose)
stretchables.update(math_fences)
# >>> print(' '.join(sorted(set(stretchables.values()))))
-# [ \ ] { | } ‖ ↑ ↓ ↕ ⇑ ⇓ ⇕ ⌈ ⌉ ⌊ ⌋ ⌜ ⌝ ⌞ ⌟ ⟅ ⟆ ⟦ ⟧ ⟨ ⟩ ⟮ ⟯ ⦇ ⦈
-
+# [ \ ] { | } ‖ ↑ ↓ ↕ ⇑ ⇓ ⇕ ⌈ ⌉ ⌊ ⌋ ⌜ ⌝ ⌞ ⌟ ⎪ ⎰ ⎱ ⏐ ⟅ ⟆ ⟦ ⟧ ⟨ ⟩ ⟮ ⟯ ⦇ ⦈
# horizontal space -> <mspace>
@@ -216,7 +216,7 @@ spaces = {'qquad': '2em', # two \quad
'!': '-0.1667em', # negthinspace
}
-# accents -> <mover accent="true">
+# accents -> <mover accent="true"> # TODO: stretchy="false"
# TeX spacing combining
accents = {'acute': u'´', # u'\u0301',
'bar': u'ˉ', # u'\u0304',
@@ -225,6 +225,7 @@ accents = {'acute': u'´', # u'\u0301',
'dot': u'˙', # u'\u0307',
'ddot': u'¨', # u'\u0308',
'dddot': u'\u20DB', # '…' too wide
+ # TODO: ddddot
'grave': u'`', # u'\u0300',
'hat': u'ˆ', # u'\u0302',
'mathring': u'˚', # u'\u030A',
@@ -358,6 +359,8 @@ class math(object):
return self.attributes[key]
def __setitem__(self, key, item):
self.attributes[key] = item
+ def get(self, *args, **kwargs):
+ return self.attributes.get(*args, **kwargs)
def full(self):
"""Return boolean indicating whether children may be appended."""
@@ -627,47 +630,103 @@ def tex_number(string):
def tex_token(string):
"""Return first simple TeX token and remainder of `string`.
- >>> tex_token('{first simple group} returned without brackets')
- ('first simple group', ' returned without brackets')
>>> tex_token('\\command{without argument}')
('\\command', '{without argument}')
- >>> tex_token(' first non-white character')
- ('f', 'irst non-white character')
+ >>> tex_token('or first character')
+ ('o', 'r first character')
"""
- m = re.match(r"""\s* # leading whitespace
- {(?P<token>(\\}|[^{}]|\\{)*)} # {group} without nested groups
- (?P<remainder>.*$)
- """, string, re.VERBOSE)
- if m is None:
- m = re.match(r"""\s* # leading whitespace
- (?P<token>\\([a-zA-Z]+))\s* # \cmdname
- (?P<remainder>.*$)
- """, string, re.VERBOSE)
- if m is None:
- m = re.match(r"""\s* # leading whitespace
- (?P<token>.?) # first character or empty string
- (?P<remainder>.*$)
+ m = re.match(r"""((?P<cmd>\\[a-zA-Z]+)\s* # TeX command, skip whitespace
+ |(?P<chcmd>\\.) # one-character TeX command
+ |(?P<ch>.?)) # first character (or empty)
+ (?P<remainder>.*$) # remaining part of string
""", string, re.VERBOSE)
-
- return m.group('token'), m.group('remainder')
+ cmd, chcmd, ch, remainder = m.group('cmd', 'chcmd', 'ch', 'remainder')
+ return cmd or chcmd or ch, remainder
# Test:
#
-# >>> tex_token('{opening bracket of group with {nested group}}')
-# ('{', 'opening bracket of group with {nested group}}')
-# >>> tex_token('{group with \\{escaped\\} brackets}')
-# ('group with \\{escaped\\} brackets', '')
-# >>> tex_token('{group followed by closing bracket}} from outer group')
-# ('group followed by closing bracket', '} from outer group')
-# >>> tex_token(' {skip leading whitespace}')
-# ('skip leading whitespace', '')
-# >>> tex_token(' \\skip{leading whitespace}')
-# ('\\skip', '{leading whitespace}')
+# >>> tex_token('{opening bracket of group}')
+# ('{', 'opening bracket of group}')
# >>> tex_token('\\skip whitespace after macro name')
# ('\\skip', 'whitespace after macro name')
+# >>> tex_token('. but not after single char')
+# ('.', ' but not after single char')
# >>> tex_token('') # empty string.
# ('', '')
+# >>> tex_token('\{escaped bracket')
+# ('\\{', 'escaped bracket')
+
+def tex_group(string):
+ """Return first TeX group or token and remainder of `string`.
+
+ >>> tex_group('{first group} returned without brackets')
+ ('first group', ' returned without brackets')
+
+ """
+ split_index = 0
+ nest_level = 0 # level of {{nested} groups}
+ escape = False # the next character is escaped (\)
+
+ if not string.startswith('{'):
+ # special case: there is no group, return first token and remainder
+ return string[:1], string[1:]
+ for c in string:
+ split_index += 1
+ if escape:
+ escape = False
+ elif c == '\\':
+ escape = True
+ elif c == '{':
+ nest_level += 1
+ elif c == '}':
+ nest_level -= 1
+ if nest_level == 0:
+ break
+ else:
+ raise SyntaxError('Group without closing bracket')
+ return string[1:split_index-1], string[split_index:]
+
+
+
+# >>> tex_group('{} empty group')
+# ('', ' empty group')
+# >>> tex_group('{group with {nested} group} ')
+# ('group with {nested} group', ' ')
+# >>> tex_group('{group with {nested group}} at the end')
+# ('group with {nested group}', ' at the end')
+# >>> tex_group('{{group} {with {{complex }nesting}} constructs}')
+# ('{group} {with {{complex }nesting}} constructs', '')
+# >>> tex_group('{group with \\{escaped\\} brackets}')
+# ('group with \\{escaped\\} brackets', '')
+# >>> tex_group('{group followed by closing bracket}} from outer group')
+# ('group followed by closing bracket', '} from outer group')
+# >>> tex_group('No group? Return first character.')
+# ('N', 'o group? Return first character.')
+# >>> tex_group(' {also whitespace}')
+# (' ', '{also whitespace}')
+
+
+def tex_token_or_group(string):
+ """Return first TeX group or token and remainder of `string`.
+
+ >>> tex_token_or_group('\\command{without argument}')
+ ('\\command', '{without argument}')
+ >>> tex_token_or_group('first character')
+ ('f', 'irst character')
+ >>> tex_token_or_group(' also whitespace')
+ (' ', 'also whitespace')
+ >>> tex_token_or_group('{first group} keep rest')
+ ('first group', ' keep rest')
+
+ """
+ arg, remainder = tex_token(string)
+ if arg == '{':
+ arg, remainder = tex_group(string.lstrip())
+ return arg, remainder
+
+# >>> tex_token_or_group('\{no group but left bracket')
+# ('\\{', 'no group but left bracket')
def tex_optarg(string):
"""Return optional argument and remainder.
@@ -797,13 +856,13 @@ def handle_cmd(name, node, string):
# identifier -> <mi>
if name in letters:
+ new_node = mi(letters[name])
if name in greek_capitals:
# upright in "TeX style" but MathML sets them italic ("ISO style").
# CSS styling does not change the font style in Firefox 78.
# Use 'mathvariant="normal"'?
- node = node.append(mi(greek_capitals[name], CLASS='capital-greek'))
- else:
- node = node.append(mi(letters[name]))
+ new_node['class'] = 'capital-greek'
+ node = node.append(new_node)
return node, string
if (name in functions):
@@ -812,7 +871,7 @@ def handle_cmd(name, node, string):
if name == 'operatorname':
# custom function name, e.g. ``\operatorname{abs}(x)``
# TODO: \operatorname* -> with limits
- arg, string = tex_token(string)
+ arg, string = tex_token_or_group(string)
new_node = mi(arg, mathvariant='normal')
else:
new_node = mi(functions[name])
@@ -834,22 +893,22 @@ def handle_cmd(name, node, string):
return node, string
if name in math_alphabets:
- arg, remainder = tex_token(string)
- if arg[0] == '\\':
- # convert single letters (so the isalpha() test below works).
- arg = letters.get(arg[1:], arg)
- if name == 'mathbb':
- # mathvariant="double-struck" is ignored for Greek letters
- # (tested in Firefox 78). Use literal Unicode characters.
- arg = mathbb.get(arg, arg)
if name == 'boldsymbol':
attributes = {'style': 'font-weight: bold'}
else:
attributes = {'mathvariant': math_alphabets[name]}
if name == 'mathscr':
attributes['class'] = 'mathscr'
-
- # one symbol (single letter, name, or ⅀)
+ # Check for single symbol (letter, name, or ⅀)
+ arg, remainder = tex_token_or_group(string)
+ if arg.startswith('\\'):
+ # convert single letters (so the isalpha() test below works).
+ # TODO: convert all LICRs in a group (\matrm{\mu\Omega})
+ arg = letters.get(arg[1:], arg)
+ if name == 'mathbb':
+ # mathvariant="double-struck" is ignored for Greek letters
+ # (tested in Firefox 78). Use literal Unicode characters.
+ arg = mathbb.get(arg, arg)
if arg.isalpha() or arg == u'\u2140':
node = node.append(mi(arg, **attributes))
return node, remainder
@@ -874,21 +933,26 @@ def handle_cmd(name, node, string):
return node, string
if name in operators:
- node = node.append(mo(operators[name]))
+ if name in displaylimits and string and string[0] in ' _^':
+ node = node.append(mo(operators[name], movablelimits = 'true'))
+ else:
+ node = node.append(mo(operators[name]))
return node, string
if name in delimiter_sizes:
delimiter_attributes = {}
size = delimiter_sizes[name]
- delimiter, string = tex_token(string)
+ delimiter, string = tex_token_or_group(string)
if delimiter not in '()[]/|.':
try:
delimiter = stretchables[delimiter.lstrip('\\')]
except KeyError:
- raise SyntaxError(u'Missing "\\%s" delimiter!' % name)
+ raise SyntaxError(u'Unsupported "\\%s" delimiter "%s"!'
+ % (name, delimiter))
if size:
delimiter_attributes['maxsize'] = size
delimiter_attributes['minsize'] = size
+ delimiter_attributes['symmetric'] = 'true'
if name == 'left' or name.endswith('l'):
row = mrow()
node.append(row)
@@ -900,7 +964,7 @@ def handle_cmd(name, node, string):
return node, string
if name == 'not':
- arg, string = tex_token(string)
+ arg, string = tex_token_or_group(string)
try:
node = node.append(mo(negatables[arg]))
except KeyError:
@@ -909,7 +973,7 @@ def handle_cmd(name, node, string):
# arbitrary text (usually comments) -> <mtext>
if name in ('text', 'mbox', 'textrm'):
- arg, string = tex_token(string)
+ arg, string = tex_token_or_group(string)
parts = arg.split('$') # extract inline math
for i, part in enumerate(parts):
if i % 2 == 0: # i is even
@@ -924,8 +988,10 @@ def handle_cmd(name, node, string):
node = node.append(mspace(width='%s'%spaces[name]))
return node, string
- if name == 'hspace':
- arg, string = tex_token(string)
+ if name in ('hspace', 'mspace'):
+ arg, string = tex_group(string)
+ if arg.endswith('mu'):
+ arg = '%sem' % (float(arg[:-2])/18)
node = node.append(mspace(width='%s'%arg))
return node, string
@@ -1053,7 +1119,7 @@ def handle_cmd(name, node, string):
# >>> handle_cmd('left', math(), '[a\\right]')
# (mrow(mo('[')), 'a\\right]')
-# >>> handle_cmd('left', math(), '. a)') # emtpy \left
+# >>> handle_cmd('left', math(), '. a)') # empty \left
# (mrow(), ' a)')
# >>> handle_cmd('left', math(), '\\uparrow a)') # cmd
# (mrow(mo('↑')), 'a)')
@@ -1103,7 +1169,7 @@ def handle_script_or_limit(node, c, limits=''):
new_node = munderover(*child.children, switch=True)
elif (limits in ('limits', 'displaylimits')
or limits == ''
- and getattr(child, 'data', '') in displaylimits):
+ and child.get('movablelimits', None) == 'true'):
new_node = munder(child)
else:
new_node = msub(child)
@@ -1114,7 +1180,7 @@ def handle_script_or_limit(node, c, limits=''):
new_node = munderover(*child.children)
elif (limits in ('limits', 'displaylimits')
or limits == ''
- and getattr(child, 'data', '') in displaylimits):
+ and child.get('movablelimits', None) == 'true'):
new_node = mover(child)
else:
new_node = msup(child)
@@ -1123,7 +1189,7 @@ def handle_script_or_limit(node, c, limits=''):
def begin_environment(node, string):
- name, string = tex_token(string)
+ name, string = tex_group(string)
if name in matrices:
left_delimiter = matrices[name][0]
attributes = {}
@@ -1137,6 +1203,8 @@ def begin_environment(node, string):
wrapper = mstyle(scriptlevel=1)
node.append(wrapper)
node = wrapper
+ # TODO: aligned, alignedat
+ # take an optional [t], [b] or the default [c]
entry = mtd()
node.append(mtable(mtr(entry), **attributes))
node = entry
@@ -1146,7 +1214,7 @@ def begin_environment(node, string):
def end_environment(node, string):
- name, string = tex_token(string)
+ name, string = tex_group(string)
if name in matrices:
node = node.close().close().close() # close: mtd, mdr, mtable
right_delimiter = matrices[name][1]
@@ -1160,23 +1228,64 @@ def end_environment(node, string):
return node, string
+# Return the number of "equation_columns" in `code_lines`. See "alignat"
+# in http://mirror.ctan.org/macros/latex/required/amsmath/amsldoc.pdf
+def tex_equation_columns(rows):
+ tabs = max(row.count('&') - row.count(r'\&') for row in rows)
+ if tabs == 0:
+ return 0
+ return int(tabs/2 + 1)
+
+# >>> tex_equation_columns(['a = b'])
+# 0
+# >>> tex_equation_columns(['a &= b'])
+# 1
+# >>> tex_equation_columns(['a &= b & a \in S'])
+# 2
+# >>> tex_equation_columns(['a &= b & c &= d'])
+# 2
+
+
+# Return dictionary with attributes to style an <mtable> as align environment:
+def align_attributes(rows):
+ atts = {'class': 'alignat',
+ 'displaystyle': 'true'}
+ tabs = max(row.count('&') - row.count(r'\&') for row in rows)
+ if tabs:
+ aligns = ['right', 'left'] * tabs
+ spacing = ['0', '2em'] * tabs
+ atts['columnalign'] = ' '.join(aligns[:tabs+1])
+ atts['columnspacing'] = ' '.join(spacing[:tabs])
+ return atts
+
+# >>> align_attributes(['a = b'])
+# {'class': 'alignat', 'displaystyle': 'true'}
+# >>> align_attributes(['a &= b'])
+# {'class': 'alignat', 'displaystyle': 'true', 'columnalign': 'right left', 'columnspacing': '0'}
+# >>> align_attributes(['a &= b & a \in S'])
+# {'class': 'alignat', 'displaystyle': 'true', 'columnalign': 'right left right', 'columnspacing': '0 2em'}
+# >>> align_attributes(['a &= b & c &= d'])
+# {'class': 'alignat', 'displaystyle': 'true', 'columnalign': 'right left right left', 'columnspacing': '0 2em 0'}
+
+
def tex2mathml(tex_math, inline=True):
"""Return string with MathML code corresponding to `tex_math`.
Set `inline` to False for displayed math.
"""
# Set up tree
- tree = math(xmlns='http://www.w3.org/1998/Math/MathML')
- if inline:
- node = tree
- else:
- # block: emulate align* environment with a math table
- tree['display'] = 'block'
- node = mtd()
- tree.append(mtable(mtr(node), displaystyle='true', CLASS='align'))
-
+ math_tree = math(xmlns='http://www.w3.org/1998/Math/MathML')
+ node = math_tree
+ if not inline:
+ math_tree['display'] = 'block'
+ rows = toplevel_code(tex_math).split(r'\\')
+ if len(rows) > 1:
+ # emulate align* environment with a math table
+ node = mtd()
+ math_tree.append(mtable(mtr(node),
+ **align_attributes(rows)))
parse_latex_math(node, tex_math)
- return tree.toprettyxml()
+ return math_tree.toprettyxml()
# >>> print(tex2mathml('3'))
# <math xmlns="http://www.w3.org/1998/Math/MathML">
@@ -1184,26 +1293,57 @@ def tex2mathml(tex_math, inline=True):
# </math>
# >>> print(tex2mathml('3', inline=False))
# <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
-# <mtable class="align" displaystyle="true">
+# <mn>3</mn>
+# </math>
+# >>> print(tex2mathml(r'a & b \\ c & d', inline=False))
+# <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
+# <mtable class="alignat" columnalign="right left" columnspacing="0" displaystyle="true">
+# <mtr>
+# <mtd>
+# <mi>a</mi>
+# </mtd>
+# <mtd>
+# <mi>b</mi>
+# </mtd>
+# </mtr>
+# <mtr>
+# <mtd>
+# <mi>c</mi>
+# </mtd>
+# <mtd>
+# <mi>d</mi>
+# </mtd>
+# </mtr>
+# </mtable>
+# </math>
+# >>> print(tex2mathml(r'a \\ b', inline=False))
+# <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
+# <mtable class="alignat" displaystyle="true">
# <mtr>
# <mtd>
-# <mn>3</mn>
+# <mi>a</mi>
+# </mtd>
+# </mtr>
+# <mtr>
+# <mtd>
+# <mi>b</mi>
# </mtd>
# </mtr>
# </mtable>
# </math>
+
# TODO: look up more symbols from tr25, e.g.
-#
-#
+#
+#
# Table 2.8 Using Vertical Line or Solidus Overlay
# some of the negated forms of mathematical relations that can only be
# encoded by using either U+0338 COMBINING LONG SOLIDUS OVERLAY or U+20D2
# COMBINING LONG VERTICAL LINE OVERLAY . (For issues with using 0338 in
# MathML, see Section 3.2.7, Combining Marks.
-#
+#
# Table 2.9 Variants of Mathematical Symbols using VS1?
-#
+#
# Sequence Description
# 0030 + VS1 DIGIT ZERO - short diagonal stroke form
# 2205 + VS1 EMPTY SET - zero with long diagonal stroke overlay form
@@ -1230,3 +1370,4 @@ def tex2mathml(tex_math, inline=True):
# 2AAD + VS1 LARGER THAN OR slanted EQUAL
# 2ACB + VS1 SUBSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
# 2ACC + VS1 SUPERSET OF ABOVE NOT EQUAL TO - variant with stroke through bottom members
+
diff --git a/docutils/docutils/utils/math/tex2unichar.py b/docutils/docutils/utils/math/tex2unichar.py
index 3630793e5..c9c10405d 100644
--- a/docutils/docutils/utils/math/tex2unichar.py
+++ b/docutils/docutils/utils/math/tex2unichar.py
@@ -227,9 +227,11 @@ mathop = {
'iiint': u'\u222d', # ∭ TRIPLE INTEGRAL
'iint': u'\u222c', # ∬ DOUBLE INTEGRAL
'int': u'\u222b', # ∫ INTEGRAL
+ 'intop': u'\u222b', # ∫ INTEGRAL
'oiint': u'\u222f', # ∯ SURFACE INTEGRAL
'oint': u'\u222e', # ∮ CONTOUR INTEGRAL
'ointctrclockwise': u'\u2233', # ∳ ANTICLOCKWISE CONTOUR INTEGRAL
+ 'ointop': u'\u222e', # ∮ CONTOUR INTEGRAL
'prod': u'\u220f', # ∏ N-ARY PRODUCT
'sqint': u'\u2a16', # ⨖ QUATERNION INTEGRAL OPERATOR
'sum': u'\u2211', # ∑ N-ARY SUMMATION
@@ -325,8 +327,8 @@ mathord = {
'dasharrow': u'\u21e2', # ⇢ RIGHTWARDS DASHED ARROW
'dashleftarrow': u'\u21e0', # ⇠ LEFTWARDS DASHED ARROW
'dashrightarrow': u'\u21e2', # ⇢ RIGHTWARDS DASHED ARROW
- 'diagdown': u'\u27cd', # ⟍
- 'diagup': u'\u27cb', # ⟋
+ 'diagdown': u'\u27cd', # ⟍
+ 'diagup': u'\u27cb', # ⟋
'diameter': u'\u2300', # ⌀ DIAMETER SIGN
'diamondsuit': u'\u2662', # ♢ WHITE DIAMOND SUIT
'earth': u'\u2641', # ♁ EARTH
diff --git a/docutils/test/functional/expected/math_output_mathml.html b/docutils/test/functional/expected/math_output_mathml.html
index aebcc3b35..37191201f 100644
--- a/docutils/test/functional/expected/math_output_mathml.html
+++ b/docutils/test/functional/expected/math_output_mathml.html
@@ -44,57 +44,45 @@ role specificator, <math xmlns="http://www.w3.org/1998/Math/MathML">
<cite>math</cite> directive:</p>
<div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <mi>f</mi>
- <mo stretchy="false">(</mo>
- <mi>ϵ</mi>
- <mo stretchy="false">)</mo>
- <mo>=</mo>
+ <mi>f</mi>
+ <mo stretchy="false">(</mo>
+ <mi>ϵ</mi>
+ <mo stretchy="false">)</mo>
+ <mo>=</mo>
+ <mfrac>
+ <mn>1</mn>
+ <mrow>
+ <mn>1</mn>
+ <mo>+</mo>
+ <mi>exp</mi>
+ <mo>&ApplyFunction;</mo>
+ <mrow>
+ <mo>(</mo>
<mfrac>
- <mn>1</mn>
+ <mi>ε</mi>
<mrow>
- <mn>1</mn>
- <mo>+</mo>
- <mi>exp</mi>
- <mo>&ApplyFunction;</mo>
- <mrow>
- <mo>(</mo>
- <mfrac>
- <mi>ε</mi>
- <mrow>
- <msub>
- <mi>k</mi>
- <mtext>B</mtext>
- </msub>
- <mi>T</mi>
- </mrow>
- </mfrac>
- <mo>)</mo>
- </mrow>
+ <msub>
+ <mi>k</mi>
+ <mtext>B</mtext>
+ </msub>
+ <mi>T</mi>
</mrow>
</mfrac>
- </mtd>
- </mtr>
- </mtable>
+ <mo>)</mo>
+ </mrow>
+ </mrow>
+ </mfrac>
</math>
</div>
<p>Content may start on the first line of the directive, e.g.</p>
<div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <mi>N</mi>
- <mo>=</mo>
- <mfrac>
- <mtext>number of apples</mtext>
- <mn>7</mn>
- </mfrac>
- </mtd>
- </mtr>
- </mtable>
+ <mi>N</mi>
+ <mo>=</mo>
+ <mfrac>
+ <mtext>number of apples</mtext>
+ <mn>7</mn>
+ </mfrac>
</math>
</div>
<p>Equations can be labeled with a reference name using the <span class="docutils literal">:name:</span> option.
@@ -102,36 +90,30 @@ See <a class="reference internal" href="#eq-m">eq:M</a> and <a class="reference
<p>The determinant of the matrix</p>
<div id="eq-m">
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <mi mathvariant="bold">M</mi>
- <mo>=</mo>
- <mrow>
- <mo>(</mo>
- <mtable>
- <mtr>
- <mtd>
- <mi>a</mi>
- </mtd>
- <mtd>
- <mi>b</mi>
- </mtd>
- </mtr>
- <mtr>
- <mtd>
- <mi>c</mi>
- </mtd>
- <mtd>
- <mi>d</mi>
- </mtd>
- </mtr>
- </mtable>
- <mo>)</mo>
- </mrow>
- </mtd>
- </mtr>
- </mtable>
+ <mi mathvariant="bold">M</mi>
+ <mo>=</mo>
+ <mrow>
+ <mo>(</mo>
+ <mtable>
+ <mtr>
+ <mtd>
+ <mi>a</mi>
+ </mtd>
+ <mtd>
+ <mi>b</mi>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd>
+ <mi>c</mi>
+ </mtd>
+ <mtd>
+ <mi>d</mi>
+ </mtd>
+ </mtr>
+ </mtable>
+ <mo>)</mo>
+ </mrow>
</math>
</div>
<p>is <math xmlns="http://www.w3.org/1998/Math/MathML">
@@ -149,64 +131,52 @@ See <a class="reference internal" href="#eq-m">eq:M</a> and <a class="reference
For example, the following sum and integral with limits:</p>
<div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <msubsup>
- <mo>∫</mo>
- <mn>0</mn>
- <mn>1</mn>
- </msubsup>
- <msup>
- <mi>x</mi>
- <mi>n</mi>
- </msup>
- <mi>d</mi>
- <mi>x</mi>
- <mo>=</mo>
- <mfrac>
- <mn>1</mn>
- <mrow>
- <mi>n</mi>
- <mo>+</mo>
- <mn>1</mn>
- </mrow>
- </mfrac>
- </mtd>
- </mtr>
- </mtable>
+ <msubsup>
+ <mo>∫</mo>
+ <mn>0</mn>
+ <mn>1</mn>
+ </msubsup>
+ <msup>
+ <mi>x</mi>
+ <mi>n</mi>
+ </msup>
+ <mi>d</mi>
+ <mi>x</mi>
+ <mo>=</mo>
+ <mfrac>
+ <mn>1</mn>
+ <mrow>
+ <mi>n</mi>
+ <mo>+</mo>
+ <mn>1</mn>
+ </mrow>
+ </mfrac>
</math>
</div>
<div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <munderover>
- <mo movablelimits="true">∑</mo>
- <mrow>
- <mi>n</mi>
- <mo>=</mo>
- <mn>1</mn>
- </mrow>
- <mi>m</mi>
- </munderover>
- <mi>n</mi>
- <mo>=</mo>
- <mfrac>
- <mrow>
- <mi>m</mi>
- <mo stretchy="false">(</mo>
- <mi>m</mi>
- <mo>+</mo>
- <mn>1</mn>
- <mo stretchy="false">)</mo>
- </mrow>
- <mn>2</mn>
- </mfrac>
- </mtd>
- </mtr>
- </mtable>
+ <munderover>
+ <mo movablelimits="true">∑</mo>
+ <mrow>
+ <mi>n</mi>
+ <mo>=</mo>
+ <mn>1</mn>
+ </mrow>
+ <mi>m</mi>
+ </munderover>
+ <mi>n</mi>
+ <mo>=</mo>
+ <mfrac>
+ <mrow>
+ <mi>m</mi>
+ <mo stretchy="false">(</mo>
+ <mi>m</mi>
+ <mo>+</mo>
+ <mn>1</mn>
+ <mo stretchy="false">)</mo>
+ </mrow>
+ <mn>2</mn>
+ </mfrac>
</math>
</div>
<p>LaTeX-supported Unicode math symbols can be used in math roles and
@@ -214,29 +184,23 @@ directives:</p>
<p>The Schrödinger equation</p>
<div id="eq-schrodinger">
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <mi>i</mi>
- <mi>ℏ</mi>
- <mfrac>
- <mo>∂</mo>
- <mrow>
- <mo>∂</mo>
- <mi>t</mi>
- </mrow>
- </mfrac>
- <mi class="capital-greek">Ψ</mi>
- <mo>=</mo>
- <mover accent="true">
- <mi>H</mi>
- <mo>ˆ</mo>
- </mover>
- <mi class="capital-greek">Ψ</mi>
- <mo>,</mo>
- </mtd>
- </mtr>
- </mtable>
+ <mi>i</mi>
+ <mi>ℏ</mi>
+ <mfrac>
+ <mo>∂</mo>
+ <mrow>
+ <mo>∂</mo>
+ <mi>t</mi>
+ </mrow>
+ </mfrac>
+ <mi class="capital-greek">Ψ</mi>
+ <mo>=</mo>
+ <mover accent="true">
+ <mi>H</mi>
+ <mo>ˆ</mo>
+ </mover>
+ <mi class="capital-greek">Ψ</mi>
+ <mo>,</mo>
</math>
</div>
<p>with the <em>wave function</em> <math xmlns="http://www.w3.org/1998/Math/MathML">
@@ -337,101 +301,95 @@ physical system changes in time.</p>
<p>Modulation Transfer Function:</p>
<div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <mtext>MTF</mtext>
- <mo>=</mo>
- <mrow>
- <mo>|</mo>
- <mfrac>
- <mrow>
- <mi mathvariant="script">F</mi>
- <mo>{</mo>
- <mi>s</mi>
- <mo stretchy="false">(</mo>
- <mi>x</mi>
- <mo stretchy="false">)</mo>
- <mo>}</mo>
- </mrow>
- <mrow>
- <mi mathvariant="script">F</mi>
- <mo>{</mo>
- <mi>s</mi>
- <mo stretchy="false">(</mo>
- <mi>x</mi>
- <mo stretchy="false">)</mo>
- <mo>}</mo>
- <msub>
- <mo stretchy="false">|</mo>
- <mrow>
- <msub>
- <mi>ω</mi>
- <mi>x</mi>
- </msub>
- <mo>=</mo>
- <mn>0</mn>
- </mrow>
- </msub>
- </mrow>
- </mfrac>
- <mo>|</mo>
- </mrow>
- <mo>=</mo>
- <mi mathvariant="normal">abs</mi>
- <mrow>
- <mo>(</mo>
- <mfrac>
- <mrow>
- <msubsup>
- <mo>∫</mo>
- <mrow>
- <mo>−</mo>
- <mo>∞</mo>
- </mrow>
- <mo>∞</mo>
- </msubsup>
- <mi>s</mi>
- <mo stretchy="false">(</mo>
- <mi>x</mi>
- <mo stretchy="false">)</mo>
- <msup>
- <mi mathvariant="normal">e</mi>
- <mrow>
- <mi mathvariant="normal">i</mi>
- <msub>
- <mi>ω</mi>
- <mi>x</mi>
- </msub>
- <mi>x</mi>
- </mrow>
- </msup>
- <mi mathvariant="normal">d</mi>
- <mi>x</mi>
- </mrow>
- <mrow>
- <msubsup>
- <mo>∫</mo>
- <mrow>
- <mo>−</mo>
- <mo>∞</mo>
- </mrow>
- <mo>∞</mo>
- </msubsup>
- <mi>s</mi>
- <mo stretchy="false">(</mo>
+ <mtext>MTF</mtext>
+ <mo>=</mo>
+ <mrow>
+ <mo>|</mo>
+ <mfrac>
+ <mrow>
+ <mi mathvariant="script">F</mi>
+ <mo>{</mo>
+ <mi>s</mi>
+ <mo stretchy="false">(</mo>
+ <mi>x</mi>
+ <mo stretchy="false">)</mo>
+ <mo>}</mo>
+ </mrow>
+ <mrow>
+ <mi mathvariant="script">F</mi>
+ <mo>{</mo>
+ <mi>s</mi>
+ <mo stretchy="false">(</mo>
+ <mi>x</mi>
+ <mo stretchy="false">)</mo>
+ <mo>}</mo>
+ <msub>
+ <mo stretchy="false">|</mo>
+ <mrow>
+ <msub>
+ <mi>ω</mi>
<mi>x</mi>
- <mo stretchy="false">)</mo>
- <mi mathvariant="normal">d</mi>
+ </msub>
+ <mo>=</mo>
+ <mn>0</mn>
+ </mrow>
+ </msub>
+ </mrow>
+ </mfrac>
+ <mo>|</mo>
+ </mrow>
+ <mo>=</mo>
+ <mi mathvariant="normal">abs</mi>
+ <mrow>
+ <mo>(</mo>
+ <mfrac>
+ <mrow>
+ <msubsup>
+ <mo>∫</mo>
+ <mrow>
+ <mo>−</mo>
+ <mo>∞</mo>
+ </mrow>
+ <mo>∞</mo>
+ </msubsup>
+ <mi>s</mi>
+ <mo stretchy="false">(</mo>
+ <mi>x</mi>
+ <mo stretchy="false">)</mo>
+ <msup>
+ <mi mathvariant="normal">e</mi>
+ <mrow>
+ <mi mathvariant="normal">i</mi>
+ <msub>
+ <mi>ω</mi>
<mi>x</mi>
- </mrow>
- </mfrac>
- <mo>)</mo>
- </mrow>
- <mo>.</mo>
- </mtd>
- </mtr>
- </mtable>
+ </msub>
+ <mi>x</mi>
+ </mrow>
+ </msup>
+ <mi mathvariant="normal">d</mi>
+ <mi>x</mi>
+ </mrow>
+ <mrow>
+ <msubsup>
+ <mo>∫</mo>
+ <mrow>
+ <mo>−</mo>
+ <mo>∞</mo>
+ </mrow>
+ <mo>∞</mo>
+ </msubsup>
+ <mi>s</mi>
+ <mo stretchy="false">(</mo>
+ <mi>x</mi>
+ <mo stretchy="false">)</mo>
+ <mi mathvariant="normal">d</mi>
+ <mi>x</mi>
+ </mrow>
+ </mfrac>
+ <mo>)</mo>
+ </mrow>
+ <mo>.</mo>
</math>
</div>
<p>Math split over two lines: If a double backslash is detected outside a
@@ -439,7 +397,7 @@ physical system changes in time.</p>
<span class="docutils literal">align</span> environment:</p>
<div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
+ <mtable class="alignat" columnalign="right left" columnspacing="0" displaystyle="true">
<mtr>
<mtd>
<msub>
@@ -508,43 +466,37 @@ physical system changes in time.</p>
<p>Cases with the <a class="reference external" href="ftp://ftp.ams.org/ams/doc/amsmath/short-math-guide.pdf">AMSmath</a> <span class="docutils literal">cases</span> environment:</p>
<div>
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
- <mtable class="align" displaystyle="true">
- <mtr>
- <mtd>
- <mi mathvariant="normal">sgn</mi>
- <mo stretchy="false">(</mo>
- <mi>x</mi>
- <mo stretchy="false">)</mo>
- <mo>=</mo>
- <mrow>
- <mo>{</mo>
- <mtable>
- <mtr>
- <mtd>
- <mo>−</mo>
- <mn>1</mn>
- </mtd>
- <mtd>
- <mi>x</mi>
- <mo>&lt;</mo>
- <mn>0</mn>
- </mtd>
- </mtr>
- <mtr>
- <mtd>
- <mn>1</mn>
- </mtd>
- <mtd>
- <mi>x</mi>
- <mo>&gt;</mo>
- <mn>0</mn>
- </mtd>
- </mtr>
- </mtable>
- </mrow>
- </mtd>
- </mtr>
- </mtable>
+ <mi mathvariant="normal">sgn</mi>
+ <mo stretchy="false">(</mo>
+ <mi>x</mi>
+ <mo stretchy="false">)</mo>
+ <mo>=</mo>
+ <mrow>
+ <mo>{</mo>
+ <mtable>
+ <mtr>
+ <mtd>
+ <mo>−</mo>
+ <mn>1</mn>
+ </mtd>
+ <mtd>
+ <mi>x</mi>
+ <mo>&lt;</mo>
+ <mn>0</mn>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd>
+ <mn>1</mn>
+ </mtd>
+ <mtd>
+ <mi>x</mi>
+ <mo>&gt;</mo>
+ <mn>0</mn>
+ </mtd>
+ </mtr>
+ </mtable>
+ </mrow>
</math>
</div>
</main>