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
, deco
, deformer
, document
, style
, trafo
, 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 return len(self
.items
)
160 def __getitem__(self
, i
):
164 """returns bounding box of canvas"""
166 for cmd
in self
.items
:
170 elif abbox
is not None:
173 # transform according to our global transformation and
174 # intersect with clipping bounding box (which has already been
175 # transformed in canvas.__init__())
176 if obbox
is not None and self
.clipbbox
is not None:
177 return obbox
.transformed(self
.trafo
)*self
.clipbbox
178 elif obbox
is not None:
179 return obbox
.transformed(self
.trafo
)
183 def registerPS(self
, registry
):
184 for item
in self
.items
:
185 item
.registerPS(registry
)
187 def registerPDF(self
, registry
):
188 for item
in self
.items
:
189 item
.registerPDF(registry
)
191 def outputPS(self
, file, writer
, context
):
194 file.write("gsave\n")
195 for item
in self
.items
:
196 item
.outputPS(file, writer
, context
)
197 file.write("grestore\n")
199 def outputPDF(self
, file, writer
, context
):
202 file.write("q\n") # gsave
203 for item
in self
.items
:
204 if isinstance(item
, type1font
.text_pt
):
205 if not context
.textregion
:
207 context
.textregion
= 1
209 if context
.textregion
:
211 context
.textregion
= 0
213 item
.outputPDF(file, writer
, context
)
214 if context
.textregion
:
216 context
.textregion
= 0
218 file.write("Q\n") # grestore
220 def insert(self
, item
, attrs
=None):
221 """insert item in the canvas.
223 If attrs are passed, a canvas containing the item is inserted applying attrs.
229 if not isinstance(item
, canvasitem
):
230 raise RuntimeError("only instances of base.canvasitem can be inserted into a canvas")
235 self
.items
.append(sc
)
237 self
.items
.append(item
)
241 def set(self
, attrs
):
242 """sets styles args globally for the rest of the canvas
245 attr
.checkattrs(attrs
, [style
.strokestyle
, style
.fillstyle
])
249 def draw(self
, path
, attrs
):
250 """draw path on canvas using the style given by args
252 The argument attrs consists of PathStyles, which modify
253 the appearance of the path, PathDecos, which add some new
254 visual elements to the path, or trafos, which are applied
255 before drawing the path.
259 attrs
= attr
.mergeattrs(attrs
)
260 attr
.checkattrs(attrs
, [deco
.deco
, deformer
.deformer
, style
.fillstyle
, style
.strokestyle
])
262 for adeformer
in attr
.getattrs(attrs
, [deformer
.deformer
]):
263 path
= adeformer
.deform(path
)
265 styles
= attr
.getattrs(attrs
, [style
.fillstyle
, style
.strokestyle
])
266 dp
= deco
.decoratedpath(path
, styles
=styles
)
268 # add path decorations and modify path accordingly
269 for adeco
in attr
.getattrs(attrs
, [deco
.deco
]):
270 adeco
.decorate(dp
, self
.texrunner
)
274 def stroke(self
, path
, attrs
=[]):
275 """stroke path on canvas using the style given by args
277 The argument attrs consists of PathStyles, which modify
278 the appearance of the path, PathDecos, which add some new
279 visual elements to the path, or trafos, which are applied
280 before drawing the path.
284 self
.draw(path
, [deco
.stroked
]+list(attrs
))
286 def fill(self
, path
, attrs
=[]):
287 """fill path on canvas using the style given by args
289 The argument attrs consists of PathStyles, which modify
290 the appearance of the path, PathDecos, which add some new
291 visual elements to the path, or trafos, which are applied
292 before drawing the path.
296 self
.draw(path
, [deco
.filled
]+list(attrs
))
298 def settexrunner(self
, texrunner
):
299 """sets the texrunner to be used to within the text and text_pt methods"""
301 self
.texrunner
= texrunner
303 def text(self
, x
, y
, atext
, *args
, **kwargs
):
304 """insert a text into the canvas
306 inserts a textbox created by self.texrunner.text into the canvas
308 returns the inserted textbox"""
310 return self
.insert(self
.texrunner
.text(x
, y
, atext
, *args
, **kwargs
))
313 def text_pt(self
, x
, y
, atext
, *args
):
314 """insert a text into the canvas
316 inserts a textbox created by self.texrunner.text_pt into the canvas
318 returns the inserted textbox"""
320 return self
.insert(self
.texrunner
.text_pt(x
, y
, atext
, *args
))
323 # user canvas class which adds a few convenience methods for single page output
326 def _wrappedindocument(method
):
327 def wrappedindocument(self
, filename
, *args
, **kwargs
):
328 d
= document
.document([document
.page(self
, *args
, **kwargs
)])
329 self
.__doc
__ = method
.__doc
__
330 return method(d
, filename
)
331 return wrappedindocument
333 class canvas(_canvas
):
335 """a canvas holds a collection of canvasitems"""
337 writeEPSfile
= _wrappedindocument(document
.document
.writeEPSfile
)
338 writePSfile
= _wrappedindocument(document
.document
.writePSfile
)
339 writePDFfile
= _wrappedindocument(document
.document
.writePDFfile
)
340 writetofile
= _wrappedindocument(document
.document
.writetofile
)