diff options
| author | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2021-06-17 09:58:31 +0000 |
|---|---|---|
| committer | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2021-06-17 09:58:31 +0000 |
| commit | 5ffda575a88b108830a781de64e9bce8b69d95e9 (patch) | |
| tree | 85645d939ffacb793989e1908854481ea3e9a242 /docutils | |
| parent | 0c0aee2cff28cb9b820e45f8f1e0853b4e666ac6 (diff) | |
| download | docutils-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.txt | 238 | ||||
| -rw-r--r-- | docutils/docutils/utils/math/__init__.py | 12 | ||||
| -rw-r--r-- | docutils/docutils/utils/math/latex2mathml.py | 303 | ||||
| -rw-r--r-- | docutils/docutils/utils/math/tex2unichar.py | 6 | ||||
| -rw-r--r-- | docutils/test/functional/expected/math_output_mathml.html | 512 |
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>⁡</mo> + <mrow> + <mo>(</mo> <mfrac> - <mn>1</mn> + <mi>ε</mi> <mrow> - <mn>1</mn> - <mo>+</mo> - <mi>exp</mi> - <mo>⁡</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><</mo> - <mn>0</mn> - </mtd> - </mtr> - <mtr> - <mtd> - <mn>1</mn> - </mtd> - <mtd> - <mi>x</mi> - <mo>></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><</mo> + <mn>0</mn> + </mtd> + </mtr> + <mtr> + <mtd> + <mn>1</mn> + </mtd> + <mtd> + <mi>x</mi> + <mo>></mo> + <mn>0</mn> + </mtd> + </mtr> + </mtable> + </mrow> </math> </div> </main> |
