Skip to content

Commit 46d9fd6

Browse files
committed
Deprecate various vector-backend-specific mathtext helpers.
A single mathtext "backend" yielding a list of glyphs and rectangles is enough; renderer-specific logic can be implemented in the renderer themselves. (MathtextBackendAgg needs to stay separate because it uses a different font hinting flag.)
1 parent 80848d2 commit 46d9fd6

File tree

9 files changed

+126
-68
lines changed

9 files changed

+126
-68
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Deprecation of various mathtext helpers
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
The ``MathtextBackendPdf``, ``MathtextBackendPs``, ``MathtextBackendSvg``,
4+
and ``MathtextBackendCairo`` classes from the :mod:`.mathtext` module, as
5+
well as the corresponding ``.mathtext_parser`` attributes on ``RendererPdf``,
6+
``RendererPS``, ``RendererSVG``, and ``RendererCairo``, are deprecated. The
7+
``MathtextBackendPath`` class can be used to obtain a list of glyphs and
8+
rectangles in a mathtext expression, and renderer-specific logic should be
9+
directly implemented in the renderer.
10+
11+
``StandardPsFonts.pswriter`` is unused and deprecated.

lib/matplotlib/afm.py

+4
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,10 @@ def get_fontname(self):
469469
"""Return the font name, e.g., 'Times-Roman'."""
470470
return self._header[b'FontName']
471471

472+
@property
473+
def postscript_name(self): # For consistency with FT2Font.
474+
return self.get_fontname()
475+
472476
def get_fullname(self):
473477
"""Return the font full name, e.g., 'Times-Roman'."""
474478
name = self._header.get(b'FullName')

lib/matplotlib/backends/_backend_pdf_ps.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def track(self, font, s):
4545
fname = font.fname
4646
self.used.setdefault(fname, set()).update(map(ord, s))
4747

48+
# Not public, can be removed when pdf/ps merge_used_characters is removed.
4849
def merge(self, other):
4950
"""Update self with a font path to character codepoints."""
5051
for fname, charset in other.items():
@@ -87,7 +88,12 @@ def get_text_width_height_descent(self, s, prop, ismath):
8788
s, fontsize, renderer=self)
8889
return w, h, d
8990
elif ismath:
90-
parse = self.mathtext_parser.parse(s, 72, prop)
91+
# Circular import.
92+
from matplotlib.backends.backend_ps import RendererPS
93+
parse = self._text2path.mathtext_parser.parse(
94+
s, 72, prop,
95+
force_standard_ps_fonts=(isinstance(self, RendererPS)
96+
and mpl.rcParams["ps.useafm"]))
9197
return parse.width, parse.height, parse.depth
9298
elif mpl.rcParams[self._use_afm_rc_name]:
9399
font = self._get_font_afm(prop)

lib/matplotlib/backends/backend_cairo.py

+14-10
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,13 @@ def __init__(self, dpi):
131131
self.gc = GraphicsContextCairo(renderer=self)
132132
self.text_ctx = cairo.Context(
133133
cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1))
134-
self.mathtext_parser = MathTextParser('Cairo')
135134
RendererBase.__init__(self)
136135

136+
@cbook.deprecated("3.4")
137+
@property
138+
def mathtext_parser(self):
139+
return MathTextParser('Cairo')
140+
137141
def set_ctx_from_surface(self, surface):
138142
self.gc.ctx = cairo.Context(surface)
139143
# Although it may appear natural to automatically call
@@ -254,26 +258,25 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
254258

255259
def _draw_mathtext(self, gc, x, y, s, prop, angle):
256260
ctx = gc.ctx
257-
width, height, descent, glyphs, rects = self.mathtext_parser.parse(
258-
s, self.dpi, prop)
261+
width, height, descent, glyphs, rects = \
262+
self._text2path.mathtext_parser.parse(s, self.dpi, prop)
259263

260264
ctx.save()
261265
ctx.translate(x, y)
262266
if angle:
263267
ctx.rotate(np.deg2rad(-angle))
264268

265-
for font, fontsize, s, ox, oy in glyphs:
269+
for font, fontsize, idx, ox, oy in glyphs:
266270
ctx.new_path()
267-
ctx.move_to(ox, oy)
268-
271+
ctx.move_to(ox, -oy)
269272
ctx.select_font_face(
270273
*_cairo_font_args_from_font_prop(ttfFontProperty(font)))
271274
ctx.set_font_size(fontsize * self.dpi / 72)
272-
ctx.show_text(s)
275+
ctx.show_text(chr(idx))
273276

274277
for ox, oy, w, h in rects:
275278
ctx.new_path()
276-
ctx.rectangle(ox, oy, w, h)
279+
ctx.rectangle(ox, -oy - h, w, h)
277280
ctx.set_source_rgb(0, 0, 0)
278281
ctx.fill_preserve()
279282

