make warnings more explicit concerning the font they refer to (although this leads...
[PyX/mjg.git] / pyx / pswriter.py
blobba1ec0c1ba426fe9c480778c9a72d9e5c1ec086a
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, unit, trafo
26 try:
27 enumerate([])
28 except NameError:
29 # fallback implementation for Python 2.2 and below
30 def enumerate(list):
31 return zip(xrange(len(list)), list)
33 try:
34 dict([])
35 except NameError:
36 # fallback implementation for Python 2.1
37 def dict(list):
38 result = {}
39 for key, value in list:
40 result[key] = value
41 return result
44 class PSregistry:
46 def __init__(self):
47 # in order to keep a consistent order of the registered resources we
48 # not only store them in a hash but also keep an ordered list (up to a
49 # possible merging of resources, in which case the first instance is
50 # kept)
51 self.resourceshash = {}
52 self.resourceslist = []
54 def add(self, resource):
55 rkey = (resource.type, resource.id)
56 if self.resourceshash.has_key(rkey):
57 self.resourceshash[rkey].merge(resource)
58 else:
59 self.resourceshash[rkey] = resource
60 self.resourceslist.append(resource)
62 def mergeregistry(self, registry):
63 for resource in registry.resources:
64 self.add(resource)
66 def output(self, file, writer):
67 """ write all PostScript code of the prolog resources """
68 for resource in self.resourceslist:
69 resource.output(file, writer, self)
72 # Abstract base class
75 class PSresource:
77 """ a PostScript resource """
79 def __init__(self, type, id):
80 # Every PSresource has to have a type and a unique id.
81 # Resources with the same type and id will be merged
82 # when they are registered in the PSregistry
83 self.type = type
84 self.id = id
86 def merge(self, other):
87 """ merge self with other, which has to be a resource of the same type and with
88 the same id"""
89 pass
91 def output(self, file, writer, registry):
92 raise NotImplementedError("output not implemented for %s" % repr(self))
95 # Different variants of prolog items
98 class PSdefinition(PSresource):
100 """ PostScript function definition included in the prolog """
102 def __init__(self, id, body):
103 self.type = "definition"
104 self.id = id
105 self.body = body
107 def output(self, file, writer, registry):
108 file.write("%%%%BeginRessource: %s\n" % self.id)
109 file.write("%(body)s /%(id)s exch def\n" % self.__dict__)
110 file.write("%%EndRessource\n")
113 class epswriter:
115 def __init__(self, document, file, title=None):
116 self._fontmap = None
118 if len(document.pages) != 1:
119 raise ValueError("EPS file can be constructed out of a single page document only")
120 page = document.pages[0]
121 canvas = page.canvas
123 pagefile = cStringIO.StringIO()
124 registry = PSregistry()
125 acontext = context()
126 pagebbox = bbox.empty()
128 page.processPS(pagefile, self, acontext, registry, pagebbox)
130 file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
131 if pagebbox:
132 file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
133 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox.highrestuple_pt())
134 file.write("%%%%Creator: PyX %s\n" % version.version)
135 if title is not None:
136 file.write("%%%%Title: %s\n" % title)
137 file.write("%%%%CreationDate: %s\n" %
138 time.asctime(time.localtime(time.time())))
139 file.write("%%EndComments\n")
141 file.write("%%BeginProlog\n")
142 registry.output(file, self)
143 file.write("%%EndProlog\n")
145 file.write(pagefile.getvalue())
146 pagefile.close()
148 file.write("showpage\n")
149 file.write("%%Trailer\n")
150 file.write("%%EOF\n")
152 def getfontmap(self):
153 if self._fontmap is None:
154 # late import due to cyclic dependency
155 from pyx.dvi import mapfile
156 self._fontmap = mapfile.readfontmap(["psfonts.map"])
157 # config.get("text", "fontmaps", "psfonts.map")
158 # self.fontmap = dvifile.readfontmap(self.fontmaps.split())
159 return self._fontmap
162 class pswriter:
164 def __init__(self, document, file, writebbox=0, title=None):
165 # We first have to process the content of the pages, writing them into the stream pagesfile
166 # Doing so, we fill the registry and also calculate the page bounding boxes, which are
167 # stored in page._bbox for every page
168 pagesfile = cStringIO.StringIO()
169 registry = PSregistry()
171 # calculated bounding boxes of the whole document
172 documentbbox = bbox.empty()
174 for nr, page in enumerate(document.pages):
175 # process contents of page
176 pagefile = cStringIO.StringIO()
177 acontext = context()
178 pagebbox = bbox.empty()
179 page.processPS(pagefile, self, acontext, registry, pagebbox)
181 documentbbox += pagebbox
183 pagesfile.write("%%%%Page: %s %d\n" % (page.pagename is None and str(nr+1) or page.pagename, nr+1))
184 if page.paperformat:
185 pagesfile.write("%%%%PageMedia: %s\n" % page.paperformat.name)
186 pagesfile.write("%%%%PageOrientation: %s\n" % (page.rotated and "Landscape" or "Portrait"))
187 if pagebbox and writebbox:
188 pagesfile.write("%%%%PageBoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
190 # page setup section
191 pagesfile.write("%%BeginPageSetup\n")
192 pagesfile.write("/pgsave save def\n")
194 pagesfile.write("%%EndPageSetup\n")
195 pagesfile.write(pagefile.getvalue())
196 pagefile.close()
197 pagesfile.write("pgsave restore\n")
198 pagesfile.write("showpage\n")
199 pagesfile.write("%%PageTrailer\n")
201 file.write("%!PS-Adobe-3.0\n")
202 if documentbbox and writebbox:
203 file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox.lowrestuple_pt())
204 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox.highrestuple_pt())
205 file.write("%%%%Creator: PyX %s\n" % version.version)
206 if title is not None:
207 file.write("%%%%Title: %s\n" % title)
208 file.write("%%%%CreationDate: %s\n" %
209 time.asctime(time.localtime(time.time())))
211 # required paper formats
212 paperformats = {}
213 for page in document.pages:
214 if page.paperformat:
215 paperformats[page.paperformat] = page.paperformat
217 first = 1
218 for paperformat in paperformats.values():
219 if first:
220 file.write("%%DocumentMedia: ")
221 first = 0
222 else:
223 file.write("%%+ ")
224 file.write("%s %d %d 75 white ()\n" % (paperformat.name,
225 unit.topt(paperformat.width),
226 unit.topt(paperformat.height)))
228 # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here
230 file.write("%%%%Pages: %d\n" % len(document.pages))
231 file.write("%%PageOrder: Ascend\n")
232 file.write("%%EndComments\n")
234 # document defaults section
235 #file.write("%%BeginDefaults\n")
236 #file.write("%%EndDefaults\n")
238 # document prolog section
239 file.write("%%BeginProlog\n")
240 registry.output(file, self)
241 file.write("%%EndProlog\n")
243 # document setup section
244 #file.write("%%BeginSetup\n")
245 #file.write("%%EndSetup\n")
247 file.write(pagesfile.getvalue())
248 pagesfile.close()
250 file.write("%%Trailer\n")
251 file.write("%%EOF\n")
253 class context:
255 def __init__(self):
256 self.linewidth_pt = None
257 self.colorspace = None
258 self.selectedfont = None
259 # dictionary mapping font names to dictionaries mapping encoding names to encodings
260 # encodings themselves are mappings from glyphnames to codepoints
261 self.encodings = {}
263 def __call__(self, **kwargs):
264 newcontext = copy.copy(self)
265 for key, value in kwargs.items():
266 setattr(newcontext, key, value)
267 return newcontext