Drawing.get_imagedata with EPS format fails for some drawings
Issue #66
resolved
In a large production code base, recently (perhaps due to schemdraw
or matplotlib
changes) our EPS Drawing
s now throw an exception when we call get_imagedata()
on them with the EPS
format.
return drawing.get_imagedata(drawing_format.value)
py3.9/lib/python3.9/site-packages/schemdraw/schemdraw.py:405: in get_imagedata
return self.fig.getimage(ext=fmt) # type: ignore
py3.9/lib/python3.9/site-packages/schemdraw/backends/mpl.py:284: in getimage
fig.savefig(output, format=ext, bbox_inches='tight')
py3.9/lib/python3.9/site-packages/matplotlib/figure.py:3272: in savefig
self.canvas.print_figure(fname, **kwargs)
py3.9/lib/python3.9/site-packages/matplotlib/backend_bases.py:2338: in print_figure
result = print_method(
py3.9/lib/python3.9/site-packages/matplotlib/backend_bases.py:2204: in <lambda>
print_method = functools.wraps(meth)(lambda *args, **kwargs: meth(
py3.9/lib/python3.9/site-packages/matplotlib/_api/deprecation.py:410: in wrapper
return func(*inner_args, **inner_kwargs)
py3.9/lib/python3.9/site-packages/matplotlib/backends/backend_ps.py:869: in _print_ps
printer(fmt, outfile, dpi=dpi, dsc_comments=dsc_comments,
py3.9/lib/python3.9/site-packages/matplotlib/backends/backend_ps.py:927: in _print_figure
self.figure.draw(renderer)
py3.9/lib/python3.9/site-packages/matplotlib/artist.py:74: in draw_wrapper
result = draw(artist, renderer, *args, **kwargs)
py3.9/lib/python3.9/site-packages/matplotlib/artist.py:51: in draw_wrapper
return draw(artist, renderer)
py3.9/lib/python3.9/site-packages/matplotlib/figure.py:3069: in draw
mimage._draw_list_compositing_images(
py3.9/lib/python3.9/site-packages/matplotlib/image.py:131: in _draw_list_compositing_images
a.draw(renderer)
py3.9/lib/python3.9/site-packages/matplotlib/artist.py:51: in draw_wrapper
return draw(artist, renderer)
py3.9/lib/python3.9/site-packages/matplotlib/axes/_base.py:3106: in draw
mimage._draw_list_compositing_images(
py3.9/lib/python3.9/site-packages/matplotlib/image.py:131: in _draw_list_compositing_images
a.draw(renderer)
py3.9/lib/python3.9/site-packages/matplotlib/artist.py:51: in draw_wrapper
return draw(artist, renderer)
py3.9/lib/python3.9/site-packages/matplotlib/text.py:736: in draw
textrenderer.draw_text(gc, x, y, clean_line,
py3.9/lib/python3.9/site-packages/matplotlib/backends/backend_ps.py:248: in wrapper
return meth(self, *args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <matplotlib.backends.backend_ps.RendererPS object at 0x141d65c70>
gc = <matplotlib.backend_bases.GraphicsContextBase object at 0x141d485b0>
x = 317.3922, y = 365.8765624999998, s = ''
prop = <matplotlib.font_manager.FontProperties object at 0x1407cbeb0>
angle = 0.0, ismath = False, mtext = None
@_log_if_debug_on
def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
# docstring inherited
if self._is_transparent(gc.get_rgb()):
return # Special handling for fully transparent.
if ismath == 'TeX':
return self.draw_tex(gc, x, y, s, prop, angle)
if ismath:
return self.draw_mathtext(gc, x, y, s, prop, angle)
if mpl.rcParams['ps.useafm']:
font = self._get_font_afm(prop)
scale = 0.001 * prop.get_size_in_points()
stream = []
thisx = 0
last_name = None # kerns returns 0 for None.
xs_names = []
for c in s:
name = uni2type1.get(ord(c), f"uni{ord(c):04X}")
try:
width = font.get_width_from_char_name(name)
except KeyError:
name = 'question'
width = font.get_width_char('?')
kern = font.get_kern_dist_from_name(last_name, name)
last_name = name
thisx += kern * scale
xs_names.append((thisx, name))
thisx += width * scale
ps_name = (font.postscript_name
.encode("ascii", "replace").decode("ascii"))
stream.append((ps_name, xs_names))
else:
font = self._get_font_ttf(prop)
self._character_tracker.track(font, s)
stream = []
prev_font = curr_stream = None
for item in _text_helpers.layout(s, font):
ps_name = (item.ft_object.postscript_name
.encode("ascii", "replace").decode("ascii"))
if item.ft_object is not prev_font:
if curr_stream:
stream.append(curr_stream)
prev_font = item.ft_object
curr_stream = [ps_name, []]
curr_stream[1].append(
(item.x, item.ft_object.get_glyph_name(item.glyph_idx))
)
# append the last entry
stream.append(curr_stream)
self.set_color(*gc.get_rgb())
> for ps_name, xs_names in stream:
E TypeError: cannot unpack non-iterable NoneType object
py3.9/lib/python3.9/site-packages/matplotlib/backends/backend_ps.py:673: TypeError
We are using the "headless" mode, which includes
matplotlib.use("Agg")
When we get to this line:
data = d.get_imagedata(ImageFormat.EPS)
The exception above occurs. This happens both on macOS with Python 3.9.13 as well as a python:3.9
. In both cases we are running unit tests under pytest
.
matplotlib 3.6.0
schemdraw 0.15
I tried to produce a minimal reproducible example for this, but was not successful.
I thought this would reproduce it, but it did not. It might require a more complex drawing to trigger this problem?
import matplotlib
from schemdraw import ImageFormat, schemdraw
from schemdraw import elements as elm
matplotlib.use("Agg")
matplotlib.rcParams["svg.fonttype"] = "none"
def main() -> None:
d = schemdraw.Drawing()
d += elm.Resistor().right().label('1Ω')
data = d.get_imagedata(ImageFormat.EPS)
print(len(data))
if __name__ == "__main__":
main()
Comments (3)
-
repo owner -
@cdkeller Good find! That does indeed seem like a likely culprit.
-
repo owner - changed status to resolved
Fixed by Matplotlib 3.6.1.
- Log in to comment
Looks to be related to https://github.com/matplotlib/matplotlib/issues/23954. I can reproduce if the label has a blank line: