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
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
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
)
42 self
.resourceshash
[rkey
] = resource
43 self
.resourceslist
.append(resource
)
45 def mergeregistry(self
, registry
):
46 for resource
in registry
.resources
:
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
)
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
69 def merge(self
, other
):
70 """ merge self with other, which has to be a resource of the same type and with
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"
86 def output(self
, file, writer
, registry
):
87 file.write("%%%%BeginResource: %s\n" % self
.id)
88 file.write_bytes(self
.body
)
89 file.write(" /%s exch def\n" % self
.id)
90 file.write("%%EndResource\n")
98 def __init__(self
, title
=None, strip_fonts
=True, text_as_path
=False, mesh_as_bitmap
=False, mesh_as_bitmap_resolution
=300):
101 self
.strip_fonts
= strip_fonts
102 self
.text_as_path
= text_as_path
103 self
.mesh_as_bitmap
= mesh_as_bitmap
104 self
.mesh_as_bitmap_resolution
= mesh_as_bitmap_resolution
106 # dictionary mapping font names to dictionaries mapping encoding names to encodings
107 # encodings themselves are mappings from glyphnames to codepoints
110 def writeinfo(self
, file):
111 file.write("%%%%Creator: PyX %s\n" % version
.version
)
112 if self
.title
is not None:
113 file.write("%%%%Title: %s\n" % self
.title
)
114 file.write("%%%%CreationDate: %s\n" %
115 time
.asctime(time
.localtime(time
.time())))
117 def getfontmap(self
):
118 if self
._fontmap
is None:
119 # late import due to cyclic dependency
120 from pyx
.dvi
import mapfile
121 fontmapfiles
= config
.getlist("text", "psfontmaps", ["psfonts.map"])
122 self
._fontmap
= mapfile
.readfontmap(fontmapfiles
)
126 class EPSwriter(_PSwriter
):
128 def __init__(self
, document
, file, **kwargs
):
129 _PSwriter
.__init
__(self
, **kwargs
)
130 file = writer
.writer(file)
132 if len(document
.pages
) != 1:
133 raise ValueError("EPS file can be constructed out of a single page document only")
134 page
= document
.pages
[0]
137 pagefile
= writer
.writer(io
.BytesIO())
138 registry
= PSregistry()
140 pagebbox
= bbox
.empty()
142 page
.processPS(pagefile
, self
, acontext
, registry
, pagebbox
)
144 file.write("%!PS-Adobe-3.0 EPSF-3.0\n")
146 file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox
.lowrestuple_pt())
147 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox
.highrestuple_pt())
149 file.write("%%EndComments\n")
151 file.write("%%BeginProlog\n")
152 registry
.output(file, self
)
153 file.write("%%EndProlog\n")
155 file.write_bytes(pagefile
.file.getvalue())
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())
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))
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())
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 pagesfile
.write("pgsave restore\n")
200 pagesfile
.write("showpage\n")
201 pagesfile
.write("%%PageTrailer\n")
203 file.write("%!PS-Adobe-3.0\n")
204 if documentbbox
and writebbox
:
205 file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox
.lowrestuple_pt())
206 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox
.highrestuple_pt())
209 # required paper formats
211 for page
in document
.pages
:
213 paperformats
[page
.paperformat
] = page
.paperformat
216 for paperformat
in list(paperformats
.values()):
218 file.write("%%DocumentMedia: ")
222 file.write("%s %d %d 75 white ()\n" % (paperformat
.name
,
223 unit
.topt(paperformat
.width
),
224 unit
.topt(paperformat
.height
)))
226 # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here
228 file.write("%%%%Pages: %d\n" % len(document
.pages
))
229 file.write("%%PageOrder: Ascend\n")
230 file.write("%%EndComments\n")
232 # document defaults section
233 #file.write("%%BeginDefaults\n")
234 #file.write("%%EndDefaults\n")
236 # document prolog section
237 file.write("%%BeginProlog\n")
238 registry
.output(file, self
)
239 file.write("%%EndProlog\n")
241 # document setup section
242 #file.write("%%BeginSetup\n")
243 #file.write("%%EndSetup\n")
245 file.write_bytes(pagesfile
.file.getvalue())
247 file.write("%%Trailer\n")
248 file.write("%%EOF\n")
254 self
.linewidth_pt
= None
255 self
.colorspace
= None
256 self
.selectedfont
= None
259 def __call__(self
, **kwargs
):
260 newcontext
= copy
.copy(self
)
261 for key
, value
in list(kwargs
.items()):
262 setattr(newcontext
, key
, value
)