diff --git a/doc/api/next_api_changes/2019-08-19-pass_gid.rst b/doc/api/next_api_changes/2019-08-19-pass_gid.rst new file mode 100644 index 000000000000..ebc18b0ec886 --- /dev/null +++ b/doc/api/next_api_changes/2019-08-19-pass_gid.rst @@ -0,0 +1,10 @@ +The ``gid`` is now correctly passed to svg output +````````````````````````````````````````````````` + +Previously, if a figure, axis, legend or some other artists had a custom +``gid`` set (e.g. via ``.set_gid()``), this would not be reflected in +the svg output. Instead a default gid, like ``figure_1`` would be shown. +This is now fixed, such that e.g. ``fig.set_gid("myfigure")`` correctly +shows up as ```` in the svg file. If you relied on the +gid having the default format, you now need to make sure not to set the +``gid`` parameter of the artists. \ No newline at end of file diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index b99100ecaf62..fd5d92e1b3f2 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2560,7 +2560,7 @@ def draw(self, renderer=None, inframe=False): return self._unstale_viewLim() - renderer.open_group('axes') + renderer.open_group('axes', gid=self.get_gid()) # prevent triggering call backs during the draw process self._stale = True diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 2feb56593d2f..35cb25bdb794 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -291,7 +291,7 @@ def draw(self, renderer): if not self.get_visible(): self.stale = False return - renderer.open_group(self.__name__) + renderer.open_group(self.__name__, gid=self.get_gid()) for artist in [self.gridline, self.tick1line, self.tick2line, self.label1, self.label2]: artist.draw(renderer) @@ -1222,7 +1222,7 @@ def draw(self, renderer, *args, **kwargs): if not self.get_visible(): return - renderer.open_group(__name__) + renderer.open_group(__name__, gid=self.get_gid()) ticks_to_draw = self._update_ticks() ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw, diff --git a/lib/matplotlib/collections.py b/lib/matplotlib/collections.py index 3f9a6d5ece09..cd2ef67b8979 100644 --- a/lib/matplotlib/collections.py +++ b/lib/matplotlib/collections.py @@ -1882,7 +1882,7 @@ def convert_mesh_to_paths(tri): def draw(self, renderer): if not self.get_visible(): return - renderer.open_group(self.__class__.__name__) + renderer.open_group(self.__class__.__name__, gid=self.get_gid()) transform = self.get_transform() # Get a list of triangles and the color at each vertex. diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index e375cb204839..4e0cc02f9b05 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -1724,7 +1724,7 @@ def draw(self, renderer): child.apply_aspect() try: - renderer.open_group('figure') + renderer.open_group('figure', gid=self.get_gid()) if self.get_constrained_layout() and self.axes: self.execute_constrained_layout(renderer) if self.get_tight_layout() and self.axes: diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index ee754bd37379..9cabd4185566 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -633,7 +633,7 @@ def draw(self, renderer): if not self.get_visible(): return - renderer.open_group('legend') + renderer.open_group('legend', gid=self.get_gid()) fontsize = renderer.points_to_pixels(self._fontsize) diff --git a/lib/matplotlib/table.py b/lib/matplotlib/table.py index 9f0f0ba321c5..c4faafdea5fd 100644 --- a/lib/matplotlib/table.py +++ b/lib/matplotlib/table.py @@ -411,7 +411,7 @@ def draw(self, renderer): if not self.get_visible(): return - renderer.open_group('table') + renderer.open_group('table', gid=self.get_gid()) self._update_positions(renderer) for key in sorted(self._cells): diff --git a/lib/matplotlib/tests/test_backend_svg.py b/lib/matplotlib/tests/test_backend_svg.py index 46d8d655dfd3..642975b68177 100644 --- a/lib/matplotlib/tests/test_backend_svg.py +++ b/lib/matplotlib/tests/test_backend_svg.py @@ -1,6 +1,5 @@ import numpy as np from io import BytesIO -import os import re import tempfile import warnings @@ -145,3 +144,67 @@ def test_svgnone_with_data_coordinates(): buf = fd.read().decode() assert expected in buf + + +def test_gid(): + """Test that object gid appears in output svg.""" + from matplotlib.offsetbox import OffsetBox + from matplotlib.axis import Tick + + fig = plt.figure() + + ax1 = fig.add_subplot(131) + ax1.imshow([[1., 2.], [2., 3.]], aspect="auto") + ax1.scatter([1, 2, 3], [1, 2, 3], label="myscatter") + ax1.plot([2, 3, 1], label="myplot") + ax1.legend() + ax1a = ax1.twinx() + ax1a.bar([1, 2, 3], [1, 2, 3]) + + ax2 = fig.add_subplot(132, projection="polar") + ax2.plot([0, 1.5, 3], [1, 2, 3]) + + ax3 = fig.add_subplot(133, projection="3d") + ax3.plot([1, 2], [1, 2], [1, 2]) + + fig.canvas.draw() + + gdic = {} + for idx, obj in enumerate(fig.findobj(include_self=True)): + if obj.get_visible(): + gid = f"test123{obj.__class__.__name__}_{idx}" + gdic[gid] = obj + obj.set_gid(gid) + + fd = BytesIO() + fig.savefig(fd, format='svg') + fd.seek(0) + buf = fd.read().decode() + fd.close() + + def include(gid, obj): + # we need to exclude certain objects which will not appear in the svg + if isinstance(obj, OffsetBox): + return False + if isinstance(obj, plt.Text): + if obj.get_text() == "": + return False + elif obj.axes is None: + return False + if isinstance(obj, plt.Line2D): + if np.array(obj.get_data()).shape == (2, 1): + return False + elif not hasattr(obj, "axes") or obj.axes is None: + return False + if isinstance(obj, Tick): + loc = obj.get_loc() + if loc == 0: + return False + vi = obj.get_view_interval() + if loc < min(vi) or loc > max(vi): + return False + return True + + for gid, obj in gdic.items(): + if include(gid, obj): + assert gid in buf diff --git a/lib/mpl_toolkits/axisartist/axis_artist.py b/lib/mpl_toolkits/axisartist/axis_artist.py index e31cc1544a6c..e6aa06364eca 100644 --- a/lib/mpl_toolkits/axisartist/axis_artist.py +++ b/lib/mpl_toolkits/axisartist/axis_artist.py @@ -127,7 +127,7 @@ def draw(self, renderer): if not self._visible: return - renderer.open_group('line2d') + renderer.open_group('line2d', gid=self.get_gid()) gc = renderer.new_gc() self._set_gc_clip(gc) @@ -1230,7 +1230,7 @@ def draw(self, renderer): if not self.get_visible(): return - renderer.open_group(__name__) + renderer.open_group(__name__, gid=self.get_gid()) self._axis_artist_helper.update_lim(self.axes) diff --git a/lib/mpl_toolkits/mplot3d/axis3d.py b/lib/mpl_toolkits/mplot3d/axis3d.py index 884cb81a0400..7ea7bc08069c 100644 --- a/lib/mpl_toolkits/mplot3d/axis3d.py +++ b/lib/mpl_toolkits/mplot3d/axis3d.py @@ -190,7 +190,7 @@ def _get_coord_info(self, renderer): return mins, maxs, centers, deltas, tc, highs def draw_pane(self, renderer): - renderer.open_group('pane3d') + renderer.open_group('pane3d', gid=self.get_gid()) mins, maxs, centers, deltas, tc, highs = self._get_coord_info(renderer) @@ -209,7 +209,7 @@ def draw_pane(self, renderer): @artist.allow_rasterization def draw(self, renderer): self.label._transform = self.axes.transData - renderer.open_group('axis3d') + renderer.open_group('axis3d', gid=self.get_gid()) ticks = self._update_ticks()