From 66e9fa21039cbf47fa8700e078ea1c57286580cf Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 28 Apr 2015 00:15:48 -0400 Subject: [PATCH] BUG : fix svg corner case Under certain conditions the ``id`` of the `clipPath` node emitted by the svg writer will be the same for multiple files. If both svg fragments are embedded in the same document, then one of them is removed, the clipping of the remaining fragment can become inconsistent. See https://github.com/ipython/ipython/issues/8133 Closes #4349 --- doc/api/api_changes/2015-04-27_svgclip_url.rst | 13 +++++++++++++ lib/matplotlib/backends/backend_svg.py | 12 +++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 doc/api/api_changes/2015-04-27_svgclip_url.rst diff --git a/doc/api/api_changes/2015-04-27_svgclip_url.rst b/doc/api/api_changes/2015-04-27_svgclip_url.rst new file mode 100644 index 000000000000..5572fb388c76 --- /dev/null +++ b/doc/api/api_changes/2015-04-27_svgclip_url.rst @@ -0,0 +1,13 @@ +Add salt to cilpPath id +``````````````````````` + +Add salt to the hash used to determine the id of the ``clipPath`` +nodes. This is to avoid conflicts in two svg documents with the same +clip path are included in the same document (see +https://github.com/ipython/ipython/issues/8133 and +https://github.com/matplotlib/matplotlib/issues/4349 ), however this +means that the svg output is no longer deterministic if the same +figure is saved twice. It is not expected that this will affect any +users as the current ids are generated from an md5 hash of properties +of the clip path and any user would have a very difficult time +anticipating the value of the id. diff --git a/lib/matplotlib/backends/backend_svg.py b/lib/matplotlib/backends/backend_svg.py index 4462c9b12cf6..5c58345e6fba 100644 --- a/lib/matplotlib/backends/backend_svg.py +++ b/lib/matplotlib/backends/backend_svg.py @@ -10,6 +10,7 @@ import numpy as np from hashlib import md5 +import uuid from matplotlib import verbose, __version__, rcParams from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\ @@ -255,7 +256,7 @@ def __init__(self, width, height, svgwriter, basename=None, image_dpi=72): self.width = width self.height = height self.writer = XMLWriter(svgwriter) - self.image_dpi = image_dpi # the actual dpi we want to rasterize stuff with + self.image_dpi = image_dpi # the actual dpi we want to rasterize stuff with self._groupd = {} if not rcParams['svg.image_inline']: @@ -309,9 +310,14 @@ def _write_default_style(self): def _make_id(self, type, content): content = str(content) + salt = str(uuid.uuid4()) if six.PY3: content = content.encode('utf8') - return '%s%s' % (type, md5(content).hexdigest()[:10]) + salt = salt.encode('utf8') + m = md5() + m.update(salt) + m.update(content) + return '%s%s' % (type, m.hexdigest()[:10]) def _make_flip_transform(self, transform): return (transform + @@ -532,7 +538,7 @@ def close_group(self, s): def option_image_nocomposite(self): """ - return whether to generate a composite image from multiple images on + return whether to generate a composite image from multiple images on a set of axes """ if rcParams['svg.image_noscale']: