diff options
| author | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2021-05-20 12:24:59 +0000 |
|---|---|---|
| committer | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2021-05-20 12:24:59 +0000 |
| commit | 3b5acdf48a93e3134c4472bb35b4cf112eebc262 (patch) | |
| tree | 475c902cac53df1f922863a778e46024aaed60ab | |
| parent | 82380c29bfd2f2133b4bcbf0857688a32a2c9cce (diff) | |
| download | docutils-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.txt | 197 | ||||
| -rw-r--r-- | docutils/docutils/utils/math/latex2mathml.py | 153 | ||||
| -rw-r--r-- | docutils/docutils/utils/math/tex2unichar.py | 4 | ||||
| -rw-r--r-- | docutils/docutils/writers/html5_polyglot/plain.css | 3 | ||||
| -rw-r--r-- | docutils/docutils/writers/html5_polyglot/responsive.css | 3 | ||||
| -rw-r--r-- | docutils/test/functional/expected/math_output_mathml.html | 124 |
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> |
