Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d31a156

Browse filesBrowse files
committed
Merge pull request #5671 from mdboom/deterministic-svg
Deterministic svg
1 parent a6f881a commit d31a156
Copy full SHA for d31a156

File tree

Expand file treeCollapse file tree

9 files changed

+79
-6
lines changed
Filter options
Expand file treeCollapse file tree

9 files changed

+79
-6
lines changed

‎doc/api/backend_svg_api.rst

Copy file name to clipboard
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
:mod:`matplotlib.backends.backend_svg`
3+
======================================
4+
5+
.. automodule:: matplotlib.backends.backend_svg
6+
:members:
7+
:show-inheritance:

‎doc/api/index_backend_api.rst

Copy file name to clipboardExpand all lines: doc/api/index_backend_api.rst
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ backends
1212
backend_qt5agg_api.rst
1313
backend_wxagg_api.rst
1414
backend_pdf_api.rst
15+
backend_svg_api.rst
1516
.. backend_webagg.rst
1617
dviread.rst
1718
type1font.rst

‎doc/users/whats_new/rcparams.rst

Copy file name to clipboard
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Added ``svg.hashsalt`` key to rcParams
2+
```````````````````````````````````````
3+
If ``svg.hashsalt`` is ``None`` (which it is by default), the svg backend uses ``uuid4`` to generate the hash salt.
4+
If it is not ``None``, it must be a string that is used as the hash salt instead of ``uuid4``.
5+
This allows for deterministic SVG output.

‎lib/matplotlib/axes/_base.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_base.py
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from __future__ import (absolute_import, division, print_function,
22
unicode_literals)
33

4+
from collections import OrderedDict
5+
46
from matplotlib.externals import six
57
from matplotlib.externals.six.moves import xrange
68

7-
from collections import OrderedDict
89
import itertools
910
import warnings
1011
import math
@@ -33,7 +34,6 @@
3334
import matplotlib.image as mimage
3435
from matplotlib.offsetbox import OffsetBox
3536
from matplotlib.artist import allow_rasterization
36-
from matplotlib.cbook import iterable, index_of
3737
from matplotlib.rcsetup import cycler
3838

3939
rcParams = matplotlib.rcParams

‎lib/matplotlib/backends/backend_svg.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_svg.py
+7-3Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from __future__ import (absolute_import, division, print_function,
22
unicode_literals)
33

4+
from collections import OrderedDict
5+
46
from matplotlib.externals import six
57
from matplotlib.externals.six.moves import xrange
68
from matplotlib.externals.six import unichr
79

810
import os, base64, tempfile, gzip, io, sys, codecs, re
9-
from collections import OrderedDict
1011

1112
import numpy as np
1213

@@ -316,7 +317,10 @@ def _write_default_style(self):
316317

317318
def _make_id(self, type, content):
318319
content = str(content)
319-
salt = str(uuid.uuid4())
320+
if rcParams['svg.hashsalt'] is None:
321+
salt = str(uuid.uuid4())
322+
else:
323+
salt = rcParams['svg.hashsalt']
320324
if six.PY3:
321325
content = content.encode('utf8')
322326
salt = salt.encode('utf8')
@@ -840,7 +844,7 @@ def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None):
840844
if rcParams['svg.image_inline']:
841845
bytesio = io.BytesIO()
842846
_png.write_png(np.array(im)[::-1], bytesio)
843-
oid = oid or self._make_id('image', bytesio)
847+
oid = oid or self._make_id('image', bytesio.getvalue())
844848
attrib['xlink:href'] = (
845849
"data:image/png;base64,\n" +
846850
base64.b64encode(bytesio.getvalue()).decode('ascii'))

‎lib/matplotlib/rcsetup.py

Copy file name to clipboardExpand all lines: lib/matplotlib/rcsetup.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,16 @@ def validate_float_or_None(s):
166166
raise ValueError('Could not convert "%s" to float or None' % s)
167167

168168

169+
def validate_string_or_None(s):
170+
"""convert s to string or raise"""
171+
if s is None:
172+
return None
173+
try:
174+
return six.text_type(s)
175+
except ValueError:
176+
raise ValueError('Could not convert "%s" to string' % s)
177+
178+
169179
def validate_dpi(s):
170180
"""confirm s is string 'figure' or convert s to float or raise"""
171181
if s == 'figure':
@@ -1165,6 +1175,7 @@ def validate_hist_bins(s):
11651175
# True to save all characters as paths in the SVG
11661176
'svg.embed_char_paths': [True, deprecate_svg_embed_char_paths],
11671177
'svg.fonttype': ['path', validate_svg_fonttype],
1178+
'svg.hashsalt': [None, validate_string_or_None],
11681179

11691180
# set this when you want to generate hardcopy docstring
11701181
'docstring.hardcopy': [False, validate_bool],

‎lib/matplotlib/tests/test_backend_svg.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_backend_svg.py
+42Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,48 @@ def test_bold_font_output_with_none_fonttype():
119119
ax.set_title('bold-title', fontweight='bold')
120120

121121

122+
def _test_determinism(filename):
123+
# This function is mostly copy&paste from "def test_visibility"
124+
# To require no GUI, we use Figure and FigureCanvasSVG
125+
# instead of plt.figure and fig.savefig
126+
from matplotlib.figure import Figure
127+
from matplotlib.backends.backend_svg import FigureCanvasSVG
128+
from matplotlib import rc
129+
rc('svg', hashsalt='asdf')
130+
131+
fig = Figure()
132+
ax = fig.add_subplot(111)
133+
134+
x = np.linspace(0, 4 * np.pi, 50)
135+
y = np.sin(x)
136+
yerr = np.ones_like(y)
137+
138+
a, b, c = ax.errorbar(x, y, yerr=yerr, fmt='ko')
139+
for artist in b:
140+
artist.set_visible(False)
141+
142+
FigureCanvasSVG(fig).print_svg(filename)
143+
144+
145+
@cleanup
146+
def test_determinism():
147+
import os
148+
import sys
149+
from subprocess import check_call
150+
from nose.tools import assert_equal
151+
plots = []
152+
for i in range(3):
153+
check_call([sys.executable, '-R', '-c',
154+
'from matplotlib.tests.test_backend_svg '
155+
'import _test_determinism;'
156+
'_test_determinism("determinism.svg")'])
157+
with open('determinism.svg', 'rb') as fd:
158+
plots.append(fd.read())
159+
os.unlink('determinism.svg')
160+
for p in plots[1:]:
161+
assert_equal(p, plots[0])
162+
163+
122164
if __name__ == '__main__':
123165
import nose
124166
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

‎lib/matplotlib/textpath.py

Copy file name to clipboardExpand all lines: lib/matplotlib/textpath.py
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from __future__ import (absolute_import, division, print_function,
44
unicode_literals)
55

6+
from collections import OrderedDict
7+
68
from matplotlib.externals import six
79
from matplotlib.externals.six.moves import zip
810

@@ -20,7 +22,6 @@
2022
from matplotlib.font_manager import FontProperties, get_font
2123
from matplotlib.transforms import Affine2D
2224
from matplotlib.externals.six.moves.urllib.parse import quote as urllib_quote
23-
from collections import OrderedDict
2425

2526

2627
class TextToPath(object):

‎matplotlibrc.template

Copy file name to clipboardExpand all lines: matplotlibrc.template
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@ backend : %(backend)s
505505
# 'path': Embed characters as paths -- supported by most SVG renderers
506506
# 'svgfont': Embed characters as SVG fonts -- supported only by Chrome,
507507
# Opera and Safari
508+
#svg.hashsalt : None # if not None, use this string as hash salt
509+
# instead of uuid4
508510

509511
# docstring params
510512
#docstring.hardcopy = False # set this when you want to generate hardcopy docstring

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.