@@ -290,8 +293,9 @@ def get_text_width_height_descent(self, s, prop, ismath):
290293
return super().get_text_width_height_descent(s, prop, ismath)
291294

292295
if ismath:
293-
dims = self.mathtext_parser.parse(s, self.dpi, prop)
294-
return dims[0:3] # return width, height, descent
296+
width, height, descent, *_ = \
297+
self._text2path.mathtext_parser.parse(s, self.dpi, prop)
298+
return width, height, descent
295299

296300
ctx = self.text_ctx
297301
# problem - scale remembers last setting and font can become

lib/matplotlib/backends/backend_pdf.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -1772,9 +1772,13 @@ def __init__(self, file, image_dpi, height, width):
17721772
super().__init__(width, height)
17731773
self.file = file
17741774
self.gc = self.new_gc()
1775-
self.mathtext_parser = MathTextParser("Pdf")
17761775
self.image_dpi = image_dpi
17771776

1777+
@cbook.deprecated("3.4")
1778+
@property
1779+
def mathtext_parser(self):
1780+
return MathTextParser("Pdf")
1781+
17781782
def finalize(self):
17791783
self.file.output(*self.gc.finalize())
17801784

@@ -2020,9 +2024,8 @@ def _setup_textpos(self, x, y, angle, oldx=0, oldy=0, oldangle=0):
20202024

20212025
def draw_mathtext(self, gc, x, y, s, prop, angle):
20222026
# TODO: fix positioning and encoding
2023-
width, height, descent, glyphs, rects, used_characters = \
2024-
self.mathtext_parser.parse(s, 72, prop)
2025-
self.file._character_tracker.merge(used_characters)
2027+
width, height, descent, glyphs, rects = \
2028+
self._text2path.mathtext_parser.parse(s, 72, prop)
20262029

20272030
# When using Type 3 fonts, we can't use character codes higher
20282031
# than 255, so we use the "Do" command to render those
@@ -2040,7 +2043,9 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
20402043
self.file.output(Op.begin_text)
20412044
prev_font = None, None
20422045
oldx, oldy = 0, 0
2043-
for ox, oy, fontname, fontsize, num, symbol_name in glyphs:
2046+
for font, fontsize, num, ox, oy in glyphs:
2047+
self.file._character_tracker.track(font, chr(num))
2048+
fontname = font.fname
20442049
if is_opentype_cff_font(fontname):
20452050
fonttype = 42
20462051
else:
@@ -2060,7 +2065,8 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
20602065
# If using Type 3 fonts, render all of the multi-byte characters
20612066
# as XObjects using the 'Do' command.
20622067
if global_fonttype == 3:
2063-
for ox, oy, fontname, fontsize, num, symbol_name in glyphs:
2068+
for font, fontsize, num, ox, oy in glyphs:
2069+
fontname = font.fname
20642070
if is_opentype_cff_font(fontname):
20652071
fonttype = 42
20662072
else:
@@ -2072,6 +2078,7 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
20722078
0.001 * fontsize, 0,
20732079
0, 0.001 * fontsize,
20742080
ox, oy, Op.concat_matrix)
2081+
symbol_name = font.get_glyph_name(font.get_char_index(num))
20752082
name = self.file._get_xobject_symbol_name(
20762083
fontname, symbol_name)
20772084
self.file.output(Name(name), Op.use_xobject)

lib/matplotlib/backends/backend_ps.py

+31-12
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import matplotlib as mpl
2121
from matplotlib import cbook, _path
2222
from matplotlib import _text_layout
23+
from matplotlib.afm import AFM
2324
from matplotlib.backend_bases import (
2425
_Backend, _check_savefig_extra_args, FigureCanvasBase, FigureManagerBase,
2526
GraphicsContextBase, RendererBase)
@@ -167,7 +168,11 @@ def __init__(self, width, height, pswriter, imagedpi=72):
167168
self._path_collection_id = 0
168169

169170
self._character_tracker = _backend_pdf_ps.CharacterTracker()
170-
self.mathtext_parser = MathTextParser("PS")
171+
172+
@cbook.deprecated("3.3")
173+
@property
174+
def mathtext_parser(self):
175+
return MathTextParser("PS")
171176

172177
@cbook.deprecated("3.3")
173178
@property
@@ -599,18 +604,32 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
599604
if debugPS:
600605
self._pswriter.write("% mathtext\n")
601606

