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

Skip to content

Commit 8fb289b

Browse files
committed
Added compression support to PDF backend, controlled by
pdf.compression rc setting. svn path=/trunk/matplotlib/; revision=2310
1 parent a46cda1 commit 8fb289b

4 files changed

Lines changed: 92 additions & 26 deletions

File tree

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2006-04-19 Added compression support to PDF backend, controlled by
2+
new pdf.compression rc setting. - JKS
3+
14
2006-04-19 Added Jouni's PDF backend
25

36
2006-04-18 Fixed a bug that caused agg to not render long lines

lib/matplotlib/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,7 @@ def __call__(self, s):
860860
'ps.useafm' : [ False, validate_bool], # Set PYTHONINSPECT
861861
'ps.usedistiller' : [ False, validate_ps_distiller], # use ghostscript or xpdf to distill ps output
862862
'ps.distiller.res' : [6000, validate_int], # dpi
863+
'pdf.compression' : [6, validate_int], # compression level from 0 to 9; 0 to disable
863864
'plugins.directory' : ['.matplotlib_plugins', str], # where plugin directory is locate
864865

865866
}
@@ -990,12 +991,12 @@ def rc_params(fail_on_error=False):
990991
except KeyError: continue
991992
else:
992993
cval = validate_key(key, val, line, cnt, fname, fail_on_error)
993-
if cval: defaultParams[key][0] = cval
994+
if cval is not None: defaultParams[key][0] = cval
994995
while len(rc_temp) > 0:
995996
key, (val, line, cnt) = rc_temp.popitem()
996997

997998
cval = validate_key(key, val, line, cnt, fname, fail_on_error)
998-
if cval: defaultParams[key][0] = cval
999+
if cval is not None: defaultParams[key][0] = cval
9991000
else: continue
10001001

10011002
# strip the converter funcs and return

lib/matplotlib/backends/backend_pdf.py

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
1+
# -*- coding: iso-8859-1 -*-
12
"""
23
A PDF matplotlib backend
4+
Author: Jouni K Seppänen <[email protected]>
5+
6+
As of yet, this implements a small subset of the backend protocol, but
7+
enough to get some output from simple plots. Alpha is supported.
38
"""
49
from __future__ import division
510

611
import md5
712
import re
813
import sys
914
import time
15+
import zlib
1016

17+
from matplotlib import __version__, rcParams
1118
from matplotlib._pylab_helpers import Gcf
1219
from matplotlib.backend_bases import RendererBase, GraphicsContextBase,\
1320
FigureManagerBase, FigureCanvasBase
@@ -89,8 +96,7 @@ def pdfRepr(self):
8996
return "%d 0 R" % self.id
9097

9198
def write(self, contents, file):
92-
file.recordXref(self.id)
93-
write = file.fh.write
99+
write = file.write
94100
write("%d 0 obj\n" % self.id)
95101
write(pdfRepr(contents))
96102
write("\nendobj\n")
@@ -113,43 +119,75 @@ class Stream:
113119
"""PDF stream object.
114120
115121
This has no pdfRepr method. Instead, call begin(), then output the
116-
contents of the stream, and finally call end().
122+
contents of the stream by calling write(), and finally call end().
117123
"""
118124

119-
# TODO: compression
120-
121125
def __init__(self, id, len, file):
122126
"""id: object id of stream; len: an unused Reference object
123127
for the length of the stream; file: a PdfFile
124128
"""
125-
self.id = id
126-
self.len = len
127-
self.file = file
129+
self.id = id # object id
130+
self.len = len # id of length object
131+
self.file = file # file to which the stream is written
132+
self.compressobj = None # compression object
128133

129134
def begin(self):
130-
write = self.file.write
135+
"""Initialize stream."""
136+
137+
write = self.file.fh.write
131138
self.file.recordXref(self.id)
132139
write("%d 0 obj\n" % self.id)
133-
write(pdfRepr({ 'Length': self.len }))
140+
dict = { 'Length': self.len }
141+
if rcParams['pdf.compression']:
142+
dict['Filter'] = Name('FlateDecode')
143+
write(pdfRepr(dict))
134144
write("\nstream\n")
135145
self.pos = self.file.fh.tell()
146+
if rcParams['pdf.compression']:
147+
self.compressobj = zlib.compressobj(rcParams['pdf.compression'])
136148

137149
def end(self):
150+
"""Finalize stream."""
151+
152+
self._flush()
138153
length = self.file.fh.tell() - self.pos
139154
self.file.write("\nendstream\nendobj\n")
140-
self.len.write(length, self.file)
155+
self.file.writeObject(self.len, length)
156+
157+
def write(self, data):
158+
"""Write some data on the stream."""
159+
160+
if self.compressobj is None:
161+
self.file.fh.write(data)
162+
else:
163+
compressed = self.compressobj.compress(data)
164+
self.file.fh.write(compressed)
165+
166+
def _flush(self):
167+
"""Flush the compression object."""
168+
169+
if self.compressobj is not None:
170+
compressed = self.compressobj.flush()
171+
self.file.fh.write(compressed)
172+
self.compressobj = None
141173

142174
class PdfFile:
143175
"""PDF file with one page."""
144176

