|
From: <mi...@us...> - 2021-05-20 12:22:29
|
Revision: 8748
http://sourceforge.net/p/docutils/code/8748
Author: milde
Date: 2021-05-20 12:22:27 +0000 (Thu, 20 May 2021)
Log Message:
-----------
MathML: support more functions and symbols.
- Support emellished identifiers,
- support more functions and symbols from "amsmath",
- map HYPHEN-MINUS -> MINUS SIGN and COLON -> RATIO im math context,
- simplify doctests with starred import,
- dont wrap content in <mrow> when it is inferred by an element
(let these elements inherit from "mrow").
- tex2unichar: Fix/extend character mappings.
Modified Paths:
--------------
trunk/docutils/docutils/utils/math/latex2mathml.py
trunk/docutils/docutils/utils/math/tex2unichar.py
trunk/docutils/test/functional/expected/math_output_mathml.html
trunk/docutils/test/functional/input/data/comprehensive-math-test.txt
Modified: trunk/docutils/docutils/utils/math/latex2mathml.py
===================================================================
--- trunk/docutils/docutils/utils/math/latex2mathml.py 2021-05-20 12:22:05 UTC (rev 8747)
+++ trunk/docutils/docutils/utils/math/latex2mathml.py 2021-05-20 12:22:27 UTC (rev 8748)
@@ -22,7 +22,7 @@
#
# Usage:
#
-# >>> import latex2mathml as l2m
+# >>> from latex2mathml import *
import collections
import re
@@ -54,12 +54,17 @@
functions = ['arccos', 'arcsin', 'arctan', 'arg', 'cos', 'cosh',
'cot', 'coth', 'csc', 'deg', 'det', 'dim',
'exp', 'gcd', 'hom', 'inf', 'ker', 'lg',
- 'lim', 'liminf', 'limsup', 'ln', 'log', 'max',
- 'min', 'Pr', 'sec', 'sin', 'sinh', 'sup',
- 'tan', 'tanh', 'injlim', 'varinjlim', 'projlim',
- 'varlimsup', 'varliminf', 'varprojlim']
+ 'lim', 'ln', 'log', 'max', 'min', 'Pr',
+ 'sec', 'sin', 'sinh', 'sup', 'tan', 'tanh']
+functions = dict((name, name) for name in functions)
+functions.update({# functions with a space in the name
+ 'liminf': u'lim\u202finf', 'limsup': u'lim\u202fsup',
+ 'injlim': u'inj\u202flim', 'projlim': u'proj\u202flim',
+ # embellished function names (see handle_keyword() below)
+ 'varlimsup': 'lim', 'varliminf': 'lim',
+ 'varprojlim': 'lim', 'varinjlim': 'lim'})
-# font selection -> <mi mathvariant=...>
+# math font selection -> <mi mathvariant=...> or <mstyle mathvariant=...>
math_alphabets = {# 'cmdname': 'mathvariant value' # package
'boldsymbol': 'bold',
'mathbb': 'double-struck', # amssymb
@@ -79,19 +84,6 @@
# bold-sans-serif
}
-# blackboar bold (Greek characters not working with "mathvariant" (Firefox 78)
-mathbb = {u'Γ': u'\u213E', # ℾ
- u'Π': u'\u213F', # ℿ
- 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', # ℼ
- }
-
# operator, fence, or separator -> <mo>
operators = tex2unichar.mathbin # Binary symbols
@@ -101,24 +93,58 @@
operators.update(tex2unichar.mathopen) # Braces
operators.update(tex2unichar.mathclose) # Braces
operators.update(tex2unichar.mathfence)
+operators.update({# negated symbols without pre-composed Unicode character
+ 'nleqq': u'\u2266\u0338', # ≦̸
+ 'ngeqq': u'\u2267\u0338', # ≧̸
+ 'nleqslant': u'\u2a7d\u0338', # ⩽̸
+ 'ngeqslant': u'\u2a7e\u0338', # ⩾̸
+ 'nsubseteqq': u'\u2AC5\u0338', # ⫅̸
+ 'nsupseteqq': u'\u2AC6\u0338', # ⫆̸
+ })
-# >>> '{' in l2m.operators.values()
+# >>> '{' in operators.values()
# True
# special cases
+thick_operators = {# style='font-weight: bold;'
+ 'thicksim': u'\u223C', # ∼
+ 'thickapprox':u'\u2248', # ≈
+ }
+
+small_operators = {# mathsize='75%'
+ 'shortmid': u'\u2223', # ∣
+ 'shortparallel': u'\u2225', # ∥
+ 'nshortmid': u'\u2224', # ∤
+ 'nshortparallel': u'\u2226', # ∦
+ 'smallfrown': u'\u2322', # ⌢ FROWN
+ 'smallsmile': u'\u2323', # ⌣ SMILE
+ 'smallint': u'\u222b', # ∫ INTEGRAL
+ }
+
+left_delimiters = {# rspace='0', lspace='0.22em'
+ 'lvert': '|',
+ 'lVert': u'\u2016' # ‖ DOUBLE VERTICAL LINE
+ }
+
+right_delimiters = {# lspace='0', rspace='0.22em'
+ 'rvert': '|',
+ 'rVert': u'\u2016', # ‖ DOUBLE VERTICAL LINE
+ }
+
sumintprod = ''.join([operators[symbol] for symbol in
['sum', 'int', 'oint', 'prod']])
-# >>> print(l2m.sumintprod)
+# >>> print(sumintprod)
# ∑∫∮∏
-#
+# pre-composed characters for negated symbols
+# see https://www.w3.org/TR/xml-entity-names/#combining
negatables = {'=': u'\u2260',
r'\in': u'\u2209',
r'\equiv': u'\u2262'}
-# cmds/characters allowed in left/right cmds
+# extensible delimiters allowed in left/right cmds
stretchables = {'(': '(',
')': ')',
'[': '[',
@@ -147,7 +173,7 @@
# **tex2unichar.mathfence}.items():
# stretchables['\\'+key] = value
-# >>> print(' '.join(sorted(set(l2m.stretchables.values()))))
+# >>> print(' '.join(sorted(set(stretchables.values()))))
# ( ) / [ \ ] { | } ‖ ↑ ↓ ↕ ⇑ ⇓ ⇕ ⌈ ⌉ ⌊ ⌋ ⌜ ⌝ ⌞ ⌟ ⟅ ⟆ ⟦ ⟧ ⟨ ⟩ ⟮ ⟯ ⦇ ⦈
@@ -171,34 +197,54 @@
# accents -> <mover>
# TeX spacing combining
-over = {'acute': u'\u00B4', # u'\u0301',
- 'bar': u'\u00AF', # u'\u0304',
- 'breve': u'\u02D8', # u'\u0306',
- 'check': u'\u02C7', # u'\u030C',
- 'dot': u'\u02D9', # u'\u0307',
- 'ddot': u'\u00A8', # u'\u0308',
- 'dddot': u'\u20DB',
+over = {'acute': u'´', # u'\u0301',
+ 'bar': u'-', # u'\u0304',
+ 'breve': u'˘', # u'\u0306',
+ 'check': u'ˇ', # u'\u030C',
+ 'dot': u'·', # u'\u0307',
+ 'ddot': u'··', # u'\u0308',
+ 'dddot': u'···', # u'\u20DB',
'grave': u'`', # u'\u0300',
'hat': u'^', # u'\u0302',
- 'mathring': u'\u02DA', # u'\u030A',
- 'overleftrightarrow': u'\u20e1',
- # 'overline': # u'\u0305',
- 'tilde': u'\u02DC', # u'\u0303',
- 'vec': u'\u20D7'}
+ 'mathring': u'°', # u'\u030A',
+ 'tilde': u'\u223C', # u'\u0303',
+ 'vec': u'\u20D7',
+ }
+wideover = {'overbrace': u'\u23DE', # TOP CURLY BRACKET
+ 'overleftarrow': u'\u2190',
+ 'overleftrightarrow': u'\u2194',
+ 'overline': u'¯',
+ 'overrightarrow': u'\u2192',
+ 'widehat': u'^',
+ 'widetilde': u'~'}
+wideunder = {'underbrace': u'\u23DF',
+ 'underleftarrow': u'\u2190',
+ 'underleftrightarrow': u'\u2194',
+ 'underline': u'_',
+ 'underrightarrow': u'\u2192'}
-# all supported math-characters:
-#mathcharacters = dict(letters)
-#mathcharacters.update(operators)
-#mathcharacters.update(tex2unichar.space)
-#
-## >>> l2m.mathcharacters['alpha']
-## 'α'
-## >>> l2m.mathcharacters['{']
-## '{'
-## >>> l2m.mathcharacters['pm']
-## '±'
+# Character translations
+# ----------------------
+# characters with preferred alternative in mathematical use
+anomalous_chars = {'-': u'\u2212', # HYPHEN-MINUS -> MINUS SIGN
+ ':': u'\u2236', # COLON -> RATIO
+ }
+# blackboard bold (Greek characters not working with "mathvariant" (Firefox 78)
+mathbb = {u'Γ': u'\u213E', # ℾ
+ u'Π': u'\u213F', # ℿ
+ 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', # ℼ
+ }
+
+
# MathML element classes
# ----------------------
@@ -205,8 +251,10 @@
class math(object):
"""Base class for MathML elements."""
- nchildren = 1000000
- """Required/Supported number of children"""
+ nchildren = None
+ """Expected number of children or None"""
+ parent = None
+ """Parent node in MathML DOM tree."""
_level = 0 # indentation level (static class variable)
def __init__(self, children=None, inline=None, **kwargs):
@@ -216,7 +264,7 @@
self.children = []
if children is not None:
- if not isinstance(children, list):
+ if not isinstance(children, (list, tuple)):
children = [children]
for child in children:
self.append(child)
@@ -226,7 +274,6 @@
self.attributes['xmlns'] = 'http://www.w3.org/1998/Math/MathML'
if inline is False:
self.attributes['display'] = 'block'
- # self.attributes['displaystyle'] = 'true'
# sort kwargs for predictable functional tests
# as self.attributes.update(kwargs) does not keep order in Python < 3.6
for key in sorted(kwargs.keys()):
@@ -242,9 +289,9 @@
def full(self):
"""Room for more children?"""
+ return (self.nchildren is not None
+ and len(self.children) >= self.nchildren)
- return len(self.children) >= self.nchildren
-
def append(self, child):
"""append(child) -> element
@@ -255,7 +302,7 @@
self.children.append(child)
child.parent = self
node = self
- while node.full():
+ while node is not None and node.full():
node = node.parent
return node
@@ -310,42 +357,55 @@
last_child = child
return xml
-# >>> l2m.math(l2m.mn(2))
+# >>> math(mn(2))
# math(mn(2))
-# >>> l2m.math(l2m.mn(2)).xml()
+# >>> math(mn(2)).xml()
# ['<math>', '\n ', '<mn>', '2', '</mn>', '\n', '</math>']
#
-# >>> l2m.math(id='eq3')
+# >>> math(id='eq3')
# math(id='eq3')
-# >>> l2m.math(id='eq3').xml()
+# >>> math(id='eq3').xml()
# ['<math id="eq3">', '\n', '</math>']
#
# use CLASS to get "class" in XML
-# >>> l2m.math(CLASS='test')
+# >>> math(CLASS='test')
# math(CLASS='test')
-# >>> l2m.math(CLASS='test').xml()
+# >>> math(CLASS='test').xml()
# ['<math class="test">', '\n', '</math>']
-# >>> l2m.math(inline=True)
+# >>> math(inline=True)
# math(xmlns='http://www.w3.org/1998/Math/MathML')
-# >>> l2m.math(inline=True).xml()
+# >>> math(inline=True).xml()
# ['<math xmlns="http://www.w3.org/1998/Math/MathML">', '\n', '</math>']
-# >>> l2m.math(inline=False)
+# >>> math(inline=False)
# math(xmlns='http://www.w3.org/1998/Math/MathML', display='block')
-# >>> l2m.math(inline=False).xml()
+# >>> math(inline=False).xml()
# ['<math xmlns="http://www.w3.org/1998/Math/MathML" display="block">', '\n', '</math>']
-class mrow(math): pass
+class mrow(math):
+ """Group sub-expressions as a horizontal row.
+ """
+ def __init__(self, children=None, nchildren=None, **kwargs):
+ self.nchildren = nchildren
+ math.__init__(self, children)
+ self.attributes = kwargs
-# >>> l2m.mrow(displaystyle='false')
+# >>> mrow(displaystyle='false')
# mrow(displaystyle='false')
class mtable(math): pass
-# >>> l2m.mtable(displaystyle='true')
+# >>> mtable(displaystyle='true')
# mtable(displaystyle='true')
-# >>> l2m.math(l2m.mtable(displaystyle='true')).xml()
+# >>> math(mtable(displaystyle='true')).xml()
# ['<math>', '\n ', '<mtable displaystyle="true">', '\n ', '</mtable>', '\n', '</math>']
+
+
+# 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
@@ -367,9 +427,9 @@
class mo(mx): pass
class mtext(mx): pass
-# >>> l2m.mo(u'<')
+# >>> mo(u'<')
# mo('<')
-# >>> l2m.mo(u'<').xml()
+# >>> mo(u'<').xml()
# ['<mo>', '<', '</mo>']
class msub(math):
@@ -378,9 +438,6 @@
class msup(math):
nchildren = 2
-class msqrt(math):
- nchildren = 1
-
class mroot(math):
nchildren = 2
@@ -403,18 +460,11 @@
class mspace(math):
nchildren = 0
-class mstyle(math):
- def __init__(self, children=None, nchildren=None, **kwargs):
- if nchildren is not None:
- self.nchildren = nchildren
- math.__init__(self, children)
- self.attributes = kwargs
-
class mover(math):
nchildren = 2
- def __init__(self, children=None, reversed=False):
+ def __init__(self, children=None, reversed=False, **kwargs):
self.reversed = reversed
- math.__init__(self, children)
+ math.__init__(self, children, **kwargs)
def xml(self):
if self.reversed:
@@ -422,7 +472,10 @@
self.reversed = False
return math.xml(self)
-class munder(math):
+# >>> mover(children=[mi('lim'), mo('-')], accent='false')
+# mover(mi('lim'), mo('-'), accent='false')
+
+class munder(mover):
nchildren = 2
class munderover(math):
@@ -440,11 +493,11 @@
def tex_cmdname(string):
"""Return leading TeX command name from `string`.
- >>> l2m.tex_cmdname('name2') # up to first non-letter
+ >>> tex_cmdname('name2') # up to first non-letter
('name', '2')
- >>> l2m.tex_cmdname('name 2') # strip trailing whitespace
+ >>> tex_cmdname('name 2') # strip trailing whitespace
('name', '2')
- >>> l2m.tex_cmdname('_2') # single non-letter character
+ >>> tex_cmdname('_2') # single non-letter character
('_', '2')
"""
@@ -455,21 +508,15 @@
# Test:
#
-# >>> l2m.tex_cmdname('name_2') # first non-letter terminates
+# >>> tex_cmdname('name_2') # first non-letter terminates
# ('name', '_2')
-# >>> l2m.tex_cmdname(' next') # leading whitespace is returned
+# >>> tex_cmdname(' next') # leading whitespace is returned
# (' ', 'next')
-# >>> l2m.tex_cmdname('1 2') # whitespace after non-letter is kept
+# >>> tex_cmdname('1 2') # whitespace after non-letter is kept
# ('1', ' 2')
-# >>> l2m.tex_cmdname('') # empty string
+# >>> tex_cmdname('') # empty string
# ('', '')
-
-# TODO: check for Inferred <mrow>s:
-
-# 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
#
# --- https://www.w3.org/TR/MathML3/chapter3.html#id.3.1.3.2
@@ -478,11 +525,11 @@
Return token and remainder.
- >>> l2m.tex_token('{first simple group} {without brackets}')
+ >>> tex_token('{first simple group} {without brackets}')
('first simple group', ' {without brackets}')
- >>> l2m.tex_token('\\command{without argument}')
+ >>> tex_token('\\command{without argument}')
('\\command', '{without argument}')
- >>> l2m.tex_token(' first non-white character')
+ >>> tex_token(' first non-white character')
('f', 'irst non-white character')
"""
@@ -505,19 +552,19 @@
# Test:
#
-# >>> l2m.tex_token('{opening bracket of group with {nested group}}')
+# >>> tex_token('{opening bracket of group with {nested group}}')
# ('{', 'opening bracket of group with {nested group}}')
-# >>> l2m.tex_token('{group with \\{escaped\\} brackets}')
+# >>> tex_token('{group with \\{escaped\\} brackets}')
# ('group with \\{escaped\\} brackets', '')
-# >>> l2m.tex_token('{group followed by closing bracket}} from outer group')
+# >>> tex_token('{group followed by closing bracket}} from outer group')
# ('group followed by closing bracket', '} from outer group')
-# >>> l2m.tex_token(' {skip leading whitespace}')
+# >>> tex_token(' {skip leading whitespace}')
# ('skip leading whitespace', '')
-# >>> l2m.tex_token(' \\skip{leading whitespace}')
+# >>> tex_token(' \\skip{leading whitespace}')
# ('\\skip', '{leading whitespace}')
-# >>> l2m.tex_token('\\skip whitespace after macro name')
+# >>> tex_token('\\skip whitespace after macro name')
# ('\\skip', 'whitespace after macro name')
-# >>> l2m.tex_token('') # empty string.
+# >>> tex_token('') # empty string.
# ('', '')
@@ -527,8 +574,8 @@
Return a MathML-tree parsed from `string`.
Set `inline` to False for displayed math.
- >>> l2m.parse_latex_math('\\alpha')
- math(mrow(mi('α')), xmlns='http://www.w3.org/1998/Math/MathML')
+ >>> parse_latex_math('\\alpha')
+ math(mi('α'), xmlns='http://www.w3.org/1998/Math/MathML')
"""
@@ -537,8 +584,8 @@
# Set up: tree is the whole tree and node is the current element.
if inline:
- node = mrow()
- tree = math(node, inline=True)
+ node = math(inline=True)
+ tree = node
else:
# block: emulate align* environment with a math table
node = mtd()
@@ -550,7 +597,7 @@
c, string = string[0], string[1:]
if c == ' ':
- continue
+ continue # whitespace is ignored in LaTeX math mode
if c == '\\': # start of a LaTeX macro
cmdname, string = tex_cmdname(string)
@@ -561,7 +608,10 @@
node = node.append(mn(c))
elif c in "/()[]|":
node = node.append(mo(c, stretchy='false'))
- elif c in "+-*=<>,.!?':;@":
+ # use dedicated mathematical operator characters
+ elif c in anomalous_chars:
+ node = node.append(mo(anomalous_chars[c]))
+ elif c in "+*=<>,.!?';@":
node = node.append(mo(c))
elif c == '_':
child = node.delete_child()
@@ -603,12 +653,15 @@
# Test:
-# >>> l2m.parse_latex_math(' \\sqrt{ \\alpha}')
-# math(mrow(msqrt(mrow(mi('α')))), xmlns='http://www.w3.org/1998/Math/MathML')
-# >>> l2m.parse_latex_math('\\alpha', inline=False)
+# >>> parse_latex_math(' \\sqrt{ \\alpha}')
+# math(msqrt(mi('α')), xmlns='http://www.w3.org/1998/Math/MathML')
+# >>> parse_latex_math('\\alpha', inline=False)
# math(mtable(mtr(mtd(mi('α'))), CLASS='align', displaystyle='true'), xmlns='http://www.w3.org/1998/Math/MathML', display='block')
+# >>> parse_latex_math('\\sqrt 2 \\ne 3')
+# math(msqrt(mn('2')), mo('≠'), mn('3'), xmlns='http://www.w3.org/1998/Math/MathML')
+# >>> parse_latex_math('\\sqrt{2 + 3} < 3')
+# math(msqrt(mn('2'), mo('+'), mn('3')), mo('<'), mn('3'), xmlns='http://www.w3.org/1998/Math/MathML')
-
def handle_keyword(name, node, string):
"""Process LaTeX macro `name` followed by `string`.
@@ -615,9 +668,9 @@
If needed, parse `string` for macro argument.
Return updated current node and remainder:
- >>> l2m.handle_keyword('hbar', l2m.math(), r' \frac')
+ >>> handle_keyword('hbar', math(), r' \frac')
(math(mi('ℏ')), ' \\frac')
- >>> l2m.handle_keyword('hspace', l2m.math(), r'{1ex} (x)')
+ >>> handle_keyword('hspace', math(), r'{1ex} (x)')
(math(mspace(width='1ex')), ' (x)')
"""
@@ -637,20 +690,31 @@
node = node.append(mi(letters[name]))
return node, string
- if name in functions:
+ if (name in functions or name == 'operatorname'):
# use <mi> followed by invisible function applicator character
# (see https://www.w3.org/TR/MathML3/chapter3.html#presm.mi)
- node = node.append(mi(name))
- node = node.append(mo('⁡'))
- return node, string
+ if name == 'operatorname':
+ # custom function name ``\operatorname{abs}(x)``
+ arg, string = tex_token(string)
+ identifier = mi(arg, mathvariant='normal')
+ else:
+ identifier = mi(functions[name])
+ # functions with embellished names
+ if name == 'varliminf': # \underline\lim
+ identifier = munder([identifier, mo(u'_')], accent='false')
+ elif name == 'varlimsup': # \overline\lim
+ identifier = mover([identifier, mo(u'¯')], accent='false')
+ elif name == 'varprojlim': # \underleftarrow\lim
+ identifier = munder([identifier, mo(u'\u2190')], accent='false')
+ elif name == 'varinjlim': # \underrightarrow\lim
+ identifier = munder([identifier, mo(u'\u2192')], accent='false')
- if name == 'operatorname':
- # custom function name ``\operatorname{abs}(x)``
- arg, string = tex_token(string)
- node = node.append(mi(arg, mathvariant='normal'))
+ node = node.append(identifier)
+ # TODO: only add ApplyFunction when appropriate (not \sin ^2(x), say)
node = node.append(mo('⁡')) # '\u2061'
return node, string
+
if name in math_alphabets:
arg, remainder = tex_token(string)
if arg[0] == '\\':
@@ -665,6 +729,8 @@
kwargs = {'style': 'font-weight: bold'}
else:
kwargs = {'mathvariant': math_alphabets[name]}
+ if name == 'mathscr':
+ kwargs['style'] = 'font-family: STIX' # alternative script letter shapes
# one symbol (single letter, name, or number)
if arg.isalpha():
@@ -685,10 +751,26 @@
# operator, fence, or separator -> <mo>
- if name == 'colon': # "normal" colon, not binary operator
+ if name in left_delimiters: # opening delimiters
+ node = node.append(mo(left_delimiters[name], rspace='0'))
+ return node, string
+
+ if name in right_delimiters: # closing delimiters
+ node = node.append(mo(right_delimiters[name], lspace='0', ))
+ return node, string
+
+ if name == 'colon': # trailing punctuation, not binary relation
node = node.append(mo(':', lspace='0', rspace='0.28em'))
return node, string
+ if name in thick_operators:
+ node = node.append(mo(thick_operators[name], style='font-weight: bold'))
+ return node, string
+
+ if name in small_operators:
+ node = node.append(mo(small_operators[name], mathsize='75%'))
+ return node, string
+
if name in operators:
node = node.append(mo(operators[name]))
return node, string
@@ -714,7 +796,7 @@
try:
node = node.append(mo(negatables[arg]))
except KeyError:
- raise SyntaxError(u'Expected something to negate: "\\not ..."!')
+ raise SyntaxError(u'"\\not: Cannot negate: %s!'%arg)
return node, string
# arbitrary text (usually comments) -> <mtext>
@@ -741,7 +823,11 @@
# ==================================
if name == 'sqrt':
- sqrt = msqrt()
+ if string.startswith('{'): # argument is a group
+ string = string[1:] # mrow implied, skip opening bracket
+ sqrt = msqrt()
+ else: # no group, enclose only one element
+ sqrt = msqrt(nchildren=1)
node.append(sqrt)
return sqrt, string
@@ -757,12 +843,21 @@
node = entry
return node, string
- if name in over:
- ovr = mover(mo(over[name]), reversed=True)
+ if name in over or name in wideover:
+ try:
+ ovr = mover(mo(over[name]), reversed=True)
+ except KeyError:
+ ovr = mover(mo(wideover[name]), reversed=True, accent='false')
node.append(ovr)
node = ovr
return node, string
+ if name in wideunder:
+ ovr = munder(mo(wideunder[name]), reversed=True, accent='false')
+ node.append(ovr)
+ node = ovr
+ return node, string
+
if name == 'begin':
env_name, string = tex_token(string)
if env_name == 'matrix':
@@ -794,33 +889,36 @@
raise SyntaxError(u'Unknown LaTeX command: ' + name)
-# >>> l2m.handle_keyword('left', l2m.math(), '[a\\right]')
+# >>> handle_keyword('left', math(), '[a\\right]')
# (mrow(mo('[')), 'a\\right]')
-# >>> l2m.handle_keyword('left', l2m.math(), '. a)') # emtpy \left
+# >>> handle_keyword('left', math(), '. a)') # emtpy \left
# (mrow(), ' a)')
-# >>> l2m.handle_keyword('left', l2m.math(), '\\uparrow a)') # cmd
+# >>> handle_keyword('left', math(), '\\uparrow a)') # cmd
# (mrow(mo('↑')), 'a)')
-# >>> l2m.handle_keyword('not', l2m.math(), '\\equiv \\alpha)') # cmd
+# >>> handle_keyword('not', math(), '\\equiv \\alpha)') # cmd
# (math(mo('≢')), '\\alpha)')
-# >>> l2m.handle_keyword('text', l2m.math(), '{ for } i>0') # group
+# >>> handle_keyword('text', math(), '{ for } i>0') # group
# (math(mtext(' for ')), ' i>0')
-# >>> l2m.handle_keyword('text', l2m.math(), '{B}T') # group
+# >>> handle_keyword('text', math(), '{B}T') # group
# (math(mtext('B')), 'T')
-# >>> l2m.handle_keyword('text', l2m.math(), '{number of apples}}') # group
+# >>> handle_keyword('text', math(), '{number of apples}}') # group
# (math(mtext('number of apples')), '}')
-# >>> l2m.handle_keyword('text', l2m.math(), 'i \\sin(x)') # single char
+# >>> handle_keyword('text', math(), 'i \\sin(x)') # single char
# (math(mtext('i')), ' \\sin(x)')
-# >>> l2m.handle_keyword('sin', l2m.math(), '(\\alpha)')
+# >>> handle_keyword('sin', math(), '(\\alpha)')
# (math(mi('sin'), mo('⁡')), '(\\alpha)')
-# >>> l2m.handle_keyword('sin', l2m.math(), ' \\alpha')
+# >>> handle_keyword('sin', math(), ' \\alpha')
# (math(mi('sin'), mo('⁡')), ' \\alpha')
-# >>> l2m.handle_keyword('operatorname', l2m.math(), '{abs}(x)')
+# >>> handle_keyword('operatorname', math(), '{abs}(x)')
# (math(mi('abs', mathvariant='normal'), mo('⁡')), '(x)')
-# >>> l2m.handle_keyword('mathrm', l2m.math(), '\\alpha')
+# >>> handle_keyword('mathrm', math(), '\\alpha')
# (math(mi('α', mathvariant='normal')), '')
-# >>> l2m.handle_keyword('mathrm', l2m.math(), '{out} = 3')
+# >>> handle_keyword('mathrm', math(), '{out} = 3')
# (math(mi('out', mathvariant='normal')), ' = 3')
+# >>> handle_keyword('overline', math(), '{981}')
+# (mover(mo('¯'), accent='false'), '{981}')
+
def tex2mathml(tex_math, inline=True):
"""Return string with MathML code corresponding to `tex_math`.
Modified: trunk/docutils/docutils/utils/math/tex2unichar.py
===================================================================
--- trunk/docutils/docutils/utils/math/tex2unichar.py 2021-05-20 12:22:05 UTC (rev 8747)
+++ trunk/docutils/docutils/utils/math/tex2unichar.py 2021-05-20 12:22:27 UTC (rev 8748)
@@ -1,4 +1,4 @@
-# -*- coding: utf-8 -*-
+# -*- coding: utf8 -*-
# LaTeX math to Unicode symbols translation dictionaries.
# Generated with ``write_tex2unichar.py`` from the data in
@@ -7,30 +7,30 @@
# Includes commands from: wasysym, stmaryrd, mathdots, mathabx, esint, bbold, amsxtra, amsmath, amssymb, standard LaTeX
mathaccent = {
- 'acute': u'\u0301', # x́ COMBINING ACUTE ACCENT
- 'bar': u'\u0304', # x̄ COMBINING MACRON
- 'breve': u'\u0306', # x̆ COMBINING BREVE
- 'check': u'\u030c', # x̌ COMBINING CARON
+ 'acute': u'\u0301', # ́ COMBINING ACUTE ACCENT
+ 'bar': u'\u0304', # ̄ COMBINING MACRON
+ 'breve': u'\u0306', # ̆ COMBINING BREVE
+ 'check': u'\u030c', # ̌ COMBINING CARON
'ddddot': u'\u20dc', # x⃜ COMBINING FOUR DOTS ABOVE
'dddot': u'\u20db', # x⃛ COMBINING THREE DOTS ABOVE
- 'ddot': u'\u0308', # ẍ COMBINING DIAERESIS
- 'dot': u'\u0307', # ẋ COMBINING DOT ABOVE
- 'grave': u'\u0300', # x̀ COMBINING GRAVE ACCENT
- 'hat': u'\u0302', # x̂ COMBINING CIRCUMFLEX ACCENT
- 'mathring': u'\u030a', # x̊ COMBINING RING ABOVE
- 'not': u'\u0338', # x̸ COMBINING LONG SOLIDUS OVERLAY
+ 'ddot': u'\u0308', # ̈ COMBINING DIAERESIS
+ 'dot': u'\u0307', # ̇ COMBINING DOT ABOVE
+ 'grave': u'\u0300', # ̀ COMBINING GRAVE ACCENT
+ 'hat': u'\u0302', # ̂ COMBINING CIRCUMFLEX ACCENT
+ 'mathring': u'\u030a', # ̊ COMBINING RING ABOVE
+ 'not': u'\u0338', # ̸ COMBINING LONG SOLIDUS OVERLAY
'overleftarrow': u'\u20d6', # x⃖ COMBINING LEFT ARROW ABOVE
'overleftrightarrow': u'\u20e1', # x⃡ COMBINING LEFT RIGHT ARROW ABOVE
- 'overline': u'\u0305', # x̅ COMBINING OVERLINE
+ 'overline': u'\u0305', # ̅ COMBINING OVERLINE
'overrightarrow': u'\u20d7', # x⃗ COMBINING RIGHT ARROW ABOVE
- 'tilde': u'\u0303', # x̃ COMBINING TILDE
- 'underbar': u'\u0331', # x̱ COMBINING MACRON BELOW
+ 'tilde': u'\u0303', # ̃ COMBINING TILDE
+ 'underbar': u'\u0331', # ̱ COMBINING MACRON BELOW
'underleftarrow': u'\u20ee', # x⃮ COMBINING LEFT ARROW BELOW
- 'underline': u'\u0332', # x̲ COMBINING LOW LINE
+ 'underline': u'\u0332', # ̲ COMBINING LOW LINE
'underrightarrow': u'\u20ef', # x⃯ COMBINING RIGHT ARROW BELOW
'vec': u'\u20d7', # x⃗ COMBINING RIGHT ARROW ABOVE
- 'widehat': u'\u0302', # x̂ COMBINING CIRCUMFLEX ACCENT
- 'widetilde': u'\u0303', # x̃ COMBINING TILDE
+ 'widehat': u'\u0302', # ̂ COMBINING CIRCUMFLEX ACCENT
+ 'widetilde': u'\u0303', # ̃ COMBINING TILDE
}
mathalpha = {
'Bbbk': u'\U0001d55c', # 𝕜 MATHEMATICAL DOUBLE-STRUCK SMALL K
@@ -54,7 +54,7 @@
'chi': u'\u03c7', # χ GREEK SMALL LETTER CHI
'daleth': u'\u2138', # ℸ DALET SYMBOL
'delta': u'\u03b4', # δ GREEK SMALL LETTER DELTA
- 'digamma': u'\u03dc', # Ϝ GREEK LETTER DIGAMMA
+ 'digamma': u'\u03dd', # ϝ GREEK SMALL LETTER DIGAMMA
'ell': u'\u2113', # ℓ SCRIPT SMALL L
'epsilon': u'\u03f5', # ϵ GREEK LUNATE EPSILON SYMBOL
'eta': u'\u03b7', # η GREEK SMALL LETTER ETA
@@ -91,7 +91,7 @@
'varUpsilon': u'\U0001d6f6', # 𝛶 MATHEMATICAL ITALIC CAPITAL UPSILON
'varXi': u'\U0001d6ef', # 𝛯 MATHEMATICAL ITALIC CAPITAL XI
'varepsilon': u'\u03b5', # ε GREEK SMALL LETTER EPSILON
- 'varkappa': u'\U0001d718', # 𝜘 MATHEMATICAL ITALIC KAPPA SYMBOL
+ 'varkappa': u'\u03f0', # ϰ GREEK KAPPA SYMBOL
'varphi': u'\u03c6', # φ GREEK SMALL LETTER PHI
'varpi': u'\u03d6', # ϖ GREEK PI SYMBOL
'varrho': u'\u03f1', # ϱ GREEK RHO SYMBOL
@@ -110,10 +110,12 @@
'amalg': u'\u2a3f', # ⨿ AMALGAMATION OR COPRODUCT
'ast': u'\u2217', # ∗ ASTERISK OPERATOR
'barwedge': u'\u22bc', # ⊼ NAND
+ 'bigcirc': u'\u25ef', # ◯ LARGE CIRCLE
'bigtriangledown': u'\u25bd', # ▽ WHITE DOWN-POINTING TRIANGLE
'bigtriangleup': u'\u25b3', # △ WHITE UP-POINTING TRIANGLE
'bindnasrepma': u'\u214b', # ⅋ TURNED AMPERSAND
'blacklozenge': u'\u29eb', # ⧫ BLACK LOZENGE
+ 'blacktriangle': u'\u25b4', # ▴ BLACK UP-POINTING SMALL TRIANGLE
'blacktriangledown': u'\u25be', # ▾ BLACK DOWN-POINTING SMALL TRIANGLE
'blacktriangleleft': u'\u25c2', # ◂ BLACK LEFT-POINTING SMALL TRIANGLE
'blacktriangleright': u'\u25b8', # ▸ BLACK RIGHT-POINTING SMALL TRIANGLE
@@ -128,7 +130,7 @@
'boxplus': u'\u229e', # ⊞ SQUARED PLUS
'boxslash': u'\u29c4', # ⧄ SQUARED RISING DIAGONAL SLASH
'boxtimes': u'\u22a0', # ⊠ SQUARED TIMES
- 'bullet': u'\u2219', # ∙ BULLET OPERATOR
+ 'bullet': u'\u2022', # • BULLET
'cap': u'\u2229', # ∩ INTERSECTION
'cdot': u'\u22c5', # ⋅ DOT OPERATOR
'circ': u'\u2218', # ∘ RING OPERATOR
@@ -320,9 +322,12 @@
'dasharrow': u'\u21e2', # ⇢ RIGHTWARDS DASHED ARROW
'dashleftarrow': u'\u21e0', # ⇠ LEFTWARDS DASHED ARROW
'dashrightarrow': u'\u21e2', # ⇢ RIGHTWARDS DASHED ARROW
+ 'diagdown': u'\u27cd', # ⟍
+ 'diagup': u'\u27cb', # ⟋
'diameter': u'\u2300', # ⌀ DIAMETER SIGN
'diamondsuit': u'\u2662', # ♢ WHITE DIAMOND SUIT
'earth': u'\u2641', # ♁ EARTH
+ 'emptyset': u'\u2205', # ∅ EMPTY SET
'exists': u'\u2203', # ∃ THERE EXISTS
'female': u'\u2640', # ♀ FEMALE SIGN
'flat': u'\u266d', # ♭ MUSIC FLAT SIGN
@@ -333,6 +338,7 @@
'girl': u'\u2640', # ♀ FEMALE SIGN
'heartsuit': u'\u2661', # ♡ WHITE HEART SUIT
'infty': u'\u221e', # ∞ INFINITY
+ 'invdiameter': u'\u2349', # ⍉ APL FUNCTIONAL SYMBOL CIRCLE BACKSLASH
'invneg': u'\u2310', # ⌐ REVERSED NOT SIGN
'jupiter': u'\u2643', # ♃ JUPITER
'ldots': u'\u2026', # … HORIZONTAL ELLIPSIS
@@ -386,7 +392,7 @@
'twonotes': u'\u266b', # ♫ BEAMED EIGHTH NOTES
'uranus': u'\u2645', # ♅ URANUS
'varEarth': u'\u2641', # ♁ EARTH
- 'varnothing': u'\u2205', # ∅ EMPTY SET
+ 'varnothing': u'\u2300', # ⌀ DIAMETER SIGN
'virgo': u'\u264d', # ♍ VIRGO
'wasylozenge': u'\u2311', # ⌑ SQUARE LOZENGE
'wasytherefore': u'\u2234', # ∴ THEREFORE
@@ -397,7 +403,6 @@
'wideparen': u'\u23dc', # ⏜ TOP PARENTHESIS
}
mathradical = {
- 'sqrt': u'\u221a', # √ SQUARE ROOT
'sqrt[3]': u'\u221b', # ∛ CUBE ROOT
'sqrt[4]': u'\u221c', # ∜ FOURTH ROOT
}
@@ -476,6 +481,7 @@
'gg': u'\u226b', # ≫ MUCH GREATER-THAN
'ggcurly': u'\u2abc', # ⪼ DOUBLE SUCCEEDS
'ggg': u'\u22d9', # ⋙ VERY MUCH GREATER-THAN
+ 'gggtr': u'\u22d9', # ⋙ VERY MUCH GREATER-THAN
'gnapprox': u'\u2a8a', # ⪊ GREATER-THAN AND NOT APPROXIMATE
'gneq': u'\u2a88', # ⪈ GREATER-THAN AND SINGLE-LINE NOT EQUAL TO
'gneqq': u'\u2269', # ≩ GREATER-THAN BUT NOT EQUAL TO
@@ -523,6 +529,7 @@
'll': u'\u226a', # ≪ MUCH LESS-THAN
'llcurly': u'\u2abb', # ⪻ DOUBLE PRECEDES
'lll': u'\u22d8', # ⋘ VERY MUCH LESS-THAN
+ 'llless': u'\u22d8', # ⋘ VERY MUCH LESS-THAN
'lnapprox': u'\u2a89', # ⪉ LESS-THAN AND NOT APPROXIMATE
'lneq': u'\u2a87', # ⪇ LESS-THAN AND SINGLE-LINE NOT EQUAL TO
'lneqq': u'\u2268', # ≨ LESS-THAN BUT NOT EQUAL TO
@@ -585,6 +592,7 @@
'preccurlyeq': u'\u227c', # ≼ PRECEDES OR EQUAL TO
'preceq': u'\u2aaf', # ⪯ PRECEDES ABOVE SINGLE-LINE EQUALS SIGN
'precnapprox': u'\u2ab9', # ⪹ PRECEDES ABOVE NOT ALMOST EQUAL TO
+ 'precneqq': u'\u2ab5', # ⪵ PRECEDES ABOVE NOT EQUAL TO
'precnsim': u'\u22e8', # ⋨ PRECEDES BUT NOT EQUIVALENT TO
'precsim': u'\u227e', # ≾ PRECEDES OR EQUIVALENT TO
'propto': u'\u221d', # ∝ PROPORTIONAL TO
@@ -623,6 +631,7 @@
'succcurlyeq': u'\u227d', # ≽ SUCCEEDS OR EQUAL TO
'succeq': u'\u2ab0', # ⪰ SUCCEEDS ABOVE SINGLE-LINE EQUALS SIGN
'succnapprox': u'\u2aba', # ⪺ SUCCEEDS ABOVE NOT ALMOST EQUAL TO
+ 'succneqq': u'\u2ab6', # ⪶ SUCCEEDS ABOVE NOT EQUAL TO
'succnsim': u'\u22e9', # ⋩ SUCCEEDS BUT NOT EQUIVALENT TO
'succsim': u'\u227f', # ≿ SUCCEEDS OR EQUIVALENT TO
'supset': u'\u2283', # ⊃ SUPERSET OF
Modified: trunk/docutils/test/functional/expected/math_output_mathml.html
===================================================================
--- trunk/docutils/test/functional/expected/math_output_mathml.html 2021-05-20 12:22:05 UTC (rev 8747)
+++ trunk/docutils/test/functional/expected/math_output_mathml.html 2021-05-20 12:22:27 UTC (rev 8748)
@@ -14,31 +14,27 @@
<p>Docutils supports inline math with the prefix or postfix <span class="docutils literal">:math:</span>
role specificator, <math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mi>n</mi><mo>!</mo><mo>+</mo><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo>
- <msubsup>
- <mi>x</mi><mi>n</mi><mn>2</mn>
- </msubsup>
- <mo stretchy="false">)</mo>
- </mrow>
+ <mi>n</mi><mo>!</mo><mo>+</mo><mi>sin</mi><mo>⁡</mo><mo stretchy="false">(</mo>
+ <msubsup>
+ <mi>x</mi><mi>n</mi><mn>2</mn>
+ </msubsup>
+ <mo stretchy="false">)</mo>
</math> and <math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <msub>
- <mi>A</mi><mtext>c</mtext>
- </msub>
- <mo>=</mo>
- <mfrac>
- <mrow>
- <mi>π</mi>
- </mrow>
- <mrow>
- <mn>4</mn>
- </mrow>
- </mfrac>
- <msup>
- <mi>d</mi><mn>2</mn>
- </msup>
- </mrow>
+ <msub>
+ <mi>A</mi><mtext>c</mtext>
+ </msub>
+ <mo>=</mo>
+ <mfrac>
+ <mrow>
+ <mi>π</mi>
+ </mrow>
+ <mrow>
+ <mn>4</mn>
+ </mrow>
+ </mfrac>
+ <msup>
+ <mi>d</mi><mn>2</mn>
+ </msup>
</math>, as well as displayed math via the
<cite>math</cite> directive:</p>
<div>
@@ -132,9 +128,7 @@
</math>
</div>
<p>is <math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mo stretchy="false">|</mo><mi mathvariant="bold">M</mi><mo stretchy="false">|</mo><mo>=</mo><mi>a</mi><mi>d</mi><mo>-</mo><mi>b</mi><mi>c</mi>
- </mrow>
+ <mo stretchy="false">|</mo><mi mathvariant="bold">M</mi><mo stretchy="false">|</mo><mo>=</mo><mi>a</mi><mi>d</mi><mo>−</mo><mi>b</mi><mi>c</mi>
</math>.</p>
<p>More than one display math block can be put in one math directive.
For example, the following sum and integral with limits:</p>
@@ -220,9 +214,7 @@
</math>
</div>
<p>with the <em>wave function</em> <math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mi class="capital-greek">Ψ</mi>
- </mrow>
+ <mi class="capital-greek">Ψ</mi>
</math>, describes how the quantum state of a
physical system changes in time.</p>
<dl>
@@ -235,127 +227,103 @@
</colgroup>
<tbody>
<tr><td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>a</mi>
- </mrow>
- <mo>´</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>a</mi>
+ </mrow>
+ <mo>´</mo>
+ </mover>
</math> <span class="docutils literal">\acute{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>t</mi>
- </mrow>
- <mo>˙</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>t</mi>
+ </mrow>
+ <mo>·</mo>
+ </mover>
</math> <span class="docutils literal">\dot{t}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>γ</mi>
- </mrow>
- <mo>^</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>γ</mi>
+ </mrow>
+ <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">
- <mrow>
- <mover>
- <mrow>
- <mi>a</mi>
- </mrow>
- <mo>`</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>a</mi>
+ </mrow>
+ <mo>`</mo>
+ </mover>
</math> <span class="docutils literal">\grave{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>t</mi>
- </mrow>
- <mo>¨</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>t</mi>
+ </mrow>
+ <mo>··</mo>
+ </mover>
</math> <span class="docutils literal">\ddot{t}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>α</mi>
- </mrow>
- <mo>˜</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>α</mi>
+ </mrow>
+ <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">
- <mrow>
- <mover>
- <mrow>
- <mi>x</mi>
- </mrow>
- <mo>˘</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>x</mi>
+ </mrow>
+ <mo>˘</mo>
+ </mover>
</math> <span class="docutils literal">\breve{x}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>t</mi>
- </mrow>
- <mo>⃛</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>t</mi>
+ </mrow>
+ <mo>···</mo>
+ </mover>
</math> <span class="docutils literal">\dddot{t}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>ı</mi>
- </mrow>
- <mo>⃗</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>ı</mi>
+ </mrow>
+ <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">
- <mrow>
- <mover>
- <mrow>
- <mi>a</mi>
- </mrow>
- <mo>ˇ</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>a</mi>
+ </mrow>
+ <mo>ˇ</mo>
+ </mover>
</math> <span class="docutils literal">\check{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>a</mi>
- </mrow>
- <mo>¯</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>a</mi>
+ </mrow>
+ <mo>-</mo>
+ </mover>
</math> <span class="docutils literal">\bar{a}</span></p></td>
<td><p><math xmlns="http://www.w3.org/1998/Math/MathML">
- <mrow>
- <mover>
- <mrow>
- <mi>R</mi>
- </mrow>
- <mo>⃗</mo>
- </mover>
- </mrow>
+ <mover>
+ <mrow>
+ <mi>R</mi>
+ </mrow>
+ <mo>⃗</mo>
+ </mover>
</math> <span class="docutils literal">\vec{R}</span></p></td>
</tr>
</tbody>
@@ -403,7 +371,7 @@
<munderover>
<mo>∫</mo>
<mrow>
- <mo>-</mo><mo>∞</mo>
+ <mo>−</mo><mo>∞</mo>
</mrow>
<mrow>
<mo>∞</mo>
@@ -432,7 +400,7 @@
<munderover>
<mo>∫</mo>
<mrow>
- <mo>-</mo><mo>∞</mo>
+ <mo>−</mo><mo>∞</mo>
</mrow>
<mrow>
<mo>∞</mo>
@@ -480,7 +448,7 @@
<msub>
<mi>s</mi><mi>δ</mi>
</msub>
- <mo stretchy="false">(</mo><mi>x</mi><mo>-</mo><mi>x</mi><mo>'</mo><mo stretchy="false">)</mo>
+ <mo stretchy="false">(</mo><mi>x</mi><mo>−</mo><mi>x</mi><mo>'</mo><mo stretchy="false">)</mo>
</mtd>
</mtr>
<mtr>
@@ -498,7 +466,7 @@
<msub>
<mi>s</mi><mi>δ</mi>
</msub>
- <mo stretchy="false">(</mo><mi>x</mi><mo>-</mo><mi>x</mi><mo>'</mo><mo stretchy="false">)</mo><mi mathvariant="normal">d</mi><mi>x</mi><mo>'</mo>
+ <mo stretchy="false">(</mo><mi>x</mi><mo>−</mo><mi>x</mi><mo>'</mo><mo stretchy="false">)</mo><mi mathvariant="normal">d</mi><mi>x</mi><mo>'</mo>
</mtd>
</mtr>
</mtable>
@@ -516,7 +484,7 @@
<mtable>
<mtr>
<mtd>
- <mo>-</mo><mn>1</mn>
+ <mo>−</mo><mn>1</mn>
</mtd>
<mtd>
<mi>x</mi><mo><</mo><mn>0</mn>
Modified: trunk/docutils/test/functional/input/data/comprehensive-math-test.txt
===================================================================
--- trunk/docutils/test/functional/input/data/comprehensive-math-test.txt 2021-05-20 12:22:05 UTC (rev 8747)
+++ trunk/docutils/test/functional/input/data/comprehensive-math-test.txt 2021-05-20 12:22:27 UTC (rev 8748)
@@ -136,23 +136,35 @@
.. class:: colwidths-auto
=========== ============= =========== ============= ============== ================
- `\acute{x}` ``\acute{x}`` `\dot{x}` ``\dot{x}`` `\hat{H}` ``\hat{H}``
- `\bar{v}` ``\bar{v}`` `\ddot{x}` ``\ddot{x}`` `\mathring{x}` ``\mathring{x}``
- `\breve{x}` ``\breve{x}`` `\dddot{x}` ``\dddot{x}`` `\tilde{n}` ``\tilde{n}``
- `\check{x}` ``\check{x}`` `\grave{x}` ``\grave{x}`` `\vec{R}` ``\vec{R}``
+ `\acute{x}` ``\acute{x}`` `\dot{t}` ``\dot{t}`` `\hat{H}` ``\hat{H}``
+ `\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}``
=========== ============= =========== ============= ============== ================
When adding an accent to an i or j in math, dotless variants can be
obtained with ``\imath`` and ``\jmath``: `\bar \imath`, `\hat{\jmath}`
+(MathML drops the dot automatically).
-alphabets
----------
+For adornment that span multiple symbols, see `top and bottom
+embellishments`_.
+
+Font switches
+-------------
+
+TeX’s *math alphabets* correspond to the
+:t:`mathematical alphanumeric symbols` block in Unicode and the
+"mathvariant" `style attribute`__ in MathML. They are “to be used for
+mathematical variables where style variations are important
+semantically”.
+
+__ https://developer.mozilla.org/en-US/docs/Web/MathML/Attribute
+
.. class:: colwidths-auto
=============== ============================ ==========================
command example result
=============== ============================ ==========================
- ``\boldsymbol`` ``\boldsymbol{\alpha + 3}`` `\boldsymbol{\alpha + 3}`
``\mathbf`` ``\mathbf{r}^2=x^2+y^2+z^2`` `\mathbf{r}^2=x^2+y^2+z^2`
``\mathbb`` ``\mathbb{R \subset C}`` `\mathbb{R \subset C}`
``\mathcal`` ``\mathcal{F}f(x)`` `\mathcal{F}f(x)`
@@ -160,16 +172,42 @@
``\mathit`` ``\mathit{\Gamma}`` `\mathit{\Gamma}`
``\mathrm`` ``s_\mathrm{out}`` `s_\mathrm{out}`
``\mathsf`` ``\mathsf x`` `\mathsf x`
- ``\mathtt`` ``\mathtt{0.12}`` `\mathtt{0.12}`
+ ``\mathtt`` ``\mathtt{0.12}`` `\mathtt{0.12}`
=============== ============================ ==========================
-.. with isomath:
- 'mathbfit': 'bold-italic', # isomath
- 'mathsfit': 'sans-serif-italic', # isomath
- 'mathsfbfit': 'sans-serif-bold-italic', # isomath
- with mathrsfs, ... 'mathscr'
+Additional alphabets are defined in LaTeX packages, e.g.
+.. class:: colwidths-auto
+ =========== ============= ======================
+ TeX command LaTeX package MathML "mathvariant"
+ =========== ============= ======================
+ mathbfit isomath_ bold-italic
+ mathsfit isomath_ sans-serif-italic
+ mathsfbfit isomath_ sans-serif-bold-italic
+ mathscr mathrsfs_ script
+ =========== ============= ======================
+.. _isomath: https://www.ctan.org/pkg/isomath
+.. _mathrsfs: https://www.ctan.org/pkg/mathrsfs
+
+This can be used to typeset vector symbols in **bold** *italic*
+in line with the International Standard [ISO-80000-2].
+``\mathbfit{r}^2=x^2+y^2+z^2`` becomes
+
+.. math:: \mathbfit{r}^2=x^2+y^2+z^2.
+
+
+
+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
+mathematical symbol (for other output formats, results are mixed):
+
+.. math::
+ \boldsymbol{abs(x) \pm \alpha \approx 3 \Gamma \quad \forall x \in R}
+
+
+
+
Arrows
------
.. class:: colwidths-auto
@@ -201,27 +239,26 @@
.. class:: colwidths-auto
================== ==================== ================= =================== ================== ====================
- `*` ``*`` `\circledast` ``\circledast`` `\odot` ``\odot``
- `+` ``+`` `\circledcirc` ``\circledcirc`` `\ominus` ``\ominus``
- `-` ``-`` `\circleddash` ``\circleddash`` `\oplus` ``\oplus``
- `:` ``:`` `\cup` ``\cup`` `\oslash` ``\oslash``
- `\Cap` ``\Cap`` `\curlyvee` ``\curlyvee`` `\otimes` ``\otimes``
- `\Cup` ``\Cup`` `\curlywedge` ``\curlywedge`` `\pm` ``\pm``
- `\amalg` ``\amalg`` `\dagger` ``\dagger`` `\rightthreetimes` ``\rightthreetimes``
- `\ast` ``\ast`` `\ddagger` ``\ddagger`` `\rtimes` ``\rtimes``
- `\bigcirc` ``\bigcirc`` `\diamond` ``\diamond`` `\setminus` ``\setminus``
- `\bigtriangledown` ``\bigtriangledown`` `\div` ``\div`` `\smallsetminus` ``\smallsetminus``
- `\bigtriangleup` ``\bigtriangleup`` `\divideontimes` ``\divideontimes`` `\sqcap` ``\sqcap``
- `\boxdot` ``\boxdot`` `\dotplus` ``\dotplus`` `\sqcup` ``\sqcup``
- `\boxminus` ``\boxminus`` `\doublebarwedge` ``\doublebarwedge`` `\star` ``\star``
- `\boxplus` ``\boxplus`` `\gtrdot` ``\gtrdot`` `\times` ``\times``
- `\boxtimes` ``\boxtimes`` `\intercal` ``\intercal`` `\triangleleft` ``\triangleleft``
- `\bullet` ``\bullet`` `\leftthreetimes` ``\leftthreetimes`` `\triangleright` ``\triangleright``
- `\cap` ``\cap`` `\lessdot` ``\lessdot`` `\uplus` ``\uplus``
- `\cdot` ``\cdot`` `\ltimes` ``\ltimes`` `\vee` ``\vee``
- `\centerdot` ``\centerdot`` `\mp` ``\mp`` `\veebar` ``\veebar``
- `\circ` ``\circ`` `\wedge` ``\wedge``
- .. `\wr` ``\wr``
+ `*` ``*`` `\circledast` ``\circledast`` `\ominus` ``\ominus``
+ `+` ``+`` `\circledcirc` ``\circledcirc`` `\oplus` ``\oplus``
+ `-` ``-`` `\circleddash` ``\circleddash`` `\oslash` ``\oslash``
+ `:` ``:`` `\cup` ``\cup`` `\otimes` ``\otimes``
+ `\Cap` ``\Cap`` `\curlyvee` ``\curlyvee`` `\pm` ``\pm``
+ `\Cup` ``\Cup`` `\curlywedge` ``\curlywedge`` `\rightthreetimes` ``\rightthreetimes``
+ `\amalg` ``\amalg`` `\dagger` ``\dagger`` `\rtimes` ``\rtimes``
+ `\ast` ``\ast`` `\ddagger` ``\ddagger`` `\setminus` ``\setminus``
+ `\bigcirc` ``\bigcirc`` `\diamond` ``\diamond`` `\smallsetminus` ``\smallsetminus``
+ `\bigtriangledown` ``\bigtriangledown`` `\div` ``\div`` `\sqcap` ``\sqcap``
+ `\bigtriangleup` ``\bigtriangleup`` `\divideontimes` ``\divideontimes`` `\sqcup` ``\sqcup``
+ `\boxdot` ``\boxdot`` `\dotplus` ``\dotplus`` `\star` ``\star``
+ `\boxminus` ``\boxminus`` `\doublebarwedge` ``\doublebarwedge`` `\times` ``\times``
+ `\boxplus` ``\boxplus`` `\gtrdot` ``\gtrdot`` `\triangleleft` ``\triangleleft``
+ `\boxtimes` ``\boxtimes`` `\intercal` ``\intercal`` `\triangleright` ``\triangleright``
+ `\bullet` ``\bullet`` `\leftthreetimes` ``\leftthreetimes`` `\uplus` ``\uplus``
+ `\cap` ``\cap`` `\lessdot` ``\lessdot`` `\vee` ``\vee``
+ `\cdot` ``\cdot`` `\ltimes` ``\ltimes`` `\veebar` ``\veebar``
+ `\centerdot` ``\centerdot`` `\mp` ``\mp`` `\wedge` ``\wedge``
+ `\circ` ``\circ`` `\odot` ``\odot`` `\wr` ``\wr``
================== ==================== ================= =================== ================== ====================
@@ -264,7 +301,7 @@
`\mho` ``\mho`` `\complement` ``\complement`` `\gimel` ``\gimel`` `\hslash` ``\hslash``
`\Bbbk` ``\Bbbk`` `\Finv` ``\Finv`` `\daleth` ``\daleth`` `\imath` ``\imath``
`\ell` ``\ell`` `\Game` ``\Game`` `\nabla` ``\nabla`` `\partial` ``\partial``
-`\wp` ``\wp``
+`\wp` ``\wp``
======= ========== ============= =============== ========= =========== ========== ============
@@ -365,8 +402,19 @@
Top and bottom embellishments
-----------------------------
-TODO
+Visually similar to accents_ but generally applied to multiple symbols.
+.. class:: colwidths-auto
+
+ ========================== ============================ =========================== =============================
+ `\widetilde{abi}` ``\widetilde{abi}`` `\widehat{abi}` ``\widehat{abi}``
+ `\overline{abi}` ``\overline{abi}`` `\underline{abi}` ``\underline{abi}``
+ `\overbrace{abi}` ``\overbrace{abi}`` `\underbrace{abi}` ``\underbrace{abi}``
+ `\overleftarrow{abi}` ``\overleftarrow{abi}`` `\underleftarrow{abi}` ``\underleftarrow{abi}``
+ `\overrightarrow{abi}` ``\overrightarrow{abi}`` `\underrightarrow{abi}` ``\underrightarrow{abi}``
+ `\overleftrightarrow{abi}` ``\overleftrightarrow{abi}`` `\underleftrightarrow{abi}` ``\underleftrightarrow{abi}``
+ ========================== ============================ =========================== =============================
+
Extensible arrows
-----------------
@@ -416,47 +464,65 @@
* Shorthands for combined named operators
- =============== =================
- `\liminf` ``\liminf``
- `\limsup` ``\limsup``
- `\injlim` ``\injlim``
- `\projlim` ``\projlim``
- `\varinjlim` ``\varinjlim``
- `\varliminf` ``\varliminf``
- `\varlimsup` ``\varlimsup``
- `\varprojlim` ``\varprojlim``
- =============== =================
+ ========== ============ ============= =============== ==================
+ `\liminf` ``\liminf`` `\varliminf` ``\varliminf`` `\underline{\lim}`
+ `\limsup` ``\limsup`` `\varlimsup` ``\varlimsup`` `\overline{\lim}`
+ `\injlim` ``\injlim`` `varinjlim` ``\varinjlim``
+ `\projlim` ``\projlim`` `varprojlim` ``\varprojlim``
+ ========== ============ ============= =============== ==================
* Implement ``\circledS``? (in short-math-guide.pdf but not in mathematical Unicode characters)
- ``\widetilde{xxx}``
- ``\widehat{xxx}``
-
Tests
==========
+Font changes
+------------
-LICR macros in different alphabets:
+Math alphabet macros change the default alphabet ("mathvariant" in
+MathML), leaving some symbols unchanged:
-.. math::
+:normal: `abs(x) \pm \alpha \approx 3 \Gamma \quad \forall x \in R`
+:mathrm: `\mathrm{abs(x) \pm \alpha \approx 3 \Gamma \quad \forall x \in R}`
+:mathit: `\mathit{abs(x) \pm \alpha \approx 3 \Gamma \quad \forall x \in R}`
+:mathsf: `\mathsf{abs(x) \pm \alpha \approx 3 \Gamma \quad \forall x \in R}`
+:mathbb: `\mathbb{abs(x) \pm \alpha \approx 3 \Gamma \quad \forall x \in R}`
+:mathbf: `\mathbf{abs(x) \pm \alpha \approx 3 \Gamma \quad \forall x \in R`
- \text{normal: } &
- abs(x) \pm \alpha \approx \Gamma \forall x \in R \bigstar \\
- \text{mathrm: } &
- \mathrm{abs(x) \pm \alpha \approx \Gamma \forall x \in R \bigstar} \\
- \text{mathit: } &
- \mathit{abs(x) \pm \alpha \approx \Gamma \forall x \in R \bigstar} \\
- \text{mathsf: } &
- \mathsf{abs(x) \pm \alpha \approx \Gamma \forall x \in R \bigstar} \\
- \text{mathbb: } &
- \mathbb{abs(x) \pm \alpha \approx \Gamma \forall x \in R \bigstar} \\
- \text{mathbf: } &
- \mathbf{abs(x) \pm \alpha \approx \Gamma \forall x \in R \bigstar} \\
- \text{boldsymbol: } &
- \boldsymbol{abs(x) \pm \alpha \approx \Gamma \forall x \in R \bigstar}
-
-All blackboard-bold characters:
+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
+
+Inferred <mrow>s in MathML
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+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.
+
+.. math:: a = \sqrt 2, b = \sqrt{1+x^2}, c = \sqrt\frac{sin(x)}{23}
+
+inline: :math:`a = \sqrt 2, b = \sqrt{1+x^2}, c = \sqrt\frac{sin(x)}{23}`.
+
+Accents vs. embellishments
+--------------------------
+
+MathML drops dots on "i" and "j" with accents:
+
+.. math:: \vec i \ne \overrightarrow i
+ \text{ and } \vec\lim \ne \overrightarrow\lim.
+
+Accents should be nearer to the base (in Firefox 78, it's vice versa!):
+
+.. math:: \vec a \vec l \ne \overrightarrow a \overrightarrow l
+
+ \bar a \bar l \ne \overline a \overline l
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|