602-
width, height, descent, pswriter, used_characters = \
603-
self.mathtext_parser.parse(s, 72, prop)
604-
self._character_tracker.merge(used_characters)
607+
width, height, descent, glyphs, rects = \
608+
self._text2path.mathtext_parser.parse(
609+
s, 72, prop, force_standard_ps_fonts=mpl.rcParams["ps.useafm"])
605610
self.set_color(*gc.get_rgb())
606-
thetext = pswriter.getvalue()
607-
self._pswriter.write(f"""\
608-
gsave
609-
{x:f} {y:f} translate
610-
{angle:f} rotate
611-
{thetext}
612-
grestore
613-
""")
611+
self._pswriter.write(
612+
f"gsave\n"
613+
f"{x:f} {y:f} translate\n"
614+
f"{angle:f} rotate\n")
615+
lastfont = None
616+
for font, fontsize, num, ox, oy in glyphs:
617+
self._character_tracker.track(font, chr(num))
618+
if (font.postscript_name, fontsize) != lastfont:
619+
lastfont = font.postscript_name, fontsize
620+
self._pswriter.write(
621+
f"/{font.postscript_name} findfont\n"
622+
f"{fontsize} scalefont\n"
623+
f"setfont\n")
624+
symbol_name = (
625+
font.get_name_char(chr(num)) if isinstance(font, AFM) else
626+
font.get_glyph_name(font.get_char_index(num)))
627+
self._pswriter.write(
628+
f"{ox:f} {oy:f} moveto\n"
629+
f"/{symbol_name} glyphshow\n")
630+
for ox, oy, w, h in rects:
631+
self._pswriter.write(f"{ox} {oy} {w} {h} rectfill\n")
632+
self._pswriter.write("grestore\n")
614633

615634
def draw_gouraud_triangle(self, gc, points, colors, trans):
616635
self.draw_gouraud_triangles(gc, points.reshape((1, 3, 2)),

lib/matplotlib/backends/backend_svg.py

+22-22
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ def __init__(self, width, height, svgwriter, basename=None, image_dpi=72,
294294
self._has_gouraud = False
295295
self._n_gradients = 0
296296
self._fonts = OrderedDict()
297-
self.mathtext_parser = MathTextParser('SVG')
298297

299298
RendererBase.__init__(self)
300299
self._glyph_map = dict()
@@ -312,6 +311,11 @@ def __init__(self, width, height, svgwriter, basename=None, image_dpi=72,
312311
self._write_metadata(metadata)
313312
self._write_default_style()
314313

314+
@cbook.deprecated("3.4")
315+
@property
316+
def mathtext_parser(self):
317+
return MathTextParser('SVG')
318+
315319
def finalize(self):
316320
self._write_clips()
317321
self._write_hatches()
@@ -1162,26 +1166,23 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None):
11621166
else:
11631167
writer.comment(s)
11641168

1165-
width, height, descent, svg_elements, used_characters = \
1166-
self.mathtext_parser.parse(s, 72, prop)
1167-
svg_glyphs = svg_elements.svg_glyphs
1168-
svg_rects = svg_elements.svg_rects
1169-
1170-
attrib = {}
1171-
attrib['style'] = generate_css(style)
1172-
attrib['transform'] = generate_transform([
1173-
('translate', (x, y)),
1174-
('rotate', (-angle,))])
1169+
width, height, descent, glyphs, rects = \
1170+
self._text2path.mathtext_parser.parse(s, 72, prop)
11751171

11761172
# Apply attributes to 'g', not 'text', because we likely have some
11771173
# rectangles as well with the same style and transformation.
1178-
writer.start('g', attrib=attrib)
1174+
writer.start('g',
1175+
style=generate_css(style),
1176+
transform=generate_transform([
1177+
('translate', (x, y)),
1178+
('rotate', (-angle,))]),
1179+
)
11791180

11801181
writer.start('text')
11811182

11821183
# Sort the characters by font, and output one tspan for each.
11831184
spans = OrderedDict()
1184-
for font, fontsize, thetext, new_x, new_y, metrics in svg_glyphs:
1185+
for font, fontsize, thetext, new_x, new_y in glyphs:
11851186
style = generate_css({
11861187
'font-size': short_float_fmt(fontsize) + 'px',
11871188
'font-family': font.family_name,
@@ -1212,15 +1213,14 @@ def _draw_text_as_text(self, gc, x, y, s, prop, angle, ismath, mtext=None):
12121213

12131214
writer.end('text')
12141215

1215-
if len(svg_rects):
1216-
for x, y, width, height in svg_rects:
1217-
writer.element(
1218-
'rect',
1219-
x=short_float_fmt(x),
1220-
y=short_float_fmt(-y + height),
1221-
width=short_float_fmt(width),
1222-
height=short_float_fmt(height)
1223-
)
1216+
for x, y, width, height in rects:
1217+
writer.element(
1218+
'rect',
1219+
x=short_float_fmt(x),
1220+
y=short_float_fmt(-y-1),
1221+
width=short_float_fmt(width),
1222+
height=short_float_fmt(height)
1223+
)
12241224

12251225
writer.end('g')
12261226

0 commit comments

Comments
 (0)