- move PS font resources to new font.font module
[PyX/mjg.git] / pyx / pswriter.py
blobf1ec04e3479fb7d88199a759bb8dd7c16443b123
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
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 if len(document.pages) != 1:
117 raise ValueError("EPS file can be constructed out of a single page document only")
118 page = document.pages[0]
119 canvas = page.canvas
121 pagefile = cStringIO.StringIO()
122 registry = PSregistry()
123 acontext = context()
124 pagebbox = bbox.empty()
126 page.processPS(pagefile, self, acontext, registry, pagebbox)
128 file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
129 if pagebbox:
130 file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
131 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox.highrestuple_pt())
132 file.write("%%%%Creator: PyX %s\n" % version.version)
133 if title is not None:
134 file.write("%%%%Title: %s\n" % title)
135 file.write("%%%%CreationDate: %s\n" %
136 time.asctime(time.localtime(time.time())))
137 file.write("%%EndComments\n")
139 file.write("%%BeginProlog\n")
140 registry.output(file, self)
141 file.write("%%EndProlog\n")
143 file.write(pagefile.getvalue())
144 pagefile.close()
146 file.write("showpage\n")
147 file.write("%%Trailer\n")
148 file.write("%%EOF\n")
151 class pswriter:
153 def __init__(self, document, file, writebbox=0, title=None):
154 # We first have to process the content of the pages, writing them into the stream pagesfile
155 # Doing so, we fill the registry and also calculate the page bounding boxes, which are
156 # stored in page._bbox for every page
157 pagesfile = cStringIO.StringIO()
158 registry = PSregistry()
160 # calculated bounding boxes of the whole document
161 documentbbox = bbox.empty()
163 for nr, page in enumerate(document.pages):
164 # process contents of page
165 pagefile = cStringIO.StringIO()
166 acontext = context()
167 pagebbox = bbox.empty()
168 page.processPS(pagefile, self, acontext, registry, pagebbox)
170 documentbbox += pagebbox
172 pagesfile.write("%%%%Page: %s %d\n" % (page.pagename is None and str(nr+1) or page.pagename, nr+1))
173 if page.paperformat:
174 pagesfile.write("%%%%PageMedia: %s\n" % page.paperformat.name)
175 pagesfile.write("%%%%PageOrientation: %s\n" % (page.rotated and "Landscape" or "Portrait"))
176 if pagebbox and writebbox:
177 pagesfile.write("%%%%PageBoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
179 # page setup section
180 pagesfile.write("%%BeginPageSetup\n")
181 pagesfile.write("/pgsave save def\n")
183 pagesfile.write("%%EndPageSetup\n")
184 pagesfile.write(pagefile.getvalue())
185 pagefile.close()
186 pagesfile.write("pgsave restore\n")
187 pagesfile.write("showpage\n")
188 pagesfile.write("%%PageTrailer\n")
190 file.write("%!PS-Adobe-3.0\n")
191 if documentbbox and writebbox:
192 file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox.lowrestuple_pt())
193 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox.highrestuple_pt())
194 file.write("%%%%Creator: PyX %s\n" % version.version)
195 if title is not None:
196 file.write("%%%%Title: %s\n" % title)
197 file.write("%%%%CreationDate: %s\n" %
198 time.asctime(time.localtime(time.time())))
200 # required paper formats
201 paperformats = {}
202 for page in document.pages:
203 if page.paperformat:
204 paperformats[page.paperformat] = page.paperformat
206 first = 1
207 for paperformat in paperformats.values():
208 if first:
209 file.write("%%DocumentMedia: ")
210 first = 0
211 else:
212 file.write("%%+ ")
213 file.write("%s %d %d 75 white ()\n" % (paperformat.name,
214 unit.topt(paperformat.width),
215 unit.topt(paperformat.height)))
217 # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here
219 file.write("%%%%Pages: %d\n" % len(document.pages))
220 file.write("%%PageOrder: Ascend\n")
221 file.write("%%EndComments\n")
223 # document defaults section
224 #file.write("%%BeginDefaults\n")
225 #file.write("%%EndDefaults\n")
227 # document prolog section
228 file.write("%%BeginProlog\n")
229 registry.output(file, self)
230 file.write("%%EndProlog\n")
232 # document setup section
233 #file.write("%%BeginSetup\n")
234 #file.write("%%EndSetup\n")
236 file.write(pagesfile.getvalue())
237 pagesfile.close()
239 file.write("%%Trailer\n")
240 file.write("%%EOF\n")
242 class context:
244 def __init__(self):
245 self.linewidth_pt = None
246 self.colorspace = None
247 self.selectedfont = None
248 # dictionary mapping font names to dictionaries mapping encoding names to encodings
249 # encodings themselves are mappings from glyphnames to codepoints
250 self.encodings = {}
252 def __call__(self, **kwargs):
253 newcontext = copy.copy(self)
254 for key, value in kwargs.items():
255 setattr(newcontext, key, value)
256 return newcontext