From 9b301a4d4b3a1fc1eac947867c2db75d4839fb0b Mon Sep 17 00:00:00 2001 From: Ratnabali Dutta Date: Wed, 12 Jul 2023 00:17:22 +0530 Subject: [PATCH 1/3] Add substack command for mathtext Co-Authored By: Kyle Sunden --- lib/matplotlib/_mathtext.py | 37 +++++++++++++++++- .../test_mathtext/mathtext1_dejavusans_08.png | Bin 0 -> 6514 bytes lib/matplotlib/tests/test_mathtext.py | 1 + 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_08.png diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index d86385e94589..09b1c969b959 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -18,7 +18,7 @@ Empty, Forward, Literal, NotAny, oneOf, OneOrMore, Optional, ParseBaseException, ParseException, ParseExpression, ParseFatalException, ParserElement, ParseResults, QuotedString, Regex, StringEnd, ZeroOrMore, - pyparsing_common) + pyparsing_common, Group) import matplotlib as mpl from . import cbook @@ -27,6 +27,11 @@ from .font_manager import FontProperties, findfont, get_font from .ft2font import FT2Image, KERNING_DEFAULT +from pyparsing import __version_info__ as pyparsing_version +if pyparsing_version < (3, 0): + from pyparsing import nestedExpr as nested_expr +else: + from pyparsing import nested_expr ParserElement.enablePackrat() _log = logging.getLogger("matplotlib.mathtext") @@ -1865,7 +1870,7 @@ def csnames(group, names): + r"|\\(?:{})(?![A-Za-z])".format( "|".join(map(re.escape, tex2uni))) )("sym").leaveWhitespace() - p.unknown_symbol = Regex(r"\\[A-Za-z]*")("name") + p.unknown_symbol = Regex(r"\\[A-Za-z]+")("name") p.font = csnames("font", self._fontnames) p.start_group = Optional(r"\math" + oneOf(self._fontnames)("font")) + "{" @@ -1925,6 +1930,11 @@ def csnames(group, names): p.text = cmd(r"\text", QuotedString('{', '\\', endQuoteChar="}")) + p.substack = cmd(r"\substack", + nested_expr(opener="{", closer="}", + content=Group(OneOrMore(p.token)) + + ZeroOrMore(Literal("\\\\").suppress()))("parts")) + p.subsuper = ( (Optional(p.placeable)("nucleus") + OneOrMore(oneOf(["_", "^"]) - p.placeable)("subsuper") @@ -1963,6 +1973,7 @@ def csnames(group, names): | p.overline | p.text | p.boldsymbol + | p.substack ) mdelim = r"\middle" - (p.delim("mdelim") | Error("Expected a delimiter")) @@ -2648,3 +2659,25 @@ def boldsymbol(self, s, loc, toks): self.pop_state() return Hlist(hlist) + + def substack(self, s, loc, toks): + parts = toks["parts"] + state = self.get_state() + thickness = state.get_current_underline_thickness() + vlist = [] + + hlist = [Hlist(k) for k in parts[0]] + max_width = max(map(lambda c: c.width, hlist)) + + for sub in hlist: + cp = HCentered([sub]) + cp.hpack(max_width, 'exactly') + vlist.append(cp) + + vlist = [val for pair in zip(vlist, + [Vbox(0, thickness * 2)] * + len(vlist)) for val in pair] + del vlist[-1] + vlt = Vlist(vlist) + result = [Hlist([vlt])] + return result diff --git a/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_08.png b/lib/matplotlib/tests/baseline_images/test_mathtext/mathtext1_dejavusans_08.png new file mode 100644 index 0000000000000000000000000000000000000000..565464062e39fcf9411fbf3a067adb9f98509dcd GIT binary patch literal 6514 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 ZWLNGVu5XV4|Ji_`r?M(C#Ztz>{|7P&!s!42 literal 0 HcmV?d00001 diff --git a/lib/matplotlib/tests/test_mathtext.py b/lib/matplotlib/tests/test_mathtext.py index dab981f29a23..f281b1e47412 100644 --- a/lib/matplotlib/tests/test_mathtext.py +++ b/lib/matplotlib/tests/test_mathtext.py @@ -137,6 +137,7 @@ r'$\boldsymbol{abcde} \boldsymbol{+} \boldsymbol{\Gamma + \Omega} \boldsymbol{01234} \boldsymbol{\alpha * \beta}$', r'$\left\lbrace\frac{\left\lbrack A^b_c\right\rbrace}{\left\leftbrace D^e_f \right\rbrack}\right\rightbrace\ \left\leftparen\max_{x} \left\lgroup \frac{A}{B}\right\rgroup \right\rightparen$', r'$\left( a\middle. b \right)$ $\left( \frac{a}{b} \middle\vert x_i \in P^S \right)$ $\left[ 1 - \middle| a\middle| + \left( x - \left\lfloor \dfrac{a}{b}\right\rfloor \right) \right]$', + r'$\sum_{\substack{k = 1\\ k \neq \lfloor n/2\rfloor}}^{n}P(i,j) \sum_{\substack{i \neq 0\\ -1 \leq i \leq 3\\ 1 \leq j \leq 5}} F^i(x,y) \sum_{\substack{\left \lfloor \frac{n}{2} \right\rfloor}} F(n)$', ] digits = "0123456789" From c6a1bf5b26de11e4c328419d6bd5e87327179e53 Mon Sep 17 00:00:00 2001 From: Ratnabali Dutta Date: Wed, 12 Jul 2023 00:17:22 +0530 Subject: [PATCH 2/3] Add substack command for mathtext Co-Authored By: Kyle Sunden --- lib/matplotlib/_mathtext.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index 09b1c969b959..a80c9f614b4a 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -27,8 +27,8 @@ from .font_manager import FontProperties, findfont, get_font from .ft2font import FT2Image, KERNING_DEFAULT -from pyparsing import __version_info__ as pyparsing_version -if pyparsing_version < (3, 0): +from pyparsing import __version__ as pyparsing_version +if pyparsing_version < '3.0.0': from pyparsing import nestedExpr as nested_expr else: from pyparsing import nested_expr From f780312df190236a3a8e710be1d75ea3d25058f4 Mon Sep 17 00:00:00 2001 From: Ratnabali Dutta Date: Thu, 27 Jul 2023 12:37:17 +0530 Subject: [PATCH 3/3] Fix pyparsing version check Co-authored-by: Kyle Sunden --- lib/matplotlib/_mathtext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/_mathtext.py b/lib/matplotlib/_mathtext.py index a80c9f614b4a..3c937d3fe224 100644 --- a/lib/matplotlib/_mathtext.py +++ b/lib/matplotlib/_mathtext.py @@ -28,7 +28,7 @@ from .ft2font import FT2Image, KERNING_DEFAULT from pyparsing import __version__ as pyparsing_version -if pyparsing_version < '3.0.0': +if tuple(int(x) for x in pyparsing_version.split(".")) < (3, 0, 0): from pyparsing import nestedExpr as nested_expr else: from pyparsing import nested_expr