make the graph functional again ;-)
[PyX.git] / pswriter.py
blob6c288ff9ab8839fe34e00d795798faf2de2f4770
1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2005-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2005-2011 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 io, copy, time, math
24 from . import bbox, config, style, version, unit, trafo, writer
27 class PSregistry:
29 def __init__(self):
30 # in order to keep a consistent order of the registered resources we
31 # not only store them in a hash but also keep an ordered list (up to a
32 # possible merging of resources, in which case the first instance is
33 # kept)
34 self.resourceshash = {}
35 self.resourceslist = []
37 def add(self, resource):
38 rkey = (resource.type, resource.id)
39 if rkey in self.resourceshash:
40 self.resourceshash[rkey].merge(resource)
41 else:
42 self.resourceshash[rkey] = resource
43 self.resourceslist.append(resource)
45 def mergeregistry(self, registry):
46 for resource in registry.resources:
47 self.add(resource)
49 def output(self, file, writer):
50 """ write all PostScript code of the prolog resources """
51 for resource in self.resourceslist:
52 resource.output(file, writer, self)
55 # Abstract base class
58 class PSresource:
60 """ a PostScript resource """
62 def __init__(self, type, id):
63 # Every PSresource has to have a type and a unique id.
64 # Resources with the same type and id will be merged
65 # when they are registered in the PSregistry
66 self.type = type
67 self.id = id
69 def merge(self, other):
70 """ merge self with other, which has to be a resource of the same type and with
71 the same id"""
72 pass
74 def output(self, file, writer, registry):
75 raise NotImplementedError("output not implemented for %s" % repr(self))
77 class PSdefinition(PSresource):
79 """ PostScript function definition included in the prolog """
81 def __init__(self, id, body):
82 self.type = "definition"
83 self.id = id
84 self.body = body
86 def output(self, file, writer, registry):
87 file.write("%%%%BeginResource: %s\n" % self.id)
88 file.write("%(body)s /%(id)s exch def\n" % self.__dict__)
89 file.write("%%EndResource\n")
92 # Writers
95 class _PSwriter:
97 def __init__(self, title=None, strip_fonts=True, text_as_path=False, mesh_as_bitmap=False, mesh_as_bitmap_resolution=300):
98 self._fontmap = None
99 self.title = title
100 self.strip_fonts = strip_fonts
101 self.text_as_path = text_as_path
102 self.mesh_as_bitmap = mesh_as_bitmap
103 self.mesh_as_bitmap_resolution = mesh_as_bitmap_resolution
105 # dictionary mapping font names to dictionaries mapping encoding names to encodings
106 # encodings themselves are mappings from glyphnames to codepoints
107 self.encodings = {}
109 def writeinfo(self, file):
110 file.write("%%%%Creator: PyX %s\n" % version.version)
111 if self.title is not None:
112 file.write("%%%%Title: %s\n" % self.title)
113 file.write("%%%%CreationDate: %s\n" %
114 time.asctime(time.localtime(time.time())))
116 def getfontmap(self):
117 if self._fontmap is None:
118 # late import due to cyclic dependency
119 from pyx.dvi import mapfile
120 fontmapfiles = config.getlist("text", "psfontmaps", ["psfonts.map"])
121 self._fontmap = mapfile.readfontmap(fontmapfiles)
122 return self._fontmap
125 class EPSwriter(_PSwriter):
127 def __init__(self, document, file, **kwargs):
128 _PSwriter.__init__(self, **kwargs)
129 file = writer.writer(file)
131 if len(document.pages) != 1:
132 raise ValueError("EPS file can be constructed out of a single page document only")
133 page = document.pages[0]
134 canvas = page.canvas
136 pagefile = writer.writer(io.BytesIO())
137 registry = PSregistry()
138 acontext = context()
139 pagebbox = bbox.empty()
141 page.processPS(pagefile, self, acontext, registry, pagebbox)
143 file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
144 if pagebbox:
145 file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
146 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox.highrestuple_pt())
147 self.writeinfo(file)
148 file.write("%%EndComments\n")
150 file.write("%%BeginProlog\n")
151 registry.output(file, self)
152 file.write("%%EndProlog\n")
154 file.write_bytes(pagefile.file.getvalue())
155 pagefile.close()
157 file.write("showpage\n")
158 file.write("%%Trailer\n")
159 file.write("%%EOF\n")
162 class PSwriter(_PSwriter):
164 def __init__(self, document, file, writebbox=False, **kwargs):
165 _PSwriter.__init__(self, **kwargs)
166 file = writer.writer(file)
168 # We first have to process the content of the pages, writing them into the stream pagesfile
169 # Doing so, we fill the registry and also calculate the page bounding boxes, which are
170 # stored in page._bbox for every page
171 pagesfile = writer.writer(io.BytesIO())
172 registry = PSregistry()
174 # calculated bounding boxes of the whole document
175 documentbbox = bbox.empty()
177 for nr, page in enumerate(document.pages):
178 # process contents of page
179 pagefile = writer.writer(io.BytesIO())
180 acontext = context()
181 pagebbox = bbox.empty()
182 page.processPS(pagefile, self, acontext, registry, pagebbox)
184 documentbbox += pagebbox
186 pagesfile.write("%%%%Page: %s %d\n" % (page.pagename is None and str(nr+1) or page.pagename, nr+1))
187 if page.paperformat:
188 pagesfile.write("%%%%PageMedia: %s\n" % page.paperformat.name)
189 pagesfile.write("%%%%PageOrientation: %s\n" % (page.rotated and "Landscape" or "Portrait"))
190 if pagebbox and writebbox:
191 pagesfile.write("%%%%PageBoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt())
193 # page setup section
194 pagesfile.write("%%BeginPageSetup\n")
195 pagesfile.write("/pgsave save def\n")
197 pagesfile.write("%%EndPageSetup\n")
198 pagesfile.write_bytes(pagefile.file.getvalue())
199 pagefile.close()
200 pagesfile.write("pgsave restore\n")
201 pagesfile.write("showpage\n")
202 pagesfile.write("%%PageTrailer\n")
204 file.write("%!PS-Adobe-3.0\n")
205 if documentbbox and writebbox:
206 file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox.lowrestuple_pt())
207 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox.highrestuple_pt())
208 self.writeinfo(file)
210 # required paper formats
211 paperformats = {}
212 for page in document.pages:
213 if page.paperformat:
214 paperformats[page.paperformat] = page.paperformat
216 first = 1
217 for paperformat in list(paperformats.values()):
218 if first:
219 file.write("%%DocumentMedia: ")
220 first = 0
221 else:
222 file.write("%%+ ")
223 file.write("%s %d %d 75 white ()\n" % (paperformat.name,
224 unit.topt(paperformat.width),
225 unit.topt(paperformat.height)))
227 # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here
229 file.write("%%%%Pages: %d\n" % len(document.pages))
230 file.write("%%PageOrder: Ascend\n")
231 file.write("%%EndComments\n")
233 # document defaults section
234 #file.write("%%BeginDefaults\n")
235 #file.write("%%EndDefaults\n")
237 # document prolog section
238 file.write("%%BeginProlog\n")
239 registry.output(file, self)
240 file.write("%%EndProlog\n")
242 # document setup section
243 #file.write("%%BeginSetup\n")
244 #file.write("%%EndSetup\n")
246 file.write_bytes(pagesfile.file.getvalue())
247 pagesfile.close()
249 file.write("%%Trailer\n")
250 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 self.fillrule = 0
261 def __call__(self, **kwargs):
262 newcontext = copy.copy(self)
263 for key, value in list(kwargs.items()):
264 setattr(newcontext, key, value)
265 return newcontext