summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2021-05-20 12:24:59 +0000
committermilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2021-05-20 12:24:59 +0000
commit3b5acdf48a93e3134c4472bb35b4cf112eebc262 (patch)
tree475c902cac53df1f922863a778e46024aaed60ab
parent82380c29bfd2f2133b4bcbf0857688a32a2c9cce (diff)
downloaddocutils-3b5acdf48a93e3134c4472bb35b4cf112eebc262.tar.gz
MathML: remove unrequired <mrow>s, support more commands.
Support additional commands from the AMS math-guide. Remove <mrow>, if it is single child and the parent inferres an mrow or if it has only one child element. tex_number(): do not return trailing delimiter. Use class "mathscr" instead of hard-codes font name for \mathscr. git-svn-id: https://svn.code.sf.net/p/docutils/code/trunk@8756 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
-rw-r--r--docutils/docs/user/mathematics.txt197
-rw-r--r--docutils/docutils/utils/math/latex2mathml.py153
-rw-r--r--docutils/docutils/utils/math/tex2unichar.py4
-rw-r--r--docutils/docutils/writers/html5_polyglot/plain.css3
-rw-r--r--docutils/docutils/writers/html5_polyglot/responsive.css3
-rw-r--r--docutils/test/functional/expected/math_output_mathml.html124
6 files changed, 220 insertions, 264 deletions
diff --git a/docutils/docs/user/mathematics.txt b/docutils/docs/user/mathematics.txt
index 61f324a1d..0132015b5 100644
--- a/docutils/docs/user/mathematics.txt
+++ b/docutils/docs/user/mathematics.txt
@@ -118,7 +118,7 @@ The "narrow" accents are intended for a single-letter base.
.. class:: colwidths-auto
=========== ============= =========== ============= ============== ================
- `\acute{x}` ``\acute{x}`` `\dot{t}` ``\dot{t}`` `\hat{H}` ``\hat{H}``
+ `\acute{x}` ``\acute{x}`` `\dot{t}` ``\dot{t}`` `\hat{x}` ``\hat{x}``
`\bar{v}` ``\bar{v}`` `\ddot{t}` ``\ddot{t}`` `\mathring{x}` ``\mathring{x}``
`\breve{x}` ``\breve{x}`` `\dddot{t}` ``\dddot{t}`` `\tilde{n}` ``\tilde{n}``
`\check{x}` ``\check{x}`` `\grave{x}` ``\grave{x}`` `\vec{x}` ``\vec{x}``
@@ -190,28 +190,22 @@ Pairing delimiters
=============== ================= ========================= ===========================
`( )` ``( )`` `\langle \rangle` ``\langle \rangle``
- `[ ]` ``[ ]`` `\lceil \rceil` ``\lceil \rceil``
+ `[ ]` ``[ ]`` `\lceil \rceil` ``\lceil \rceil``
`\{ \}` ``\{ \}`` `\lfloor \rfloor` ``\lfloor \rfloor``
- `\lvert \rvert` ``\lvert \rvert`` `\lVert \rVert` ``\lVert \rVert``
+ `\lvert \rvert` ``\lvert \rvert`` `\lgroup \rgroup` ``\lgroup \rgroup``
+ `\lVert \rVert` ``\lVert \rVert`` `\lmoustache \rmoustache` ``\lmoustache \rmoustache``
=============== ================= ========================= ===========================
-.. The "math-guide" also lists macros for parts of extensible delimiters:
-
- ============= = ============= = ============ =
- `\lmoustache` ⎰ `\rmoustache` ⎱ `\bracevert` ⎪
- `\lgroup` ⟮ `\rgroup` ⟯ `\arrowvert` ⏐
- .. `\Arrowvert` ‖
- ============= = ============= = ============ =
Nonpairing delimiters
~~~~~~~~~~~~~~~~~~~~~
.. class:: colwidths-auto
- =========== ===========================
- `|` ``|`` `\vert` ``\vert``
- `\|` ``\|`` `\Vert` ``\Vert``
- `/` ``/`` `\backslash` ``\backslash``
- =========== ===========================
+ ==== ====== ============ ============== ============ ==============
+ `|` ``|`` `\vert` ``\vert`` `\arrowvert` ``\arrowvert``
+ `\|` ``\|`` `\Vert` ``\Vert`` `\Arrowvert` ``\Arrowvert``
+ `/` ``/`` `\backslash` ``\backslash`` `\bracevert` ``\bracevert``
+ ==== ====== ============ ============== ============ ==============
The use of ``|`` and ``\|`` for pairs of vertical bars may produce
incorrect spacing, e.g., ``|k|=|-k|`` produces `|k| = |−k|` and
@@ -229,6 +223,37 @@ Vertical Arrows
`\updownarrow` ``\updownarrow`` `\Updownarrow` ``\Updownarrow``
=============================== ======================================
+Functions (named operators)
+---------------------------
+.. class:: colwidths-auto
+
+ ========= =========== ========= =========== ============= ================
+ `\arccos` ``\arccos`` `\gcd` ``\gcd`` `\Pr` ``\Pr``
+ `\arcsin` ``\arcsin`` `\hom` ``\hom`` `\projlim` ``\projlim``
+ `\arctan` ``\arctan`` `\inf` ``\inf`` `\sec` ``\sec``
+ `\arg` ``\arg`` `\injlim` ``\injlim`` `\sin` ``\sin``
+ `\cos` ``\cos`` `\ker` ``\ker`` `\sinh` ``\sinh``
+ `\cosh` ``\cosh`` `\lg` ``\lg`` `\sup` ``\sup``
+ `\cot` ``\cot`` `\lim` ``\lim`` `\tan` ``\tan``
+ `\coth` ``\coth`` `\liminf` ``\liminf`` `\tanh` ``\tanh``
+ `\csc` ``\csc`` `\limsup` ``\limsup`` `\varlimsup` ``\varlimsup``
+ `\deg` ``\deg`` `\ln` ``\ln`` `\varliminf` ``\varliminf``
+ `\det` ``\det`` `\log` ``\log`` `\varprojlim` ``\varprojlim``
+ `\dim` ``\dim`` `\max` ``\max`` `\varinjlim` ``\varinjlim``
+ `\exp` ``\exp`` `\min` ``\min``
+ ========= =========== ========= =========== ============= ================
+
+Named operators outside the above list can be typeset with
+``\operatorname{name}``, e.g.
+
+.. math:: \operatorname{sgn}(-3) = -1.
+
+The ``\DeclareMathOperator`` command can only be used in the
+`LaTeX preamble`_.
+
+.. _LaTeX preamble: latex.html#latex-preamble
+
+
Greek letters
-------------
@@ -243,16 +268,16 @@ formulas and not supported by LaTeX.
========== ============ ========== ============ ========== ============ ============== ===============
`\Gamma` ``\Gamma`` `\alpha` ``\alpha`` `\mu` ``\mu`` `\omega` ``\omega``
- `\Delta` ``\Delta`` `\beta` ``\beta`` `\nu` ``\nu`` `\backepsilon` ``\backepsilon``
- `\Lambda` ``\Lambda`` `\gamma` ``\gamma`` `\xi` ``\xi`` `\digamma` ``\digamma``
- `\Omega` ``\Omega`` `\delta` ``\delta`` `\pi` ``\pi`` `\varepsilon` ``\varepsilon``
- `\Phi` ``\Phi`` `\epsilon` ``\epsilon`` `\rho` ``\rho`` `\varkappa` ``\varkappa``
- `\Pi` ``\Pi`` `\zeta` ``\zeta`` `\sigma` ``\sigma`` `\varphi` ``\varphi``
- `\Psi` ``\Psi`` `\eta` ``\eta`` `\tau` ``\tau`` `\varpi` ``\varpi``
- `\Sigma` ``\Sigma`` `\theta` ``\theta`` `\upsilon` ``\upsilon`` `\varrho` ``\varrho``
- `\Theta` ``\Theta`` `\iota` ``\iota`` `\phi` ``\phi`` `\varsigma` ``\varsigma``
- `\Upsilon` ``\Upsilon`` `\kappa` ``\kappa`` `\chi` ``\chi`` `\vartheta` ``\vartheta``
- `\Xi` ``\Xi`` `\lambda` ``\lambda`` `\psi` ``\psi``
+ `\Delta` ``\Delta`` `\beta` ``\beta`` `\nu` ``\nu`` `\digamma` ``\digamma``
+ `\Lambda` ``\Lambda`` `\gamma` ``\gamma`` `\xi` ``\xi`` `\backepsilon` ``\backepsilon``
+ `\Phi` ``\Phi`` `\delta` ``\delta`` `\pi` ``\pi`` `\varepsilon` ``\varepsilon``
+ `\Pi` ``\Pi`` `\epsilon` ``\epsilon`` `\rho` ``\rho`` `\varkappa` ``\varkappa``
+ `\Psi` ``\Psi`` `\zeta` ``\zeta`` `\sigma` ``\sigma`` `\varphi` ``\varphi``
+ `\Sigma` ``\Sigma`` `\eta` ``\eta`` `\tau` ``\tau`` `\varpi` ``\varpi``
+ `\Theta` ``\Theta`` `\theta` ``\theta`` `\upsilon` ``\upsilon`` `\varrho` ``\varrho``
+ `\Upsilon` ``\Upsilon`` `\iota` ``\iota`` `\phi` ``\phi`` `\varsigma` ``\varsigma``
+ `\Xi` ``\Xi`` `\kappa` ``\kappa`` `\chi` ``\chi`` `\vartheta` ``\vartheta``
+ `\Omega` ``\Omega`` `\lambda` ``\lambda`` `\psi` ``\psi``
========== ============ ========== ============ ========== ============ ============== ===============
__ http://mirrors.ctan.org/macros/latex/contrib/isomath/isomath.html#table-2
@@ -262,20 +287,21 @@ 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``
+`\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``
+`\eth` ``\eth`` `\mho` ``\mho``
========== ============ ============= =============== ========= =========== =========== =============
Mathematical Alphabets
----------------------
-For mathematical variables where style variations are important semantically,
-select an appropriate *math alphabet* [#]_.
+Mathematical alphabets select a combination of font attributes (shape,
+weight, family) [#]_. They are intended for mathematical variables where
+style variations are important semantically.
.. class:: colwidths-auto
@@ -321,6 +347,11 @@ in line with the International Standard [ISO-80000-2].
.. math:: \mathbfit{r}^2=x^2+y^2+z^2.
+The package mathrsfs_ (and some drop-in replacements) define the ``\mathscr``
+macro that selects a differently shaped "script" alphabet.
+Compare `\mathscr{A, B, …, Z, a, b, …, z}`
+with `\mathcal{A, B, …, Z, a, b, …, z}`.
+
In contrast to the math alphabet selectors, ``\boldsymbol`` only changes
the *font weight*. In LaTeX, it can be used to get a bold version of any
@@ -350,46 +381,18 @@ Miscellaneous symbols
==================== ====================== ================ ================== ================= ===================
-Named operators
----------------
-.. class:: colwidths-auto
-
- ========= =========== ========= =========== ============= ================
- `\arccos` ``\arccos`` `\gcd` ``\gcd`` `\Pr` ``\Pr``
- `\arcsin` ``\arcsin`` `\hom` ``\hom`` `\projlim` ``\projlim``
- `\arctan` ``\arctan`` `\inf` ``\inf`` `\sec` ``\sec``
- `\arg` ``\arg`` `\injlim` ``\injlim`` `\sin` ``\sin``
- `\cos` ``\cos`` `\ker` ``\ker`` `\sinh` ``\sinh``
- `\cosh` ``\cosh`` `\lg` ``\lg`` `\sup` ``\sup``
- `\cot` ``\cot`` `\lim` ``\lim`` `\tan` ``\tan``
- `\coth` ``\coth`` `\liminf` ``\liminf`` `\tanh` ``\tanh``
- `\csc` ``\csc`` `\limsup` ``\limsup`` `\varlimsup` ``\varlimsup``
- `\deg` ``\deg`` `\ln` ``\ln`` `\varliminf` ``\varliminf``
- `\det` ``\det`` `\log` ``\log`` `\varprojlim` ``\varprojlim``
- `\dim` ``\dim`` `\max` ``\max`` `\varinjlim` ``\varinjlim``
- `\exp` ``\exp`` `\min` ``\min``
- ========= =========== ========= =========== ============= ================
-
-Named operators outside the above list can be typeset with
-``\operatorname{name}``, e.g.
-
-.. math:: \operatorname{sgn}(-3) = -1.
-
-The ``\DeclareMathOperator`` command can only be used in the
-`LaTeX preamble`_.
-
-.. _LaTeX preamble: latex.html#latex-preamble
-
Punctuation
-----------
.. class:: colwidths-auto
-======== ===============
-`\colon` ``\colon`` [#]_
-`\cdots` ``\cdots``
-`\ddots` ``\ddots``
-`\ddots` ``\ddots``
-======== ===============
+=== ===== ======== =============== ======== ==========
+`.` ``.`` `!` ``!`` `?` ``?``
+`/` ``/`` `\colon` ``\colon`` [#]_ `\dotsb` ``\dotsb``
+`|` ``|`` `\cdots` ``\cdots`` `\dotsc` ``\dotsc``
+`'` ``'`` `\ddots` ``\ddots`` `\dotsi` ``\dotsi``
+`;` ``;`` `\ldots` ``\ldots`` `\dotsm` ``\dotsm``
+`:` ``:`` `\vdots` ``\vdots`` `\dotso` ``\dotso``
+=== ===== ======== =============== ======== ==========
.. [#] Punctuation (not ratio):
Compare spacing in `a\colon b\to c` to `a:b = c`.
@@ -582,6 +585,9 @@ Result:
.. TODO: small matrices, matrices with delimiters built in?
+ The environments pmatrix, bmatrix, Bmatrix, vmatrix, and Vmatrix have
+ (respectively) ( ), [ ], { }, | |, and k k delimiters built in.
+
For piecewise function definitions there is a cases environment:
.. math::
@@ -596,19 +602,22 @@ Horizontal space
.. class:: colwidths-auto
- ================= ================== =============
- :m:`|\qquad|` ``|\qquad|`` 2 em
- :m:`|\quad|` ``|\quad|`` 1 em
- :m:`|\;|` ``|\;|`` thick
- :m:`|\ |` ``|\ |`` standard
- :m:`|~|` ``|~|`` non-break
- :m:`|\:|` ``|\:|`` medium
- :m:`|\,|` ``|\,|`` thin
- :m:`| |` ``| |`` none
- :m:`|\!|` ``|\!|`` thin negative
- `|\hspace{1ex}|` ``|\hspace{1ex}|`` custom
- ================= ================== =============
-
+ ===================== ======== =======================
+ :m:`|\qquad|` ``|\qquad|`` (2em)
+ :m:`|\quad|` ``|\quad|`` (1em)
+ :m:`|~|` ``|~|`` ``|\nobreakspace|``
+ :m:`|\ |` ``|\ |`` escaped space
+ :m:`|\;|` ``|\;|`` ``|\thickspace|``
+ :m:`|\:|` ``|\:|`` ``|\medspace|``
+ :m:`|\,|` ``|\,|`` ``|\thinspace|``
+ :m:`| |` ``| |`` regular space (ignored)
+ :m:`|\!|` ``|\!|`` ``|\negthinspace|``
+ :m:`|\negmedspace|` ``|\negmedspace|``
+ :m:`|\negthickspace|` ``|\negthickspace|``
+ `|\hspace{1ex}|` ``|\hspace{1ex}|``
+ ===================== ======== =======================
+
+.. TODO: \phantom \hphantom, \vphantom
Roots and Fractions
-------------------
@@ -623,6 +632,8 @@ Roots and Fractions
``\frac`` ``\frac{1}{1-x}`` `\frac{1}{1-x}`
========= ==================== ==================
+.. TODO: \dfrac, \tfrac, \binom, \dbinom, \tbinom
+
Text
====
@@ -638,22 +649,11 @@ Whitespace is kept inside the argument:
Currently, math in text is not supported by LaTeX2MathML.
+.. TODO: Math inside text: ``n - 1 \text{if $n$ is odd}``.
-ToDo
-====
-
-internal LaTeX2MathML
-
-* Math inside text: ``n - 1 \text{if $n$ is odd}``.
-* Remove circular refs.
-* Document ``\circledS``, \circledR, implement other characters
- in the short-math-guide.pdf but not in the
- Unicode `Mathematical Character Repertoire`__
-
-__ http://www.unicode.org/reports/tr25/
Tests
-==========
+=====
Font changes
------------
@@ -670,14 +670,7 @@ MathML), leaving some symbols unchanged:
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}`.
-
-The package mathrsfs_ (and some drop-in replacements) define the ``\mathscr``
-macro that selects a differently shaped "script" alphabet.
-Compare `\mathscr{A, B, …, Z, a, b, …, z}`
-with `\mathcal{A, B, …, Z, a, b, …, z}`.
-
-.. _rsfs: https://ctan.org/pkg/mathrsfs
+\mathbb\Gamma \mathbb{\Pi} \mathbb {\Sigma} \mathbb\gamma \mathbb\pi}`.
Inferred <mrow>s in MathML
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -707,3 +700,5 @@ Sub- and superscript may be given in any order:
`x_i^j = x^j_i` and `\int_0^1 = \int^1_0`.
Double exponent: `x^{10}^4`, `r_T_\mathrm{in}` and `x_i^n^2`.
+
+Binary vs. unary minus operator: `a - b = -c`
diff --git a/docutils/docutils/utils/math/latex2mathml.py b/docutils/docutils/utils/math/latex2mathml.py
index 0793fa4bf..fda5d1792 100644
--- a/docutils/docutils/utils/math/latex2mathml.py
+++ b/docutils/docutils/utils/math/latex2mathml.py
@@ -57,7 +57,7 @@ greek_capitals = {
'Gamma':u'\u0393', 'Lambda':u'\u039b'}
# functions -> <mi>
-functions = dict((name, name) for name in
+functions = dict((name, name) for name in
('arccos', 'arcsin', 'arctan', 'arg', 'cos', 'cosh',
'cot', 'coth', 'csc', 'deg', 'det', 'dim',
'exp', 'gcd', 'hom', 'inf', 'ker', 'lg',
@@ -73,7 +73,7 @@ functions.update({# functions with a space in the name
'operatorname': None})
# function with limits, use <mo> to allow "movablelimits" attribute
-functions_with_limits = dict((name, name) for name in
+functions_with_limits = dict((name, name) for name in
('lim', 'sup', 'inf', 'max', 'min'))
# math font selection -> <mi mathvariant=...> or <mstyle mathvariant=...>
@@ -114,10 +114,16 @@ operators.update({# negated symbols without pre-composed Unicode character
'nsubseteqq': u'\u2AC5\u0338', # ⫅̸
'nsupseteqq': u'\u2AC6\u0338', # ⫆̸
# alias commands:
- 'lvert': u'|', # pairing delimiters
- 'lVert': u'\u2016', # ‖
- 'rvert': u'|',
- 'rVert': u'\u2016',
+ 'lvert': u'|', # left |
+ 'lVert': u'\u2016', # left ‖
+ 'rvert': u'|', # right |
+ 'rVert': u'\u2016', # right ‖
+ 'Arrowvert': u'\u2016', # ‖
+ 'dotsb': u'\u22ef', # ⋯ with binary operators/relations
+ 'dotsc': u'\u2026', # … with commas
+ 'dotsi': u'\u22ef', # ⋯ with integrals
+ 'dotsm': u'\u22ef', # ⋯ multiplication dots
+ 'dotso': u'\u2026', # … other dots
})
# special cases
@@ -132,14 +138,14 @@ small_operators = {# mathsize='75%'
'shortparallel': u'\u2225', # ∥
'nshortmid': u'\u2224', # ∤
'nshortparallel': u'\u2226', # ∦
- 'smallfrown': u'\u2322', # ⌢ FROWN
- 'smallsmile': u'\u2323', # ⌣ SMILE
- 'smallint': u'\u222b', # ∫ INTEGRAL
+ 'smallfrown': u'\u2322', # ⌢ FROWN
+ 'smallsmile': u'\u2323', # ⌣ SMILE
+ 'smallint': u'\u222b', # ∫ INTEGRAL
}
# Operators and functions with limits
-# over/under in display formulas and in index position inline
-sumintprod = [operators[name] for name in
+# above/below in display formulas and in index position inline
+with_limits = [operators[name] for name in
('coprod', 'fatsemi', 'fint', 'iiiint', 'iiint',
'iint', 'int', 'oiint', 'oint', 'ointctrclockwise',
'prod', 'sqint', 'sum', 'varointclockwise',
@@ -249,11 +255,6 @@ mathbb = {u'Γ': u'\u213E', # ℾ
u'Σ': u'\u2140', # ⅀
u'γ': u'\u213D', # ℽ
u'π': u'\u213C', # ℼ
- r'\Gamma': u'\u213E', # ℾ
- r'\Pi': u'\u213F', # ℿ
- r'\Sigma': u'\u2140', # ⅀
- r'\gamma': u'\u213D', # ℽ
- r'\pi': u'\u213C', # ℼ
}
@@ -369,12 +370,6 @@ class math(object):
# >>> math(CLASS='bold').xml_starttag()
# '<math class="bold">'
-class mrow(math):
- """Group sub-expressions as a horizontal row."""
-
-# >>> mrow(displaystyle='false')
-# mrow(displaystyle='false')
-
class mtable(math): pass
# >>> mtable(displaystyle='true')
@@ -382,13 +377,41 @@ class mtable(math): pass
# >>> math(mtable(displaystyle='true')).toprettyxml()
# '<math>\n <mtable displaystyle="true">\n </mtable>\n</math>'
+class mrow(math):
+ """Group sub-expressions as a horizontal row."""
+
+ def close(self):
+ """Close element and return first non-full parent or None.
+
+ Remove <mrow>, if it is single child and the parent inferres an mrow
+ or if it has only one child element.
+ """
+ parent = self.parent
+ if isinstance(parent, MathRowInferred) and parent.nchildren == 1:
+ parent.nchildren = None
+ parent.children = self.children
+ return parent.close()
+ if len(self) == 1:
+ try:
+ parent.children[parent.children.index(self)] = self.children[0]
+ except (AttributeError, ValueError):
+ return self.children[0]
+ return super(mrow, self).close()
+
+# >>> mrow(displaystyle='false')
+# mrow(displaystyle='false')
+
# The elements <msqrt>, <mstyle>, <merror>, <mpadded>, <mphantom>, <menclose>,
# <mtd>, <mscarry>, and <math> treat their contents as a single inferred mrow
# formed from all their children.
-class msqrt(mrow): pass
-class mstyle(mrow): pass
-class mtr(mrow): pass
-class mtd(mrow): pass
+class MathRowInferred(math):
+ """Base class for elements treating content as a single inferred mrow."""
+class mtr(MathRowInferred): pass
+class mtd(MathRowInferred): pass
+class mstyle(MathRowInferred):
+ nchildren = 1
+class msqrt(MathRowInferred):
+ nchildren = 1
class MathToken(math):
"""Token Element: contains data instead of children.
@@ -511,23 +534,27 @@ def tex_cmdname(string):
# ('', '')
def tex_number(string):
- """Return first number literal and remainder of `string`.
+ """Return leading number literal and remainder of `string`.
>>> tex_number('123.4')
('123.4', '')
"""
- m = re.match(r'([0-9.,]+)(.*)', string)
+ m = re.match(r'([0-9.,]*[0-9]+)(.*)', string)
if m is None:
return '', string
return m.group(1), m.group(2)
# Test:
#
-# >>> tex_number(' 123.4')
-# ('', ' 123.4')
-# >>> tex_number('23,400')
-# ('23,400', '')
+# >>> tex_number(' 23.4b') # leading whitespace -> no number
+# ('', ' 23.4b')
+# >>> tex_number('23,400/2') # comma separator included
+# ('23,400', '/2')
+# >>> tex_number('23. 4/2') # trailing separator not included
+# ('23', '. 4/2')
+# >>> tex_number('4, 2') # trailing separator not included
+# ('4', ', 2')
# >>> tex_number('1 000.4')
# ('1', ' 000.4')
@@ -650,15 +677,15 @@ def parse_latex_math(node, string):
elif c.isdigit():
number, string = tex_number(string)
node = node.append(mn(c+number))
- elif c in "/()[]|":
- node = node.append(mo(c, stretchy='false'))
- # use dedicated mathematical operator characters
elif c in anomalous_chars:
+ # characters with a special meaning in LaTeX math mode
node = node.append(mo(anomalous_chars[c]))
+ elif c in "/()[]|":
+ node = node.append(mo(c, stretchy='false'))
elif c in "+*=<>,.!?';@":
node = node.append(mo(c))
else:
- raise SyntaxError(u'Illegal character: "%s"' % c)
+ raise SyntaxError(u'Unsupported character: "%s"' % c)
return node
# Test:
@@ -673,6 +700,8 @@ def parse_latex_math(node, string):
# math(msqrt(mn('2')), mo('≠'), mn('3'))
# >>> parse_latex_math(math(), '\\sqrt{2 + 3} < 3')
# math(msqrt(mn('2'), mo('+'), mn('3')), mo('<'), mn('3'))
+# >>> parse_latex_math(math(), '\\sqrt[3]{2 + 3}')
+# math(mroot(mrow(mn('2'), mo('+'), mn('3')), mn('3')))
# >>> parse_latex_math(math(), '\max_x') # function takes limits
# math(munder(mi('max', movablelimits='true'), mi('x')))
# >>> parse_latex_math(math(), 'x^j_i') # ensure correct order: base, sub, sup
@@ -701,10 +730,10 @@ def handle_cmdname(name, node, string):
if name in letters:
if name in greek_capitals:
- node = node.append(mi(greek_capitals[name], CLASS='capital-greek'))
# 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]))
return node, string
@@ -718,7 +747,7 @@ def handle_cmdname(name, node, string):
new_node = mi(arg, mathvariant='normal')
else:
new_node = mi(functions[name])
- # compound function symbols:
+ # embellished function names:
if name == 'varliminf': # \underline\lim
new_node = munder(new_node, mo(u'_'))
elif name == 'varlimsup': # \overline\lim
@@ -738,36 +767,27 @@ def handle_cmdname(name, node, string):
if name in math_alphabets:
arg, remainder = tex_token(string)
if arg[0] == '\\':
- if name == 'mathbb':
- # mathvariant="double-struck" is ignored for Greek letters
- # (tested in Firefox 78). Use literal Unicode characters.
- arg = mathbb.get(arg, arg)
- # convert single letters (so they can be set with <mi>)
+ # 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':
- # alternative script letter shapes
- attributes['style'] = 'font-family: STIX'
+ attributes['class'] = 'mathscr'
- # one symbol (single letter, name, or number)
- if arg.isalpha():
+ # one symbol (single letter, name, or ⅀)
+ if arg.isalpha() or arg == u'\u2140':
node = node.append(mi(arg, **attributes))
return node, remainder
- if arg.replace('.', '').replace(',', '').isdigit():
- node = node.append(mn(arg, **attributes))
- return node, remainder
- if len(arg) == 1 and arg != '{':
- node = node.append(mo(arg, **attributes))
- return node, remainder
-
# Wrap in <style>
style = mstyle(**attributes)
node.append(style)
- return style, string[1:] # take of the opening '{', <mrow> is inferred
+ return style, string
# operator, fence, or separator -> <mo>
@@ -798,7 +818,7 @@ def handle_cmdname(name, node, string):
row = mrow()
node.append(row)
node = row
- if delimiter: # may be empty (source '.')
+ if delimiter: # may be empty
node.append(mo(delimiter))
if name == 'right':
node = node.close()
@@ -813,7 +833,6 @@ def handle_cmdname(name, node, string):
return node, string
# arbitrary text (usually comments) -> <mtext>
-
if name in ('text', 'mbox', 'textrm'):
arg, string = tex_token(string)
text = re.sub('(^ | $)', u'\u00a0', arg)
@@ -821,7 +840,6 @@ def handle_cmdname(name, node, string):
return node, string
# horizontal space -> <mspace>
-
if name in spaces:
node = node.append(mspace(width='%s'%spaces[name]))
return node, string
@@ -835,18 +853,14 @@ def handle_cmdname(name, node, string):
# ==================================
if name == 'sqrt':
- # TODO: optional arg -> <mroot> <mn>2</mn> <mn>3</mn> </mroot>
- degree, string = tex_optarg(string)
- if degree:
+ radix, string = tex_optarg(string)
+ if radix:
indexnode = mrow()
- parse_latex_math(indexnode, degree)
new_node = mroot(indexnode, switch=True)
+ parse_latex_math(indexnode, radix)
+ indexnode.close()
else:
new_node = msqrt()
- if string.startswith('{'): # argument is a group
- string = string[1:] # mrow implied, skip opening bracket
- else: # no group, enclose only one element
- new_node.nchildren = 1
node.append(new_node)
return new_node, string
@@ -977,8 +991,7 @@ def handle_script_or_limit(node, c):
new_node = msubsup(*child.children, switch=True)
elif isinstance(child, mover):
new_node = munderover(*child.children, switch=True)
- # elif isinstance(child, MathToken) and child.data in sumintprod:
- elif getattr(child, 'data', '') in sumintprod:
+ elif getattr(child, 'data', '') in with_limits:
child.attributes['movablelimits'] = 'true'
new_node = munder(child)
else:
@@ -988,7 +1001,7 @@ def handle_script_or_limit(node, c):
new_node = msubsup(*child.children)
elif isinstance(child, munder):
new_node = munderover(*child.children)
- elif isinstance(child, MathToken) and child.data in sumintprod:
+ elif getattr(child, 'data', '') in with_limits:
child.attributes['movablelimits'] = 'true'
new_node = mover(child)
else:
diff --git a/docutils/docutils/utils/math/tex2unichar.py b/docutils/docutils/utils/math/tex2unichar.py
index 52d990199..3630793e5 100644
--- a/docutils/docutils/utils/math/tex2unichar.py
+++ b/docutils/docutils/utils/math/tex2unichar.py
@@ -295,6 +295,7 @@ mathord = {
'angle': u'\u2220', # ∠ ANGLE
'aquarius': u'\u2652', # ♒ AQUARIUS
'aries': u'\u2648', # ♈ ARIES
+ 'arrowvert': u'\u23d0', # ⏐ VERTICAL LINE EXTENSION
'ast': u'*', # * ASTERISK
'backepsilon': u'\u03f6', # ϶ GREEK REVERSED LUNATE EPSILON SYMBOL
'backprime': u'\u2035', # ‵ REVERSED PRIME
@@ -307,6 +308,7 @@ mathord = {
'blacksquare': u'\u25fc', # ◼ BLACK MEDIUM SQUARE
'bot': u'\u22a5', # ⊥ UP TACK
'boy': u'\u2642', # ♂ MALE SIGN
+ 'bracevert': u'\u23aa', # ⎪ CURLY BRACKET EXTENSION
'cancer': u'\u264b', # ♋ CANCER
'capricornus': u'\u2651', # ♑ CAPRICORN
'cdots': u'\u22ef', # ⋯ MIDLINE HORIZONTAL ELLIPSIS
@@ -347,6 +349,7 @@ mathord = {
'leftturn': u'\u21ba', # ↺ ANTICLOCKWISE OPEN CIRCLE ARROW
'leo': u'\u264c', # ♌ LEO
'libra': u'\u264e', # ♎ LIBRA
+ 'lmoustache': u'\u23b0', # ⎰ UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION
'lnot': u'\xac', # ¬ NOT SIGN
'lozenge': u'\u25ca', # ◊ LOZENGE
'male': u'\u2642', # ♂ MALE SIGN
@@ -369,6 +372,7 @@ mathord = {
'quarternote': u'\u2669', # ♩ QUARTER NOTE
'rightmoon': u'\u263d', # ☽ FIRST QUARTER MOON
'rightturn': u'\u21bb', # ↻ CLOCKWISE OPEN CIRCLE ARROW
+ 'rmoustache': u'\u23b1', # ⎱ UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION
'sagittarius': u'\u2650', # ♐ SAGITTARIUS
'saturn': u'\u2644', # ♄ SATURN
'scorpio': u'\u264f', # ♏ SCORPIUS
diff --git a/docutils/docutils/writers/html5_polyglot/plain.css b/docutils/docutils/writers/html5_polyglot/plain.css
index 599b74364..0b85c5c5e 100644
--- a/docutils/docutils/writers/html5_polyglot/plain.css
+++ b/docutils/docutils/writers/html5_polyglot/plain.css
@@ -265,6 +265,9 @@ pre.code .inserted, code .inserted { background-color: #A3D289}
mtable.align > mtr > mtd { /* emulate AMS "align" environment. */
text-align: left;
}
+mstyle.mathscr, mi.mathscr {
+ font-family: STIX;
+}
/* Epigraph */
/* Highlights */
diff --git a/docutils/docutils/writers/html5_polyglot/responsive.css b/docutils/docutils/writers/html5_polyglot/responsive.css
index 4dbc43e37..551240f2d 100644
--- a/docutils/docutils/writers/html5_polyglot/responsive.css
+++ b/docutils/docutils/writers/html5_polyglot/responsive.css
@@ -345,6 +345,9 @@ table.align-right {
mtable.align > mtr > mtd { /* emulate AMS "align" environment. */
text-align: left;
}
+mstyle.mathscr, mi.mathscr {
+ font-family: STIX;
+}
/* Adaptive page layout */
/* ==================== */
diff --git a/docutils/test/functional/expected/math_output_mathml.html b/docutils/test/functional/expected/math_output_mathml.html
index b5c8f7e31..11eb52fe4 100644
--- a/docutils/test/functional/expected/math_output_mathml.html
+++ b/docutils/test/functional/expected/math_output_mathml.html
@@ -33,12 +33,8 @@ role specificator, <math xmlns="http://www.w3.org/1998/Math/MathML">
</msub>
<mo>=</mo>
<mfrac>
- <mrow>
- <mi>π</mi>
- </mrow>
- <mrow>
- <mn>4</mn>
- </mrow>
+ <mi>π</mi>
+ <mn>4</mn>
</mfrac>
<msup>
<mi>d</mi>
@@ -57,9 +53,7 @@ role specificator, <math xmlns="http://www.w3.org/1998/Math/MathML">
<mo stretchy="false">)</mo>
<mo>=</mo>
<mfrac>
- <mrow>
- <mn>1</mn>
- </mrow>
+ <mn>1</mn>
<mrow>
<mn>1</mn>
<mo>+</mo>
@@ -68,9 +62,7 @@ role specificator, <math xmlns="http://www.w3.org/1998/Math/MathML">
<mrow>
<mo>(</mo>
<mfrac>
- <mrow>
- <mi>ε</mi>
- </mrow>
+ <mi>ε</mi>
<mrow>
<msub>
<mi>k</mi>
@@ -97,12 +89,8 @@ role specificator, <math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>N</mi>
<mo>=</mo>
<mfrac>
- <mrow>
- <mtext>number of apples</mtext>
- </mrow>
- <mrow>
- <mn>7</mn>
- </mrow>
+ <mtext>number of apples</mtext>
+ <mn>7</mn>
</mfrac>
</mtd>
</mtr>
@@ -177,9 +165,7 @@ For example, the following sum and integral with limits:</p>
<mi>x</mi>
<mo>=</mo>
<mfrac>
- <mrow>
- <mn>1</mn>
- </mrow>
+ <mn>1</mn>
<mrow>
<mi>n</mi>
<mo>+</mo>
@@ -216,9 +202,7 @@ For example, the following sum and integral with limits:</p>
<mn>1</mn>
<mo stretchy="false">)</mo>
</mrow>
- <mrow>
- <mn>2</mn>
- </mrow>
+ <mn>2</mn>
</mfrac>
</mtd>
</mtr>
@@ -236,9 +220,7 @@ directives:</p>
<mi>i</mi>
<mi>ℏ</mi>
<mfrac>
- <mrow>
- <mo>∂</mo>
- </mrow>
+ <mo>∂</mo>
<mrow>
<mo>∂</mo>
<mi>t</mi>
@@ -247,9 +229,7 @@ directives:</p>
<mi class="capital-greek">Ψ</mi>
<mo>=</mo>
<mover accent="true">
- <mrow>
- <mi>H</mi>
- </mrow>
+ <mi>H</mi>
<mo>ˆ</mo>
</mover>
<mi class="capital-greek">Ψ</mi>
@@ -274,100 +254,76 @@ physical system changes in time.</p>
<tbody>
<tr><td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>a</mi>
- </mrow>
+ <mi>a</mi>
<mo>´</mo>
</mover>
</math> <span class="docutils literal">\acute{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>t</mi>
- </mrow>
+ <mi>t</mi>
<mo>˙</mo>
</mover>
</math> <span class="docutils literal">\dot{t}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>γ</mi>
- </mrow>
+ <mi>γ</mi>
<mo>ˆ</mo>
</mover>
</math> <span class="docutils literal"><span class="pre">\hat{\gamma}</span></span></p></td>
</tr>
<tr><td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>a</mi>
- </mrow>
+ <mi>a</mi>
<mo>`</mo>
</mover>
</math> <span class="docutils literal">\grave{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>t</mi>
- </mrow>
+ <mi>t</mi>
<mo>¨</mo>
</mover>
</math> <span class="docutils literal">\ddot{t}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>α</mi>
- </mrow>
+ <mi>α</mi>
<mo>˜</mo>
</mover>
</math> <span class="docutils literal"><span class="pre">\tilde{\alpha}</span></span></p></td>
</tr>
<tr><td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>x</mi>
- </mrow>
+ <mi>x</mi>
<mo>˘</mo>
</mover>
</math> <span class="docutils literal">\breve{x}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>t</mi>
- </mrow>
+ <mi>t</mi>
<mo>⃛</mo>
</mover>
</math> <span class="docutils literal">\dddot{t}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="false">
- <mrow>
- <mi>ı</mi>
- </mrow>
+ <mi>ı</mi>
<mo>→</mo>
</mover>
</math> <span class="docutils literal"><span class="pre">\vec{\imath}</span></span></p></td>
</tr>
<tr><td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>a</mi>
- </mrow>
+ <mi>a</mi>
<mo>ˇ</mo>
</mover>
</math> <span class="docutils literal">\check{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="true">
- <mrow>
- <mi>a</mi>
- </mrow>
+ <mi>a</mi>
<mo>ˉ</mo>
</mover>
</math> <span class="docutils literal">\bar{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
<mover accent="false">
- <mrow>
- <mi>R</mi>
- </mrow>
+ <mi>R</mi>
<mo>→</mo>
</mover>
</math> <span class="docutils literal">\vec{R}</span></p></td>
@@ -411,9 +367,7 @@ physical system changes in time.</p>
<mrow>
<msub>
<mi>ω</mi>
- <mrow>
- <mi>x</mi>
- </mrow>
+ <mi>x</mi>
</msub>
<mo>=</mo>
<mn>0</mn>
@@ -435,9 +389,7 @@ physical system changes in time.</p>
<mo>−</mo>
<mo>∞</mo>
</mrow>
- <mrow>
- <mo>∞</mo>
- </mrow>
+ <mo>∞</mo>
</munderover>
<mi>s</mi>
<mo stretchy="false">(</mo>
@@ -449,17 +401,13 @@ physical system changes in time.</p>
<mi mathvariant="normal">i</mi>
<msub>
<mi>ω</mi>
- <mrow>
- <mi>x</mi>
- </mrow>
+ <mi>x</mi>
</msub>
<mi>x</mi>
</mrow>
</msup>
<mi mathvariant="normal">d</mi>
- <mrow>
- <mi>x</mi>
- </mrow>
+ <mi>x</mi>
</mrow>
<mrow>
<munderover>
@@ -468,18 +416,14 @@ physical system changes in time.</p>
<mo>−</mo>
<mo>∞</mo>
</mrow>
- <mrow>
- <mo>∞</mo>
- </mrow>
+ <mo>∞</mo>
</munderover>
<mi>s</mi>
<mo stretchy="false">(</mo>
<mi>x</mi>
<mo stretchy="false">)</mo>
<mi mathvariant="normal">d</mi>
- <mrow>
- <mi>x</mi>
- </mrow>
+ <mi>x</mi>
</mrow>
</mfrac>
<mo>)</mo>
@@ -500,9 +444,7 @@ physical system changes in time.</p>
<mtd>
<msub>
<mi>s</mi>
- <mrow>
- <mi mathvariant="normal">out</mi>
- </mrow>
+ <mi mathvariant="normal">out</mi>
</msub>
<mo stretchy="false">(</mo>
<mi>x</mi>
@@ -512,9 +454,7 @@ physical system changes in time.</p>
<mo>=</mo>
<msub>
<mi>s</mi>
- <mrow>
- <mi mathvariant="normal">in</mi>
- </mrow>
+ <mi mathvariant="normal">in</mi>
</msub>
<mo stretchy="false">(</mo>
<mi>x</mi>
@@ -541,9 +481,7 @@ physical system changes in time.</p>
<mo>∫</mo>
<msub>
<mi>s</mi>
- <mrow>
- <mi mathvariant="normal">in</mi>
- </mrow>
+ <mi mathvariant="normal">in</mi>
</msub>
<mo stretchy="false">(</mo>
<mi>x</mi>