2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2005 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 import copy
, time
, math
25 import style
, version
, type1font
, unit
31 # in order to keep a consistent order of the registered resources we
32 # not only store them in a hash but also keep an ordered list (up to a
33 # possible merging of resources, in which case the first instance is
35 self
.resourceshash
= {}
36 self
.resourceslist
= []
38 def add(self
, resource
):
39 rkey
= (resource
.type, resource
.id)
40 if self
.resourceshash
.has_key(rkey
):
41 self
.resourceshash
[rkey
].merge(resource
)
43 self
.resourceshash
[rkey
] = resource
44 self
.resourceslist
.append(resource
)
46 def outputPS(self
, file, writer
):
47 """ write all PostScript code of the prolog resources """
48 for resource
in self
.resourceslist
:
49 resource
.outputPS(file, writer
, self
)
57 """ a PostScript resource """
59 def __init__(self
, type, id):
60 # Every PSresource has to have a type and a unique id.
61 # Resources with the same type and id will be merged
62 # when they are registered in the PSregistry
66 def merge(self
, other
):
67 """ merge self with other, which has to be a resource of the same type and with
71 def outputPS(self
, file, writer
, registry
):
72 raise NotImplementedError("outputPS not implemented for %s" % repr(self
))
75 # Different variants of prolog items
78 class PSdefinition(PSresource
):
80 """ PostScript function definition included in the prolog """
82 def __init__(self
, id, body
):
83 self
.type = "definition"
87 def outputPS(self
, file, writer
, registry
):
88 file.write("%%%%BeginRessource: %s\n" % self
.id)
89 file.write("%(body)s /%(id)s exch def\n" % self
.__dict
__)
90 file.write("%%EndRessource\n")
95 def __init__(self
, font
, chars
, registry
):
97 registry
.add(PSfontfile(font
.basefontname
,
102 registry
.add(_ReEncodeFont
)
103 registry
.add(PSfontencoding(font
.encoding
))
104 registry
.add(PSfontreencoding(font
.name
,
109 class PSfontfile(PSresource
):
111 """ PostScript font definition included in the prolog """
113 def __init__(self
, name
, filename
, encoding
, chars
):
114 """ include type 1 font defined by the following parameters
116 - name: name of the PostScript font
117 - filename: name (without path) of file containing the font definition
118 - encfilename: name (without path) of file containing used encoding of font
119 or None (if no encoding file used)
120 - chars: character list to fill usedchars
124 # Note that here we only need the encoding for selecting the used glyphs!
126 self
.type = "fontfile"
127 self
.id = self
.name
= name
128 self
.filename
= filename
130 self
.encodingfilename
= None
132 self
.encodingfilename
= encoding
.filename
135 self
.usedchars
[char
] = 1
137 def merge(self
, other
):
138 if self
.encodingfilename
== other
.encodingfilename
:
139 self
.usedchars
.update(other
.usedchars
)
141 self
.usedchars
= None # stripping of font not possible
143 def outputPS(self
, file, writer
, registry
):
144 fontfile
= type1font
.fontfile(self
.name
, self
.filename
, self
.usedchars
, self
.encodingfilename
)
145 fontfile
.outputPS(file, writer
, registry
)
148 class PSfontencoding(PSresource
):
150 """ PostScript font encoding vector included in the prolog """
152 def __init__(self
, encoding
):
153 """ include font encoding vector specified by encoding """
155 self
.type = "fontencoding"
156 self
.id = encoding
.name
157 self
.encoding
= encoding
159 def outputPS(self
, file, writer
, registry
):
160 encodingfile
= type1font
.encodingfile(self
.encoding
.name
, self
.encoding
.filename
)
161 encodingfile
.outputPS(file, writer
, registry
)
164 class PSfontreencoding(PSresource
):
166 """ PostScript font re-encoding directive included in the prolog """
168 def __init__(self
, fontname
, basefontname
, encodingname
):
169 """ include font re-encoding directive specified by
171 - fontname: PostScript FontName of the new reencoded font
172 - basefontname: PostScript FontName of the original font
173 - encname: name of the encoding
174 - font: a reference to the font instance (temporarily added for pdf support)
176 Before being able to reencode a font, you have to include the
177 encoding via a fontencoding prolog item with name=encname
181 self
.type = "fontreencoding"
182 self
.id = self
.fontname
= fontname
183 self
.basefontname
= basefontname
184 self
.encodingname
= encodingname
186 def outputPS(self
, file, writer
, registry
):
187 file.write("%%%%BeginProcSet: %s\n" % self
.fontname
)
188 file.write("/%s /%s %s ReEncodeFont\n" % (self
.basefontname
, self
.fontname
, self
.encodingname
))
189 file.write("%%EndProcSet\n")
192 _ReEncodeFont
= PSdefinition("ReEncodeFont", """{
195 /newencoding exch def
196 /newfontname exch def
197 /basefontname exch def
198 /basefontdict basefontname findfont def
199 /newfontdict basefontdict maxlength dict def
201 exch dup dup /FID ne exch /Encoding ne and
202 { exch newfontdict 3 1 roll put }
206 newfontdict /FontName newfontname put
207 newfontdict /Encoding newencoding put
208 newfontname newfontdict definefont pop
215 def __init__(self
, document
, filename
):
216 if len(document
.pages
) != 1:
217 raise ValueError("EPS file can be construced out of a single page document only")
218 page
= document
.pages
[0]
221 if filename
[-4:] != ".eps":
222 filename
= filename
+ ".eps"
224 file = open(filename
, "w")
226 raise IOError("cannot open output file")
229 bbox
.enlarge(page
.bboxenlarge
)
230 pagetrafo
= page
.pagetrafo(bbox
)
232 # if a page transformation is necessary, we have to adjust the bounding box
234 if pagetrafo
is not None:
235 bbox
.transform(pagetrafo
)
237 file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
238 bbox
.outputPS(file, self
)
239 file.write("%%%%Creator: PyX %s\n" % version
.version
)
240 file.write("%%%%Title: %s\n" % filename
)
241 file.write("%%%%CreationDate: %s\n" %
242 time
.asctime(time
.localtime(time
.time())))
243 file.write("%%EndComments\n")
245 file.write("%%BeginProlog\n")
246 registry
= PSregistry()
247 canvas
.registerPS(registry
)
248 registry
.outputPS(file, self
)
249 file.write("%%EndProlog\n")
252 # apply a possible page transformation
253 if pagetrafo
is not None:
254 pagetrafo
.outputPS(file, self
, acontext
)
256 style
.linewidth
.normal
.outputPS(file, self
, acontext
)
258 # here comes the canvas content
259 canvas
.outputPS(file, self
, acontext
)
261 file.write("showpage\n")
262 file.write("%%Trailer\n")
263 file.write("%%EOF\n")
268 def __init__(self
, document
, filename
, insertpagebbox
=0):
269 if filename
[-4:] != ".ps":
270 filename
= filename
+ ".ps"
272 file = open(filename
, "w")
274 raise IOError("cannot open output file")
276 # calculated bounding boxes of separate pages and the bounding box of the whole document
278 for page
in document
.pages
:
280 page
.bbox
= canvas
.bbox()
281 page
.bbox
.enlarge(page
.bboxenlarge
)
282 page
._pagetrafo
= page
.pagetrafo(page
.bbox
)
283 # if a page transformation is necessary, we have to adjust the bounding box
285 if page
._pagetrafo
is not None and page
.bbox
is not None:
286 page
.bbox
.transform(page
._pagetrafo
)
287 if documentbbox
is None:
288 documentbbox
= page
.bbox
.enlarged(0)
289 elif page
.bbox
is not None:
290 documentbbox
+= page
.bbox
292 file.write("%!PS-Adobe-3.0\n")
293 documentbbox
.outputPS(file, self
)
294 file.write("%%%%Creator: PyX %s\n" % version
.version
)
295 file.write("%%%%Title: %s\n" % filename
)
296 file.write("%%%%CreationDate: %s\n" %
297 time
.asctime(time
.localtime(time
.time())))
299 # required paper formats
301 for page
in document
.pages
:
302 paperformats
[page
.paperformat
] = page
.paperformat
305 for paperformat
in paperformats
.values():
307 file.write("%%DocumentMedia: ")
311 file.write("%s %d %d 75 white ()\n" % (paperformat
.name
,
312 unit
.topt(paperformat
.width
),
313 unit
.topt(paperformat
.height
)))
315 # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here
317 file.write("%%%%Pages: %d\n" % len(document
.pages
))
318 file.write("%%PageOrder: Ascend\n")
319 file.write("%%EndComments\n")
321 # document defaults section
322 #file.write("%%BeginDefaults\n")
323 #file.write("%%EndDefaults\n")
325 # document prolog section
326 file.write("%%BeginProlog\n")
327 registry
= PSregistry()
328 for page
in document
.pages
:
329 page
.canvas
.registerPS(registry
)
330 registry
.outputPS(file, self
)
331 file.write("%%EndProlog\n")
333 # document setup section
334 #file.write("%%BeginSetup\n")
335 #file.write("%%EndSetup\n")
338 for nr
, page
in enumerate(document
.pages
):
339 file.write("%%%%Page: %s %d\n" % (page
.pagename
is None and str(nr
+1) or page
.pagename
, nr
+1))
340 file.write("%%%%PageMedia: %s\n" % page
.paperformat
.name
)
341 file.write("%%%%PageOrientation: %s\n" % (page
.rotated
and "Landscape" or "Portrait"))
343 file.write("%%%%PageBoundingBox: %d %d %d %d\n" % (math
.floor(page
.bbox
.llx_pt
), math
.floor(page
.bbox
.lly_pt
),
344 math
.ceil(page
.bbox
.urx_pt
), math
.ceil(page
.bbox
.ury_pt
)))
347 file.write("%%BeginPageSetup\n")
348 file.write("/pgsave save def\n")
351 # apply a possible page transformation
352 if page
._pagetrafo
is not None:
353 page
._pagetrafo
.outputPS(file, self
, acontext
)
355 style
.linewidth
.normal
.outputPS(file, self
, acontext
)
356 file.write("%%EndPageSetup\n")
358 # here comes the actual content
359 page
.canvas
.outputPS(file, self
, acontext
)
360 file.write("pgsave restore\n")
361 file.write("showpage\n")
362 file.write("%%PageTrailer\n")
364 file.write("%%Trailer\n")
365 file.write("%%EOF\n")
370 self
.linewidth_pt
= None
371 self
.colorspace
= None
374 def __call__(self
, **kwargs
):
375 newcontext
= copy
.copy(self
)
376 for key
, value
in kwargs
.items():
377 setattr(newcontext
, key
, value
)