Skip to content

[Bug]: Empty text or text with a newline at either end + path_effects crashes #22687

@has2k1

Description

@has2k1

Bug summary

Spaces only strings, newlines only strings or strings with a newline at either end cannot be plotted with path_effects.

Code for reproduction

import matplotlib.pyplot as plt
import matplotlib.patheffects as path_effects

fig, ax = plt.subplots()
s1 = "         "
s2 = "\nNewline also causes problems"
text1 = ax.text(0.5, 0.75, s1, ha='center', va='center', size=20, bbox={'color': 'salmon'})
text2 = ax.text(0.5, 0.25, s2, ha='center', va='center', size=20, bbox={'color': 'thistle'})

text1.set_path_effects([path_effects.Normal()])
text2.set_path_effects([path_effects.Normal()])

plt.show()

Actual outcome

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File ~/.pyenv/versions/3.10.2/envs/plotnine/lib/python3.10/site-packages/IPython/core/formatters.py:339, in BaseFormatter.__call__(self, obj)
    337     pass
    338 else:
--> 339     return printer(obj)
    340 # Finally look for special method names
    341 method = get_real_method(obj, self.print_method)

File ~/.pyenv/versions/3.10.2/envs/plotnine/lib/python3.10/site-packages/IPython/core/pylabtools.py:151, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
    148     from matplotlib.backend_bases import FigureCanvasBase
    149     FigureCanvasBase(fig)
--> 151 fig.canvas.print_figure(bytes_io, **kw)
    152 data = bytes_io.getvalue()
    153 if fmt == 'svg':

File ~/scm/python/matplotlib/lib/matplotlib/backend_bases.py:2242, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
   2236     renderer = _get_renderer(
   2237         self.figure,
   2238         functools.partial(
   2239             print_method, orientation=orientation)
   2240     )
   2241     with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2242         self.figure.draw(renderer)
   2244 if bbox_inches:
   2245     if bbox_inches == "tight":

File ~/scm/python/matplotlib/lib/matplotlib/artist.py:73, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
     71 @wraps(draw)
     72 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 73     result = draw(artist, renderer, *args, **kwargs)
     74     if renderer._rasterizing:
     75         renderer.stop_rasterizing()

File ~/scm/python/matplotlib/lib/matplotlib/artist.py:50, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     47     if artist.get_agg_filter() is not None:
     48         renderer.start_filter()
---> 50     return draw(artist, renderer)
     51 finally:
     52     if artist.get_agg_filter() is not None:

File ~/scm/python/matplotlib/lib/matplotlib/figure.py:2860, in Figure.draw(self, renderer)
   2857         # ValueError can occur when resizing a window.
   2859 self.patch.draw(renderer)
-> 2860 mimage._draw_list_compositing_images(
   2861     renderer, self, artists, self.suppressComposite)
   2863 for sfig in self.subfigs:
   2864     sfig.draw(renderer)

File ~/scm/python/matplotlib/lib/matplotlib/image.py:131, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    129 if not_composite or not has_images:
    130     for a in artists:
--> 131         a.draw(renderer)
    132 else:
    133     # Composite any adjacent images together
    134     image_group = []

File ~/scm/python/matplotlib/lib/matplotlib/artist.py:50, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     47     if artist.get_agg_filter() is not None:
     48         renderer.start_filter()
---> 50     return draw(artist, renderer)
     51 finally:
     52     if artist.get_agg_filter() is not None:

File ~/scm/python/matplotlib/lib/matplotlib/axes/_base.py:3081, in _AxesBase.draw(self, renderer)
   3078         a.draw(renderer)
   3079     renderer.stop_rasterizing()
-> 3081 mimage._draw_list_compositing_images(
   3082     renderer, self, artists, self.figure.suppressComposite)
   3084 renderer.close_group('axes')
   3085 self.stale = False

File ~/scm/python/matplotlib/lib/matplotlib/image.py:131, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    129 if not_composite or not has_images:
    130     for a in artists:
--> 131         a.draw(renderer)
    132 else:
    133     # Composite any adjacent images together
    134     image_group = []

File ~/scm/python/matplotlib/lib/matplotlib/artist.py:50, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
     47     if artist.get_agg_filter() is not None:
     48         renderer.start_filter()
---> 50     return draw(artist, renderer)
     51 finally:
     52     if artist.get_agg_filter() is not None:

File ~/scm/python/matplotlib/lib/matplotlib/text.py:728, in Text.draw(self, renderer)
    724             textrenderer.draw_tex(gc, x, y, clean_line,
    725                                   self._fontproperties, angle,
    726                                   mtext=mtext)
    727         else:
--> 728             textrenderer.draw_text(gc, x, y, clean_line,
    729                                    self._fontproperties, angle,
    730                                    ismath=ismath, mtext=mtext)
    732 gc.restore()
    733 renderer.close_group('text')

File ~/scm/python/matplotlib/lib/matplotlib/backend_bases.py:546, in RendererBase.draw_text(self, gc, x, y, s, prop, angle, ismath, mtext)
    511 def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
    512     """
    513     Draw a text instance.
    514 
   (...)
    543     your text.
    544     """
--> 546     self._draw_text_as_path(gc, x, y, s, prop, angle, ismath)

File ~/scm/python/matplotlib/lib/matplotlib/patheffects.py:142, in PathEffectRenderer._draw_text_as_path(self, gc, x, y, s, prop, angle, ismath)
    140 def _draw_text_as_path(self, gc, x, y, s, prop, angle, ismath):
    141     # Implements the naive text drawing as is found in RendererBase.
--> 142     path, transform = self._get_text_path_transform(x, y, s, prop,
    143                                                     angle, ismath)
    144     color = gc.get_rgb()
    145     gc.set_linewidth(0.0)

File ~/scm/python/matplotlib/lib/matplotlib/backend_bases.py:566, in RendererBase._get_text_path_transform(self, x, y, s, prop, angle, ismath)
    563 fontsize = self.points_to_pixels(prop.get_size_in_points())
    564 verts, codes = text2path.get_text_path(prop, s, ismath=ismath)
--> 566 path = Path(verts, codes)
    567 angle = np.deg2rad(angle)
    568 if self.flipy():

File ~/scm/python/matplotlib/lib/matplotlib/path.py:130, in Path.__init__(self, vertices, codes, _interpolation_steps, closed, readonly)
    101 """
    102 Create a new path with the given vertices and codes.
    103 
   (...)
    127     and codes as read-only arrays.
    128 """
    129 vertices = _to_unmasked_float_array(vertices)
--> 130 _api.check_shape((None, 2), vertices=vertices)
    132 if codes is not None:
    133     codes = np.asarray(codes, self.code_type)

File ~/scm/python/matplotlib/lib/matplotlib/_api/__init__.py:164, in check_shape(_shape, **kwargs)
    156 dim_labels = iter(itertools.chain(
    157     'MNLIJKLH',
    158     (f"D{i}" for i in itertools.count())))
    159 text_shape = ", ".join((str(n)
    160                         if n is not None
    161                         else next(dim_labels)
    162                         for n in target_shape))
--> 164 raise ValueError(
    165     f"{k!r} must be {len(target_shape)}D "
    166     f"with shape ({text_shape}). "
    167     f"Your input has shape {v.shape}."
    168 )

ValueError: 'vertices' must be 2D with shape (M, 2). Your input has shape (0,).

Expected outcome

path-effect-space-string

Additional information

No response

Operating system

No response

Matplotlib Version

3.6.0.dev1884+g11737d0694

Matplotlib Backend

No response

Python version

No response

Jupyter version

No response

Installation

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions