2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2005 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2002-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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 """The canvas module provides a PostScript canvas class and related classes
26 A canvas holds a collection of all elements and corresponding attributes to be
35 """Base class for everything which can be inserted into a canvas"""
38 """return bounding box of canvasitem or None"""
41 def registerPS(self
, registry
):
42 """register resources needed for the canvasitem in the PS registry"""
45 def registerPDF(self
, registry
):
46 """register resources needed for the canvasitem in the PDF registry"""
49 def outputPS(self
, file, writer
, context
):
50 """write PS code corresponding to canvasitem to file
52 - file has to provide a write(string) method
53 - writer is the PSwriter used for the output
54 - context is used for keeping track of the graphics state, in particular
55 for the emulation of PS behaviour regarding fill and stroke styles, for
56 keeping track of the currently selected font as well as of text regions.
60 def outputPDF(self
, file, writer
, context
):
61 """write PDF code corresponding to canvasitem to file using the given writer
64 - file has to provide a write(string) method
65 - writer contains properties like whether streamcompression is used.
66 - context is used for keeping track of the graphics state, in particular
67 for the emulation of PS behaviour regarding fill and stroke styles, for
68 keeping track of the currently selected font as well as of text regions.
75 import attr
, color
, deco
, deformer
, unit
, style
, trafo
, pswriter
, pdfwriter
, type1font
82 class clip(canvasitem
):
84 """class for use in canvas constructor which clips to a path"""
86 def __init__(self
, path
):
87 """construct a clip instance for a given path"""
91 # as a canvasitem a clipping path has NO influence on the bbox...
95 # ... but for clipping, we nevertheless need the bbox
96 return self
.path
.bbox()
98 def outputPS(self
, file, writer
, context
):
99 file.write("newpath\n")
100 self
.path
.outputPS(file, writer
, context
)
103 def outputPDF(self
, file, writer
, context
):
104 self
.path
.outputPDF(file, writer
, context
)
109 # general canvas class
112 class _canvas(canvasitem
):
114 """a canvas holds a collection of canvasitems"""
116 def __init__(self
, attrs
=[], texrunner
=None):
118 """construct a canvas
120 The canvas can be modfied by supplying args, which have
121 to be instances of one of the following classes:
122 - trafo.trafo (leading to a global transformation of the canvas)
123 - canvas.clip (clips the canvas)
124 - style.strokestyle, style.fillstyle (sets some global attributes of the canvas)
126 Note that, while the first two properties are fixed for the
127 whole canvas, the last one can be changed via canvas.set().
129 The texrunner instance used for the text method can be specified
130 using the texrunner argument. It defaults to text.defaulttexrunner
135 self
.trafo
= trafo
.trafo()
137 if texrunner
is not None:
138 self
.texrunner
= texrunner
140 # prevent cyclic imports
142 self
.texrunner
= text
.defaulttexrunner
145 if isinstance(attr
, trafo
.trafo_pt
):
146 self
.trafo
= self
.trafo
*attr
147 self
.items
.append(attr
)
148 elif isinstance(attr
, clip
):
149 if self
.clipbbox
is None:
150 self
.clipbbox
= attr
.clipbbox().transformed(self
.trafo
)
152 self
.clippbox
*= attr
.clipbbox().transformed(self
.trafo
)
153 self
.items
.append(attr
)
158 """returns bounding box of canvas"""
160 for cmd
in self
.items
:
164 elif abbox
is not None:
167 # transform according to our global transformation and
168 # intersect with clipping bounding box (which has already been
169 # transformed in canvas.__init__())
170 if obbox
is not None and self
.clipbbox
is not None:
171 return obbox
.transformed(self
.trafo
)*self
.clipbbox
172 elif obbox
is not None:
173 return obbox
.transformed(self
.trafo
)
177 def registerPS(self
, registry
):
178 for item
in self
.items
:
179 item
.registerPS(registry
)
181 def registerPDF(self
, registry
):
182 for item
in self
.items
:
183 item
.registerPDF(registry
)
185 def outputPS(self
, file, writer
, context
):
188 file.write("gsave\n")
189 for item
in self
.items
:
190 item
.outputPS(file, writer
, context
)
191 file.write("grestore\n")
193 def outputPDF(self
, file, writer
, context
):
196 file.write("q\n") # gsave
197 for item
in self
.items
:
198 if isinstance(item
, type1font
.text_pt
):
199 if not context
.textregion
:
201 context
.textregion
= 1
203 if context
.textregion
:
205 context
.textregion
= 0
207 item
.outputPDF(file, writer
, context
)
208 if context
.textregion
:
210 context
.textregion
= 0
212 file.write("Q\n") # grestore
214 def insert(self
, item
, attrs
=None):
215 """insert item in the canvas.
217 If attrs are passed, a canvas containing the item is inserted applying attrs.
223 if not isinstance(item
, canvasitem
):
224 raise RuntimeError("only instances of base.canvasitem can be inserted into a canvas")
229 self
.items
.append(sc
)
231 self
.items
.append(item
)
235 def set(self
, attrs
):
236 """sets styles args globally for the rest of the canvas
239 attr
.checkattrs(attrs
, [style
.strokestyle
, style
.fillstyle
])
243 def draw(self
, path
, attrs
):
244 """draw path on canvas using the style given by args
246 The argument attrs consists of PathStyles, which modify
247 the appearance of the path, PathDecos, which add some new
248 visual elements to the path, or trafos, which are applied
249 before drawing the path.
253 attrs
= attr
.mergeattrs(attrs
)
254 attr
.checkattrs(attrs
, [deco
.deco
, deformer
.deformer
, style
.fillstyle
, style
.strokestyle
])
256 for adeformer
in attr
.getattrs(attrs
, [deformer
.deformer
]):
257 path
= adeformer
.deform(path
)
259 styles
= attr
.getattrs(attrs
, [style
.fillstyle
, style
.strokestyle
])
260 dp
= deco
.decoratedpath(path
, styles
=styles
)
262 # add path decorations and modify path accordingly
263 for adeco
in attr
.getattrs(attrs
, [deco
.deco
]):
264 dp
= adeco
.decorate(dp
)
268 def stroke(self
, path
, attrs
=[]):
269 """stroke path on canvas using the style given by args
271 The argument attrs consists of PathStyles, which modify
272 the appearance of the path, PathDecos, which add some new
273 visual elements to the path, or trafos, which are applied
274 before drawing the path.
278 self
.draw(path
, [deco
.stroked
]+list(attrs
))
280 def fill(self
, path
, attrs
=[]):
281 """fill path on canvas using the style given by args
283 The argument attrs consists of PathStyles, which modify
284 the appearance of the path, PathDecos, which add some new
285 visual elements to the path, or trafos, which are applied
286 before drawing the path.
290 self
.draw(path
, [deco
.filled
]+list(attrs
))
292 def settexrunner(self
, texrunner
):
293 """sets the texrunner to be used to within the text and text_pt methods"""
295 self
.texrunner
= texrunner
297 def text(self
, x
, y
, atext
, *args
, **kwargs
):
298 """insert a text into the canvas
300 inserts a textbox created by self.texrunner.text into the canvas
302 returns the inserted textbox"""
304 return self
.insert(self
.texrunner
.text(x
, y
, atext
, *args
, **kwargs
))
307 def text_pt(self
, x
, y
, atext
, *args
):
308 """insert a text into the canvas
310 inserts a textbox created by self.texrunner.text_pt into the canvas
312 returns the inserted textbox"""
314 return self
.insert(self
.texrunner
.text_pt(x
, y
, atext
, *args
))
317 # user canvas class which adds a few convenience methods for single page output
320 class canvas(_canvas
):
322 """a canvas holds a collection of canvasitems"""
324 def writeEPSfile(self
, filename
, **kwargs
):
326 document
.document([document
.page(self
, **kwargs
)]).writeEPSfile(filename
)
328 def writePDFfile(self
, filename
, **kwargs
):
330 document
.document([document
.page(self
, **kwargs
)]).writePDFfile(filename
)
332 def writetofile(self
, filename
, *args
, **kwargs
):
333 if filename
[-4:] == ".eps":
334 self
.writeEPSfile(filename
, *args
, **kwargs
)
335 elif filename
[-4:] == ".pdf":
336 self
.writePDFfile(filename
, *args
, **kwargs
)
338 raise ValueError("writetofile needs a filename extension")