145177
def __init__(self, width, height, filename):
146-
self.nextObject = 1
178+
self.nextObject = 1 # next free object id
147179
self.xrefTable = [ [0, 65535, 'the zero object'] ]
148180
fh = file(filename, 'w')
149181
self.fh = fh
150-
fh.write("%PDF-1.4\n")
182+
self.currentstream = None # stream object to write to, if any
183+
fh.write("%PDF-1.4\n") # 1.4 is the first version to have alpha
184+
# Output some binary chars as a comment so various utilities
185+
# recognize the file as binary by looking at the first few
186+
# lines (see note in section 3.4.1 of the PDF reference).
187+
fh.write("%\254\334 \253\272\n")
151188

152189
self.rootObject = self.reserveObject('root')
190+
self.infoObject = self.reserveObject('info')
153191
pagesObject = self.reserveObject('pages')
154192
thePageObject = self.reserveObject('page 0')
155193
contentObject = self.reserveObject('contents of page 0')
@@ -161,6 +199,11 @@ def __init__(self, width, height, filename):
161199
'Pages': pagesObject }
162200
self.writeObject(self.rootObject, root)
163201

202+
info = { 'Producer': 'matplotlib version ' + __version__ \
203+
+ ', http://matplotlib.sourceforge.net', }
204+
# Possible TODO: Title, Author, Subject, Keywords, CreationDate
205+
self.writeObject(self.infoObject, info)
206+
164207
pages = { 'Type': Name('Pages'),
165208
'Kids': [ thePageObject ],
166209
'Count': 1 }
@@ -173,26 +216,37 @@ def __init__(self, width, height, filename):
173216
'Contents': contentObject }
174217
self.writeObject(thePageObject, thePage)
175218

219+
# self.fonts has font objects keyed by internal font names (/F1 etc)
220+
# self.fontnames maps external to internal names
176221
self.fonts, self.fontNames = {}, {}
177-
self.nextFont = 1
178-
self.alphaStates = {}
222+
self.nextFont = 1 # next free internal font name
223+
224+
self.alphaStates = {} # maps alpha values to graphics state objects
179225
self.nextAlphaState = 1
226+
227+
# The PDF spec recommends to include every procset
180228
procsets = [ Name(x)
181229
for x in "PDF Text ImageB ImageC ImageI".split() ]
230+
231+
# Write resource dictionary.
232+
# Possibly TODO: more general ExtGState (graphics state dictionaries)
233+
# ColorSpace Pattern Shading XObject Properties
182234
resources = { 'Font': self.fontObject,
183235
'ExtGState': self.alphaStateObject,
184236
'ProcSet': procsets }
185-
# Other resources: more general ExtGState (graphics state dictionaries)
186-
# ColorSpace Pattern Shading XObject Properties
187237
self.writeObject(resourceObject, resources)
188238

239+
# Start the content stream of the page
189240
self.contents = \
190241
Stream(contentObject.id,
191242
self.reserveObject('length of content stream'),
192243
self)
193244
self.contents.begin()
245+
self.currentstream = self.contents
194246

195247
def close(self):
248+
# End the content stream and write out the various deferred
249+
# objects
196250
self.contents.end()
197251
self.writeObject(self.fontObject, self.fonts)
198252
self.writeObject(self.alphaStateObject,
@@ -203,7 +257,10 @@ def close(self):
203257
self.fh.close()
204258

205259
def write(self, data):
206-
self.fh.write(data)
260+
if self.currentstream is None:
261+
self.fh.write(data)
262+
else:
263+
self.currentstream.write(data)
207264

208265
def fontName(self, font):
209266
# TODO: the hard parts (i.e., this only does the Base 14 fonts)
@@ -258,7 +315,7 @@ def writeXref(self):
258315
"""Write out the xref table."""
259316

260317
self.startxref = self.fh.tell()
261-
self.fh.write("xref\n0 %d\n" % self.nextObject)
318+
self.write("xref\n0 %d\n" % self.nextObject)
262319
i = 0
263320
borken = False
264321
for offset, generation, name in self.xrefTable:
@@ -267,20 +324,21 @@ def writeXref(self):
267324
'No offset for object %d (%s)' % (i, name)
268325
borken = True
269326
else:
270-
self.fh.write("%010d %05d n \n" % (offset, generation))
327+
self.write("%010d %05d n \n" % (offset, generation))
271328
i += 1
272329
if borken:
273330
raise AssertionError, 'Indirect object does not exist'
274331

275332
def writeTrailer(self):
276333
"""Write out the PDF trailer."""
277334

278-
self.fh.write("trailer\n")
279-
self.fh.write(pdfRepr(
335+
self.write("trailer\n")
336+
self.write(pdfRepr(
280337
{'Size': self.nextObject,
281-
'Root': self.rootObject }))
338+
'Root': self.rootObject,
339+
'Info': self.infoObject }))
282340
# Could add 'Info' and 'ID'
283-
self.fh.write("\nstartxref\n%d\n%%%%EOF\n" % self.startxref)
341+
self.write("\nstartxref\n%d\n%%%%EOF\n" % self.startxref)
284342

285343

286344

matplotlibrc.template

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ ps.usedistiller : False # can be: None, ghostscript or xpdf
228228
# but requires ghostscript, xpdf and ps2eps
229229
ps.distiller.res : 6000 # dpi
230230

231+
# pdf backend params
232+
pdf.compression : 6 # integer from 0 to 9
233+
# 0 disables compression (good for debugging)
234+
231235
# Set the verbose flags. This controls how much information
232236
# matplotlib gives you at runtime and where it goes. Ther verbosity
233237
# levels are: silent, helpful, debug, debug-annoying. Any level is

0 commit comments

Comments
 (0)