Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c6a4660

Browse files
author
Alexis Bienvenüe
committed
To allow reproducible output:
* honour SOURCE_DATE_EPOCH for timestamps in PS and PDF files. See https://reproducible-builds.org/specs/source-date-epoch/ * get keys sorted so that hatchPatterns, images and markers are included with a reproducible order in the PDF file. See https://reproducible-builds.org/
1 parent af77c85 commit c6a4660

File tree

2 files changed

+45
-9
lines changed

2 files changed

+45
-9
lines changed

lib/matplotlib/backends/backend_pdf.py

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@
1717
import time
1818
import warnings
1919
import zlib
20+
import collections
2021
from io import BytesIO
2122
from functools import total_ordering
2223

2324
import numpy as np
2425
from six import unichr
2526

2627

27-
from datetime import datetime
28+
from datetime import datetime, tzinfo, timedelta
2829
from math import ceil, cos, floor, pi, sin
2930

3031
import matplotlib
@@ -135,6 +136,20 @@ def _string_escape(match):
135136
assert False
136137

137138

139+
# tzinfo class for UTC
140+
class UTCtimezone(tzinfo):
141+
"""UTC timezone"""
142+
143+
def utcoffset(self, dt):
144+
return timedelta(0)
145+
146+
def tzname(self, dt):
147+
return "UTC"
148+
149+
def dst(self, dt):
150+
return timedelta(0)
151+
152+
138153
def pdfRepr(obj):
139154
"""Map Python objects to PDF syntax."""
140155

@@ -202,10 +217,14 @@ def pdfRepr(obj):
202217
# A date.
203218
elif isinstance(obj, datetime):
204219
r = obj.strftime('D:%Y%m%d%H%M%S')
205-
if time.daylight:
206-
z = time.altzone
220+
z = obj.utcoffset()
221+
if z is not None:
222+
z = z.seconds
207223
else:
208-
z = time.timezone
224+
if time.daylight:
225+
z = time.altzone
226+
else:
227+
z = time.timezone
209228
if z == 0:
210229
r += 'Z'
211230
elif z < 0:
@@ -468,10 +487,19 @@ def __init__(self, filename):
468487
self.writeObject(self.rootObject, root)
469488

470489
revision = ''
490+
# get source date from SOURCE_DATE_EPOCH, if set
491+
# See https://reproducible-builds.org/specs/source-date-epoch/
492+
source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
493+
if source_date_epoch:
494+
source_date = datetime.utcfromtimestamp(int(source_date_epoch))
495+
source_date = source_date.replace(tzinfo=UTCtimezone())
496+
else:
497+
source_date = datetime.today()
498+
471499
self.infoDict = {
472500
'Creator': 'matplotlib %s, http://matplotlib.org' % __version__,
473501
'Producer': 'matplotlib pdf backend%s' % revision,
474-
'CreationDate': datetime.today()
502+
'CreationDate': source_date
475503
}
476504

477505
self.fontNames = {} # maps filenames to internal font names
@@ -483,14 +511,15 @@ def __init__(self, filename):
483511

484512
self.alphaStates = {} # maps alpha values to graphics state objects
485513
self.nextAlphaState = 1
486-
self.hatchPatterns = {}
514+
# reproducible writeHatches needs an ordered dict:
515+
self.hatchPatterns = collections.OrderedDict()
487516
self.nextHatch = 1
488517
self.gouraudTriangles = []
489518

490-
self._images = {}
519+
self._images = collections.OrderedDict() # reproducible writeImages
491520
self.nextImage = 1
492521

493-
self.markers = {}
522+
self.markers = collections.OrderedDict() # reproducible writeMarkers
494523
self.multi_byte_charprocs = {}
495524

496525
self.paths = []

lib/matplotlib/backends/backend_ps.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,14 @@ def print_figure_impl():
10871087
if title: print("%%Title: "+title, file=fh)
10881088
print(("%%Creator: matplotlib version "
10891089
+__version__+", http://matplotlib.org/"), file=fh)
1090-
print("%%CreationDate: "+time.ctime(time.time()), file=fh)
1090+
# get source date from SOURCE_DATE_EPOCH, if set
1091+
# See https://reproducible-builds.org/specs/source-date-epoch/
1092+
source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
1093+
if source_date_epoch:
1094+
source_date = time.asctime(time.gmtime(int(source_date_epoch)))
1095+
else:
1096+
source_date = time.ctime()
1097+
print("%%CreationDate: "+source_date, file=fh)
10911098
print("%%Orientation: " + orientation, file=fh)
10921099
if not isEPSF: print("%%DocumentPaperSizes: "+papertype, file=fh)
10931100
print("%%%%BoundingBox: %d %d %d %d" % bbox, file=fh)

0 commit comments

Comments
 (0)