Skip to content

Commit b6f13f1

Browse files
authored
Merge pull request #7748 from jkseppan/deterministic-tests
MAINT: Deterministic SVG and PDF tests
2 parents c4b7ad6 + 6736f7d commit b6f13f1

File tree

4 files changed

+37
-5
lines changed

4 files changed

+37
-5
lines changed
+16-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
1-
Reproducible PS and PDF output
2-
------------------------------
1+
Reproducible PS, PDF and SVG output
2+
-----------------------------------
33

44
The ``SOURCE_DATE_EPOCH`` environment variable can now be used to set
55
the timestamp value in the PS and PDF outputs. See
66
https://reproducible-builds.org/specs/source-date-epoch/
77

8+
Alternatively, calling ``savefig`` with ``metadata={'creationDate': None}``
9+
will omit the timestamp altogether.
10+
811
The reproducibility of the output from the PS and PDF backends has so
912
far been tested using various plot elements but only default values of
1013
options such as ``{ps,pdf}.fonttype`` that can affect the output at a
1114
low level, and not with the mathtext or usetex features. When
1215
matplotlib calls external tools (such as PS distillers or LaTeX) their
1316
versions need to be kept constant for reproducibility, and they may
1417
add sources of nondeterminism outside the control of matplotlib.
18+
19+
For SVG output, the ``svg.hashsalt`` rc parameter has been added in an
20+
earlier release. This parameter changes some random identifiers in the
21+
SVG file to be deterministic. The downside of this setting is that if
22+
more than one file is generated using deterministic identifiers
23+
and they end up as parts of one larger document, the identifiers can
24+
collide and cause the different parts to affect each other.
25+
26+
These features are now enabled in the tests for the PDF and SVG
27+
backends, so most test output files (but not all of them) are now
28+
deterministic.

lib/matplotlib/backends/backend_pdf.py

+10-2
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,6 @@ def __init__(self, filename, metadata=None):
472472
'Pages': self.pagesObject}
473473
self.writeObject(self.rootObject, root)
474474

475-
revision = ''
476475
# get source date from SOURCE_DATE_EPOCH, if set
477476
# See https://reproducible-builds.org/specs/source-date-epoch/
478477
source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
@@ -484,11 +483,13 @@ def __init__(self, filename, metadata=None):
484483

485484
self.infoDict = {
486485
'Creator': 'matplotlib %s, http://matplotlib.org' % __version__,
487-
'Producer': 'matplotlib pdf backend%s' % revision,
486+
'Producer': 'matplotlib pdf backend %s' % __version__,
488487
'CreationDate': source_date
489488
}
490489
if metadata is not None:
491490
self.infoDict.update(metadata)
491+
self.infoDict = {k: v for (k, v) in self.infoDict.items()
492+
if v is not None}
492493

493494
self.fontNames = {} # maps filenames to internal font names
494495
self.nextFont = 1 # next free internal font name
@@ -2459,6 +2460,13 @@ def __init__(self, filename, keep_empty=True, metadata=None):
24592460
'Document Information Dictionary'), e.g.:
24602461
`{'Creator': 'My software', 'Author': 'Me',
24612462
'Title': 'Awesome fig'}`
2463+
2464+
The standard keys are `'Title'`, `'Author'`, `'Subject'`,
2465+
`'Keywords'`, `'Creator'`, `'Producer'`, `'CreationDate'`,
2466+
`'ModDate'`, and `'Trapped'`. Values have been predefined
2467+
for `'Creator'`, `'Producer'` and `'CreationDate'`. They
2468+
can be removed by setting them to `None`.
2469+
24622470
"""
24632471
self._file = PdfFile(filename, metadata=metadata)
24642472
self.keep_empty = keep_empty

lib/matplotlib/testing/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ def set_font_settings_for_testing():
136136
rcParams['text.hinting_factor'] = 8
137137

138138

139+
def set_reproducibility_for_testing():
140+
rcParams['svg.hashsalt'] = 'matplotlib'
141+
142+
139143
def setup():
140144
# The baseline images are created in this locale, so we should use
141145
# it during all of the tests.
@@ -161,3 +165,4 @@ def setup():
161165
rcdefaults() # Start with all defaults
162166

163167
set_font_settings_for_testing()
168+
set_reproducibility_for_testing()

lib/matplotlib/testing/decorators.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,12 @@ def compare(self, idx, baseline, extension):
298298
remove_ticks_and_titles(fig)
299299

300300
actual_fname = os.path.join(self.result_dir, baseline) + '.' + extension
301-
fig.savefig(actual_fname, **self.savefig_kwargs)
301+
kwargs = self.savefig_kwargs.copy()
302+
if extension == 'pdf':
303+
kwargs.setdefault('metadata',
304+
{'Creator': None, 'Producer': None,
305+
'CreationDate': None})
306+
fig.savefig(actual_fname, **kwargs)
302307

303308
expected_fname = self.copy_baseline(baseline, extension)
304309
raise_on_image_difference(expected_fname, actual_fname, self.tol)

0 commit comments

Comments
 (0)