2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2005-2006 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2005-2006 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import cStringIO
, copy
, time
, math
25 import bbox
, style
, version
, type1font
, unit
30 # fallback implementation for Python 2.2 and below
32 return zip(xrange(len(list)), list)
38 # in order to keep a consistent order of the registered resources we
39 # not only store them in a hash but also keep an ordered list (up to a
40 # possible merging of resources, in which case the first instance is
42 self
.resourceshash
= {}
43 self
.resourceslist
= []
45 def add(self
, resource
):
46 rkey
= (resource
.type, resource
.id)
47 if self
.resourceshash
.has_key(rkey
):
48 self
.resourceshash
[rkey
].merge(resource
)
50 self
.resourceshash
[rkey
] = resource
51 self
.resourceslist
.append(resource
)
53 def mergeregistry(self
, registry
):
54 for resource
in registry
.resources
:
57 def output(self
, file, writer
):
58 """ write all PostScript code of the prolog resources """
59 for resource
in self
.resourceslist
:
60 resource
.output(file, writer
, self
)
68 """ a PostScript resource """
70 def __init__(self
, type, id):
71 # Every PSresource has to have a type and a unique id.
72 # Resources with the same type and id will be merged
73 # when they are registered in the PSregistry
77 def merge(self
, other
):
78 """ merge self with other, which has to be a resource of the same type and with
82 def output(self
, file, writer
, registry
):
83 raise NotImplementedError("output not implemented for %s" % repr(self
))
86 # Different variants of prolog items
89 class PSdefinition(PSresource
):
91 """ PostScript function definition included in the prolog """
93 def __init__(self
, id, body
):
94 self
.type = "definition"
98 def output(self
, file, writer
, registry
):
99 file.write("%%%%BeginRessource: %s\n" % self
.id)
100 file.write("%(body)s /%(id)s exch def\n" % self
.__dict
__)
101 file.write("%%EndRessource\n")
106 def __init__(self
, font
, chars
, registry
):
108 registry
.add(PSfontfile(font
.basefontname
,
113 registry
.add(_ReEncodeFont
)
114 registry
.add(PSfontencoding(font
.encoding
))
115 registry
.add(PSfontreencoding(font
.name
,
120 class PSfontfile(PSresource
):
122 """ PostScript font definition included in the prolog """
124 def __init__(self
, name
, filename
, encoding
, chars
):
125 """ include type 1 font defined by the following parameters
127 - name: name of the PostScript font
128 - filename: name (without path) of file containing the font definition
129 - encfilename: name (without path) of file containing used encoding of font
130 or None (if no encoding file used)
131 - chars: character list to fill usedchars
135 # Note that here we only need the encoding for selecting the used glyphs!
137 self
.type = "fontfile"
138 self
.id = self
.name
= name
139 self
.filename
= filename
141 self
.encodingfilename
= None
143 self
.encodingfilename
= encoding
.filename
146 self
.usedchars
[char
] = 1
150 def merge(self
, other
):
151 if self
.encodingfilename
== other
.encodingfilename
:
152 self
.usedchars
.update(other
.usedchars
)
154 # TODO: need to resolve the encoding when several encodings are in the play
157 def output(self
, file, writer
, registry
):
159 font
= font
.t1font
.T1pfbfont(self
.filename
)
161 file.write("%%%%BeginFont: %s\n" % self
.name
)
162 # file.write("%%Included glyphs: %s\n" % " ".join(usedglyphs))
164 # XXX: access to the encoding file
165 if self
.encodingfilename
:
166 encodingfile
= type1font
.encodingfile(self
.encodingfilename
, self
.encodingfilename
)
167 usedglyphs
= dict([(encodingfile
.decode(char
)[1:], 1) for char
in self
.usedchars
.keys()])
170 usedglyphs
= dict([(font
.encoding
.decode(char
), 1) for char
in self
.usedchars
.keys()])
171 strippedfont
= font
.getstrippedfont(usedglyphs
)
174 strippedfont
.outputPS(file, writer
)
175 file.write("\n%%EndFont\n")
178 class PSfontencoding(PSresource
):
180 """ PostScript font encoding vector included in the prolog """
182 def __init__(self
, encoding
):
183 """ include font encoding vector specified by encoding """
185 self
.type = "fontencoding"
186 self
.id = encoding
.name
187 self
.encoding
= encoding
189 def output(self
, file, writer
, registry
):
190 encodingfile
= type1font
.encodingfile(self
.encoding
.name
, self
.encoding
.filename
)
191 encodingfile
.outputPS(file, writer
)
194 class PSfontreencoding(PSresource
):
196 """ PostScript font re-encoding directive included in the prolog """
198 def __init__(self
, fontname
, basefontname
, encodingname
):
199 """ include font re-encoding directive specified by
201 - fontname: PostScript FontName of the new reencoded font
202 - basefontname: PostScript FontName of the original font
203 - encname: name of the encoding
204 - font: a reference to the font instance (temporarily added for pdf support)
206 Before being able to reencode a font, you have to include the
207 encoding via a fontencoding prolog item with name=encname
211 self
.type = "fontreencoding"
212 self
.id = self
.fontname
= fontname
213 self
.basefontname
= basefontname
214 self
.encodingname
= encodingname
216 def output(self
, file, writer
, registry
):
217 file.write("%%%%BeginProcSet: %s\n" % self
.fontname
)
218 file.write("/%s /%s %s ReEncodeFont\n" % (self
.basefontname
, self
.fontname
, self
.encodingname
))
219 file.write("%%EndProcSet\n")
222 _ReEncodeFont
= PSdefinition("ReEncodeFont", """{
225 /newencoding exch def
226 /newfontname exch def
227 /basefontname exch def
228 /basefontdict basefontname findfont def
229 /newfontdict basefontdict maxlength dict def
231 exch dup dup /FID ne exch /Encoding ne and
232 { exch newfontdict 3 1 roll put }
236 newfontdict /FontName newfontname put
237 newfontdict /Encoding newencoding put
238 newfontname newfontdict definefont pop
245 def __init__(self
, document
, filename
):
246 if len(document
.pages
) != 1:
247 raise ValueError("EPS file can be construced out of a single page document only")
248 page
= document
.pages
[0]
251 if not filename
.endswith(".eps"):
252 filename
= filename
+ ".eps"
254 file = open(filename
, "w")
256 raise IOError("cannot open output file")
258 pagefile
= cStringIO
.StringIO()
259 registry
= PSregistry()
261 pagebbox
= bbox
.empty()
263 page
.processPS(pagefile
, self
, acontext
, registry
, pagebbox
)
265 file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
267 file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox
.lowrestuple_pt())
268 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox
.highrestuple_pt())
269 file.write("%%%%Creator: PyX %s\n" % version
.version
)
270 file.write("%%%%Title: %s\n" % filename
)
271 file.write("%%%%CreationDate: %s\n" %
272 time
.asctime(time
.localtime(time
.time())))
273 file.write("%%EndComments\n")
275 file.write("%%BeginProlog\n")
276 registry
.output(file, self
)
277 file.write("%%EndProlog\n")
279 file.write(pagefile
.getvalue())
282 file.write("showpage\n")
283 file.write("%%Trailer\n")
284 file.write("%%EOF\n")
289 def __init__(self
, document
, filename
, writebbox
=0):
290 if not filename
.endswith(".ps"):
291 filename
= filename
+ ".ps"
293 file = open(filename
, "w")
295 raise IOError("cannot open output file")
298 # We first have to process the content of the pages, writing them into the stream pagesfile
299 # Doing so, we fill the registry and also calculate the page bounding boxes, which are
300 # stored in page._bbox for every page
301 pagesfile
= cStringIO
.StringIO()
302 registry
= PSregistry()
304 # calculated bounding boxes of the whole document
305 documentbbox
= bbox
.empty()
307 for nr
, page
in enumerate(document
.pages
):
308 # process contents of page
309 pagefile
= cStringIO
.StringIO()
311 pagebbox
= bbox
.empty()
312 page
.processPS(pagefile
, self
, acontext
, registry
, pagebbox
)
314 documentbbox
+= pagebbox
316 pagesfile
.write("%%%%Page: %s %d\n" % (page
.pagename
is None and str(nr
+1) or page
.pagename
, nr
+1))
318 pagesfile
.write("%%%%PageMedia: %s\n" % page
.paperformat
.name
)
319 pagesfile
.write("%%%%PageOrientation: %s\n" % (page
.rotated
and "Landscape" or "Portrait"))
320 if pagebbox
and writebbox
:
321 pagesfile
.write("%%%%PageBoundingBox: %d %d %d %d\n" % pagebbox
.lowrestuple_pt())
324 pagesfile
.write("%%BeginPageSetup\n")
325 pagesfile
.write("/pgsave save def\n")
327 pagesfile
.write("%%EndPageSetup\n")
328 pagesfile
.write(pagefile
.getvalue())
330 pagesfile
.write("pgsave restore\n")
331 pagesfile
.write("showpage\n")
332 pagesfile
.write("%%PageTrailer\n")
334 file.write("%!PS-Adobe-3.0\n")
335 if documentbbox
and writebbox
:
336 file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox
.lowrestuple_pt())
337 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox
.highrestuple_pt())
338 file.write("%%%%Creator: PyX %s\n" % version
.version
)
339 file.write("%%%%Title: %s\n" % filename
)
340 file.write("%%%%CreationDate: %s\n" %
341 time
.asctime(time
.localtime(time
.time())))
343 # required paper formats
345 for page
in document
.pages
:
347 paperformats
[page
.paperformat
] = page
.paperformat
350 for paperformat
in paperformats
.values():
352 file.write("%%DocumentMedia: ")
356 file.write("%s %d %d 75 white ()\n" % (paperformat
.name
,
357 unit
.topt(paperformat
.width
),
358 unit
.topt(paperformat
.height
)))
360 # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here
362 file.write("%%%%Pages: %d\n" % len(document
.pages
))
363 file.write("%%PageOrder: Ascend\n")
364 file.write("%%EndComments\n")
366 # document defaults section
367 #file.write("%%BeginDefaults\n")
368 #file.write("%%EndDefaults\n")
370 # document prolog section
371 file.write("%%BeginProlog\n")
372 registry
.output(file, self
)
373 file.write("%%EndProlog\n")
375 # document setup section
376 #file.write("%%BeginSetup\n")
377 #file.write("%%EndSetup\n")
379 file.write(pagesfile
.getvalue())
382 file.write("%%Trailer\n")
383 file.write("%%EOF\n")
388 self
.linewidth_pt
= None
389 self
.colorspace
= None
392 def __call__(self
, **kwargs
):
393 newcontext
= copy
.copy(self
)
394 for key
, value
in kwargs
.items():
395 setattr(newcontext
, key
, value
)