- split dvifile into several files organized in new dvi directory
[PyX.git] / pyx / pswriter.py
blob97588df6d3ed5db0bbd27e61f4ebc8040a75df2a
1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2005-2006 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2005-2006 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 import cStringIO, copy, time, math
24 import bbox, style, version, type1font, unit, trafo
25 from pyx.dvi import mapfile
27 try:
28 enumerate([])
29 except NameError:
30 # fallback implementation for Python 2.2 and below
31 def enumerate(list):
32 return zip(xrange(len(list)), list)
34 try:
35 dict([])
36 except NameError:
37 # fallback implementation for Python 2.1
38 def dict(list):
39 result = {}
40 for key, value in list:
41 result[key] = value
42 return result
45 class PSregistry:
47 def __init__(self):
48 # in order to keep a consistent order of the registered resources we
49 # not only store them in a hash but also keep an ordered list (up to a
50 # possible merging of resources, in which case the first instance is
51 # kept)
52 self.resourceshash = {}
53 self.resourceslist = []
55 def add(self, resource):
56 rkey = (resource.type, resource.id)
57 if self.resourceshash.has_key(rkey):
58 self.resourceshash[rkey].merge(resource)
59 else:
60 self.resourceshash[rkey] = resource
61 self.resourceslist.append(resource)
63 def mergeregistry(self, registry):
64 for resource in registry.resources:
65 self.add(resource)
67 def output(self, file, writer):
68 """ write all PostScript code of the prolog resources """
69 for resource in self.resourceslist:
70 resource.output(file, writer, self)
73 # Abstract base class
76 class PSresource:
78 """ a PostScript resource """
80 def __init__(self, type, id):
81 # Every PSresource has to have a type and a unique id.
82 # Resources with the same type and id will be merged
83 # when they are registered in the PSregistry
84 self.type = type
85 self.id = id
87 def merge(self, other):
88 """ merge self with other, which has to be a resource of the same type and with
89 the same id"""
90 pass
92 def output(self, file, writer, registry):
93 raise NotImplementedError("output not implemented for %s" % repr(self))
96 # Different variants of prolog items
99 class PSdefinition(PSresource):
101 """ PostScript function definition included in the prolog """
103 def __init__(self, id, body):
104 self.type = "definition"
105 self.id = id
106 self.body = body
108 def output(self, file, writer, registry):
109 file.write("%%%%BeginRessource: %s\n" % self.id)
110 file.write("%(body)s /%(id)s exch def\n" % self.__dict__)
111 file.write("%%EndRessource\n")
114 class epswriter:
116 def __init__(self, document, file, title=None):
117 self._fontmap = None
119 if len(document.pages) != 1:
120 raise ValueError("EPS file can be constructed out of a single page document only")
121 page = document.pages[0]
122 canvas = page.canvas
124 pagefile = cStringIO.StringIO()
125 registry = PSregistry()
126 acontext = context()
127 pagebbox = bbox.empty()
129 page.processPS(pagefile, self, acontext, registry, pagebbox)
131 file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
132 if pagebbox:
133 file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
134 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox.highrestuple_pt())
135 file.write("%%%%Creator: PyX %s\n" % version.version)
136 if title is not None:
137 file.write("%%%%Title: %s\n" % title)
138 file.write("%%%%CreationDate: %s\n" %
139 time.asctime(time.localtime(time.time())))
140 file.write("%%EndComments\n")
142 file.write("%%BeginProlog\n")
143 registry.output(file, self)
144 file.write("%%EndProlog\n")
146 file.write(pagefile.getvalue())
147 pagefile.close()
149 file.write("showpage\n")
150 file.write("%%Trailer\n")
151 file.write("%%EOF\n")
153 def getfontmap(self):
154 if self._fontmap is None:
155 self._fontmap = mapfile.readfontmap(["psfonts.map"])
156 return self._fontmap
159 class pswriter:
161 def __init__(self, document, file, writebbox=0, title=None):
162 # We first have to process the content of the pages, writing them into the stream pagesfile
163 # Doing so, we fill the registry and also calculate the page bounding boxes, which are
164 # stored in page._bbox for every page
165 pagesfile = cStringIO.StringIO()
166 registry = PSregistry()
168 # calculated bounding boxes of the whole document
169 documentbbox = bbox.empty()
171 for nr, page in enumerate(document.pages):
172 # process contents of page
173 pagefile = cStringIO.StringIO()
174 acontext = context()
175 pagebbox = bbox.empty()
176 page.processPS(pagefile, self, acontext, registry, pagebbox)
178 documentbbox += pagebbox
180 pagesfile.write("%%%%Page: %s %d\n" % (page.pagename is None and str(nr+1) or page.pagename, nr+1))
181 if page.paperformat:
182 pagesfile.write("%%%%PageMedia: %s\n" % page.paperformat.name)
183 pagesfile.write("%%%%PageOrientation: %s\n" % (page.rotated and "Landscape" or "Portrait"))
184 if pagebbox and writebbox:
185 pagesfile.write("%%%%PageBoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
187 # page setup section
188 pagesfile.write("%%BeginPageSetup\n")
189 pagesfile.write("/pgsave save def\n")
191 pagesfile.write("%%EndPageSetup\n")
192 pagesfile.write(pagefile.getvalue())
193 pagefile.close()
194 pagesfile.write("pgsave restore\n")
195 pagesfile.write("showpage\n")
196 pagesfile.write("%%PageTrailer\n")
198 file.write("%!PS-Adobe-3.0\n")
199 if documentbbox and writebbox:
200 file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox.lowrestuple_pt())
201 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox.highrestuple_pt())
202 file.write("%%%%Creator: PyX %s\n" % version.version)
203 if title is not None:
204 file.write("%%%%Title: %s\n" % title)
205 file.write("%%%%CreationDate: %s\n" %
206 time.asctime(time.localtime(time.time())))
208 # required paper formats
209 paperformats = {}
210 for page in document.pages:
211 if page.paperformat:
212 paperformats[page.paperformat] = page.paperformat
214 first = 1
215 for paperformat in paperformats.values():
216 if first:
217 file.write("%%DocumentMedia: ")
218 first = 0
219 else:
220 file.write("%%+ ")
221 file.write("%s %d %d 75 white ()\n" % (paperformat.name,
222 unit.topt(paperformat.width),
223 unit.topt(paperformat.height)))
225 # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here
227 file.write("%%%%Pages: %d\n" % len(document.pages))
228 file.write("%%PageOrder: Ascend\n")
229 file.write("%%EndComments\n")
231 # document defaults section
232 #file.write("%%BeginDefaults\n")
233 #file.write("%%EndDefaults\n")
235 # document prolog section
236 file.write("%%BeginProlog\n")
237 registry.output(file, self)
238 file.write("%%EndProlog\n")
240 # document setup section
241 #file.write("%%BeginSetup\n")
242 #file.write("%%EndSetup\n")
244 file.write(pagesfile.getvalue())
245 pagesfile.close()
247 file.write("%%Trailer\n")
248 file.write("%%EOF\n")
250 class context:
252 def __init__(self):
253 self.linewidth_pt = None
254 self.colorspace = None
255 self.selectedfont = None
256 # dictionary mapping font names to dictionaries mapping encoding names to encodings
257 # encodings themselves are mappings from glyphnames to codepoints
258 self.encodings = {}
260 def __call__(self, **kwargs):
261 newcontext = copy.copy(self)
262 for key, value in kwargs.items():
263 setattr(newcontext, key, value)
264 return newcontext