1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2005 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002-2005 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 cStringIO
, math
, warnings
24 import attr
, canvas
, helper
, path
, pdfwriter
, pswriter
, style
, unit
, trafo
26 # TODO: pattern should not derive from canvas but wrap a canvas
28 class pattern(canvas
._canvas
, attr
.exclusiveattr
, style
.fillstyle
):
30 def __init__(self
, painttype
=1, tilingtype
=1, xstep
=None, ystep
=None, bbox
=None, trafo
=None, **kwargs
):
31 canvas
._canvas
.__init
__(self
, **kwargs
)
32 attr
.exclusiveattr
.__init
__(self
, pattern
)
33 self
.id = "pattern%d" % id(self
)
35 if painttype
not in (1,2):
36 raise ValueError("painttype must be 1 or 2")
37 self
.painttype
= painttype
38 if tilingtype
not in (1,2,3):
39 raise ValueError("tilingtype must be 1, 2 or 3")
40 self
.tilingtype
= tilingtype
43 self
.patternbbox
= bbox
44 self
.patterntrafo
= trafo
46 def __call__(self
, painttype
=helper
.nodefault
, tilingtype
=helper
.nodefault
, xstep
=helper
.nodefault
, ystep
=helper
.nodefault
,
47 bbox
=helper
.nodefault
, trafo
=helper
.nodefault
):
48 if painttype
is helper
.nodefault
:
49 painttype
= self
.painttype
50 if tilingtype
is helper
.nodefault
:
51 tilingtype
= self
.tilingtype
52 if xstep
is helper
.nodefault
:
54 if ystep
is helper
.nodefault
:
56 if bbox
is helper
.nodefault
:
58 if trafo
is helper
.nodefault
:
60 return pattern(painttype
, tilingtype
, xstep
, ystep
, bbox
, trafo
)
65 def outputPS(self
, file, writer
, context
):
66 file.write("%s setpattern\n" % self
.id)
68 def outputPDF(self
, file, writer
, context
):
69 if context
.colorspace
!= "Pattern":
70 # we only set the fill color space (see next comment)
71 file.write("/Pattern cs\n")
72 context
.colorspace
= "Pattern"
73 if context
.strokeattr
:
74 # using patterns as stroke colors doesn't seem to work, so
75 # we just don't do this...
78 file.write("/%s scn\n"% self
.id)
80 def registerPS(self
, registry
):
81 canvas
._canvas
.registerPS(self
, registry
)
82 realpatternbbox
= canvas
._canvas
.bbox(self
)
83 if self
.xstep
is None:
84 xstep
= unit
.topt(realpatternbbox
.width())
86 xstep
= unit
.topt(self
.xstep
)
87 if self
.ystep
is None:
88 ystep
= unit
.topt(realpatternbbox
.height())
90 ystep
= unit
.topt(self
.ystep
)
92 raise ValueError("xstep in pattern cannot be zero")
94 raise ValueError("ystep in pattern cannot be zero")
95 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(5*unit
.pt
)
97 patternprefix
= "\n".join(("<<",
98 "/PatternType %d" % self
.patterntype
,
99 "/PaintType %d" % self
.painttype
,
100 "/TilingType %d" % self
.tilingtype
,
101 "/BBox[%d %d %d %d]" % patternbbox
.lowrestuple_pt(),
104 "/PaintProc {\nbegin\n"))
105 stringfile
= cStringIO
.StringIO()
106 # XXX here, we have a problem since the writer is not definined at that point
107 # for the moment, we just path None since we do not use it anyway
108 canvas
._canvas
.outputPS(self
, stringfile
, None, pswriter
.context())
109 patternproc
= stringfile
.getvalue()
111 patterntrafostring
= self
.patterntrafo
is None and "matrix" or str(self
.patterntrafo
)
112 patternsuffix
= "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring
114 registry
.add(pswriter
.PSdefinition(self
.id, "".join((patternprefix
, patternproc
, patternsuffix
))))
116 def registerPDF(self
, registry
):
117 realpatternbbox
= canvas
._canvas
.bbox(self
)
118 if self
.xstep
is None:
119 xstep
= unit
.topt(realpatternbbox
.width())
121 xstep
= unit
.topt(self
.xstep
)
122 if self
.ystep
is None:
123 ystep
= unit
.topt(realpatternbbox
.height())
125 ystep
= unit
.topt(self
.ystep
)
127 raise ValueError("xstep in pattern cannot be zero")
129 raise ValueError("ystep in pattern cannot be zero")
130 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(5*unit
.pt
)
131 patterntrafo
= self
.patterntrafo
or trafo
.trafo()
133 registry
.add(PDFpattern(self
.id, self
.patterntype
, self
.painttype
, self
.tilingtype
,
134 patternbbox
, xstep
, ystep
, patterntrafo
,
135 lambda file, writer
, context
: canvas
._canvas
.outputPDF(self
, file, writer
, context()),
136 lambda registry
: canvas
._canvas
.registerPDF(self
, registry
),
139 pattern
.clear
= attr
.clearclass(pattern
)
142 _base
= 0.1 * unit
.v_cm
144 class hatched(pattern
):
145 def __init__(self
, dist
, angle
, strokestyles
=[]):
146 pattern
.__init
__(self
, painttype
=1, tilingtype
=1, xstep
=dist
, ystep
=1000*unit
.t_pt
, bbox
=None, trafo
=trafo
.rotate(angle
))
147 self
.strokestyles
= attr
.mergeattrs([style
.linewidth
.THIN
] + strokestyles
)
148 attr
.checkattrs(self
.strokestyles
, [style
.strokestyle
])
151 self
.stroke(path
.line_pt(0, -500, 0, 500), self
.strokestyles
)
153 def __call__(self
, dist
=None, angle
=None, strokestyles
=None):
158 if strokestyles
is None:
159 strokestyles
= self
.strokestyles
160 return hatched(dist
, angle
, strokestyles
)
162 hatched0
= hatched(_base
, 0)
163 hatched0
.SMALL
= hatched0(_base
/math
.sqrt(64))
164 hatched0
.SMALL
= hatched0(_base
/math
.sqrt(64))
165 hatched0
.SMALl
= hatched0(_base
/math
.sqrt(32))
166 hatched0
.SMAll
= hatched0(_base
/math
.sqrt(16))
167 hatched0
.SMall
= hatched0(_base
/math
.sqrt(8))
168 hatched0
.Small
= hatched0(_base
/math
.sqrt(4))
169 hatched0
.small
= hatched0(_base
/math
.sqrt(2))
170 hatched0
.normal
= hatched0(_base
)
171 hatched0
.large
= hatched0(_base
*math
.sqrt(2))
172 hatched0
.Large
= hatched0(_base
*math
.sqrt(4))
173 hatched0
.LArge
= hatched0(_base
*math
.sqrt(8))
174 hatched0
.LARge
= hatched0(_base
*math
.sqrt(16))
175 hatched0
.LARGe
= hatched0(_base
*math
.sqrt(32))
176 hatched0
.LARGE
= hatched0(_base
*math
.sqrt(64))
178 hatched45
= hatched(_base
, 45)
179 hatched45
.SMALL
= hatched45(_base
/math
.sqrt(64))
180 hatched45
.SMALl
= hatched45(_base
/math
.sqrt(32))
181 hatched45
.SMAll
= hatched45(_base
/math
.sqrt(16))
182 hatched45
.SMall
= hatched45(_base
/math
.sqrt(8))
183 hatched45
.Small
= hatched45(_base
/math
.sqrt(4))
184 hatched45
.small
= hatched45(_base
/math
.sqrt(2))
185 hatched45
.normal
= hatched45(_base
)
186 hatched45
.large
= hatched45(_base
*math
.sqrt(2))
187 hatched45
.Large
= hatched45(_base
*math
.sqrt(4))
188 hatched45
.LArge
= hatched45(_base
*math
.sqrt(8))
189 hatched45
.LARge
= hatched45(_base
*math
.sqrt(16))
190 hatched45
.LARGe
= hatched45(_base
*math
.sqrt(32))
191 hatched45
.LARGE
= hatched45(_base
*math
.sqrt(64))
193 hatched90
= hatched(_base
, 90)
194 hatched90
.SMALL
= hatched90(_base
/math
.sqrt(64))
195 hatched90
.SMALl
= hatched90(_base
/math
.sqrt(32))
196 hatched90
.SMAll
= hatched90(_base
/math
.sqrt(16))
197 hatched90
.SMall
= hatched90(_base
/math
.sqrt(8))
198 hatched90
.Small
= hatched90(_base
/math
.sqrt(4))
199 hatched90
.small
= hatched90(_base
/math
.sqrt(2))
200 hatched90
.normal
= hatched90(_base
)
201 hatched90
.large
= hatched90(_base
*math
.sqrt(2))
202 hatched90
.Large
= hatched90(_base
*math
.sqrt(4))
203 hatched90
.LArge
= hatched90(_base
*math
.sqrt(8))
204 hatched90
.LARge
= hatched90(_base
*math
.sqrt(16))
205 hatched90
.LARGe
= hatched90(_base
*math
.sqrt(32))
206 hatched90
.LARGE
= hatched90(_base
*math
.sqrt(64))
208 hatched135
= hatched(_base
, 135)
209 hatched135
.SMALL
= hatched135(_base
/math
.sqrt(64))
210 hatched135
.SMALl
= hatched135(_base
/math
.sqrt(32))
211 hatched135
.SMAll
= hatched135(_base
/math
.sqrt(16))
212 hatched135
.SMall
= hatched135(_base
/math
.sqrt(8))
213 hatched135
.Small
= hatched135(_base
/math
.sqrt(4))
214 hatched135
.small
= hatched135(_base
/math
.sqrt(2))
215 hatched135
.normal
= hatched135(_base
)
216 hatched135
.large
= hatched135(_base
*math
.sqrt(2))
217 hatched135
.Large
= hatched135(_base
*math
.sqrt(4))
218 hatched135
.LArge
= hatched135(_base
*math
.sqrt(8))
219 hatched135
.LARge
= hatched135(_base
*math
.sqrt(16))
220 hatched135
.LARGe
= hatched135(_base
*math
.sqrt(32))
221 hatched135
.LARGE
= hatched135(_base
*math
.sqrt(64))
224 class crosshatched(pattern
):
225 def __init__(self
, dist
, angle
, strokestyles
=[]):
226 pattern
.__init
__(self
, painttype
=1, tilingtype
=1, xstep
=dist
, ystep
=dist
, bbox
=None, trafo
=trafo
.rotate(angle
))
227 self
.strokestyles
= attr
.mergeattrs([style
.linewidth
.THIN
] + strokestyles
)
228 attr
.checkattrs(self
.strokestyles
, [style
.strokestyle
])
231 self
.stroke(path
.line_pt(0, 0, 0, unit
.topt(dist
)), self
.strokestyles
)
232 self
.stroke(path
.line_pt(0, 0, unit
.topt(dist
), 0), self
.strokestyles
)
234 def __call__(self
, dist
=None, angle
=None, strokestyles
=None):
239 if strokestyles
is None:
240 strokestyles
= self
.strokestyles
241 return crosshatched(dist
, angle
, strokestyles
)
243 crosshatched0
= crosshatched(_base
, 0)
244 crosshatched0
.SMALL
= crosshatched0(_base
/math
.sqrt(64))
245 crosshatched0
.SMALl
= crosshatched0(_base
/math
.sqrt(32))
246 crosshatched0
.SMAll
= crosshatched0(_base
/math
.sqrt(16))
247 crosshatched0
.SMall
= crosshatched0(_base
/math
.sqrt(8))
248 crosshatched0
.Small
= crosshatched0(_base
/math
.sqrt(4))
249 crosshatched0
.small
= crosshatched0(_base
/math
.sqrt(2))
250 crosshatched0
.normal
= crosshatched0
251 crosshatched0
.large
= crosshatched0(_base
*math
.sqrt(2))
252 crosshatched0
.Large
= crosshatched0(_base
*math
.sqrt(4))
253 crosshatched0
.LArge
= crosshatched0(_base
*math
.sqrt(8))
254 crosshatched0
.LARge
= crosshatched0(_base
*math
.sqrt(16))
255 crosshatched0
.LARGe
= crosshatched0(_base
*math
.sqrt(32))
256 crosshatched0
.LARGE
= crosshatched0(_base
*math
.sqrt(64))
258 crosshatched45
= crosshatched(_base
, 45)
259 crosshatched45
.SMALL
= crosshatched45(_base
/math
.sqrt(64))
260 crosshatched45
.SMALl
= crosshatched45(_base
/math
.sqrt(32))
261 crosshatched45
.SMAll
= crosshatched45(_base
/math
.sqrt(16))
262 crosshatched45
.SMall
= crosshatched45(_base
/math
.sqrt(8))
263 crosshatched45
.Small
= crosshatched45(_base
/math
.sqrt(4))
264 crosshatched45
.small
= crosshatched45(_base
/math
.sqrt(2))
265 crosshatched45
.normal
= crosshatched45
266 crosshatched45
.large
= crosshatched45(_base
*math
.sqrt(2))
267 crosshatched45
.Large
= crosshatched45(_base
*math
.sqrt(4))
268 crosshatched45
.LArge
= crosshatched45(_base
*math
.sqrt(8))
269 crosshatched45
.LARge
= crosshatched45(_base
*math
.sqrt(16))
270 crosshatched45
.LARGe
= crosshatched45(_base
*math
.sqrt(32))
271 crosshatched45
.LARGE
= crosshatched45(_base
*math
.sqrt(64))
274 class PDFpattern(pdfwriter
.PDFobject
):
276 def __init__(self
, name
, patterntype
, painttype
, tilingtype
, bbox
, xstep
, ystep
, trafo
,
277 canvasoutputPDF
, canvasregisterPDF
, registry
):
278 pdfwriter
.PDFobject
.__init
__(self
, "pattern", name
)
280 self
.patterntype
= patterntype
281 self
.painttype
= painttype
282 self
.tilingtype
= tilingtype
287 self
.canvasoutputPDF
= canvasoutputPDF
289 self
.contentlength
= pdfwriter
.PDFcontentlength((self
.type, self
.id))
290 registry
.add(self
.contentlength
)
292 # we need to keep track of the resources used by the pattern, hence
293 # we create our own registry, which we merge immediately in the main registry
294 self
.patternregistry
= pdfwriter
.PDFregistry()
295 # XXX passing canvasregisterPDF is a Q&D way to get access to the registerPDF method
296 # of the _canvas superclass of the pattern
297 canvasregisterPDF(self
.patternregistry
)
298 registry
.mergeregistry(self
.patternregistry
)
300 def outputPDF(self
, file, writer
, registry
):
303 "/PatternType %d\n" % self
.patterntype
)
304 file.write("/PaintType %d\n" % self
.painttype
)
305 file.write("/TilingType %d\n" % self
.tilingtype
)
306 file.write("/BBox [%d %d %d %d]\n" % self
.bbox
.lowrestuple_pt())
307 file.write("/XStep %f\n" % self
.xstep
)
308 file.write("/YStep %f\n" % self
.ystep
)
309 file.write("/Matrix %s\n" % str(self
.trafo
))
310 file.write("/Resources <<\n")
311 if self
.patternregistry
.types
.has_key("font"):
312 file.write("/Font << %s >>\n" % " ".join(["/%s %i 0 R" % (font
.name
, registry
.getrefno(font
))
313 for font
in self
.patternregistry
.types
["font"].values()]))
314 if self
.patternregistry
.types
.has_key("pattern"):
315 file.write("/Pattern << %s >>\n" % " ".join(["/%s %i 0 R" % (pattern
.name
, registry
.getrefno(pattern
))
316 for pattern
in self
.patternregistry
.types
["pattern"].values()]))
318 file.write("/Length %i 0 R\n" % registry
.getrefno(self
.contentlength
))
320 file.write("/Filter /FlateDecode\n")
323 file.write("stream\n")
324 beginstreampos
= file.tell()
327 stream
= pdfwriter
.compressedstream(file, writer
.compresslevel
)
331 acontext
= pdfwriter
.context()
332 self
.canvasoutputPDF(stream
, writer
, acontext
)
336 self
.contentlength
.contentlength
= file.tell() - beginstreampos
337 file.write("endstream\n")