1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002-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
, math
, warnings
24 from . import attr
, canvas
, path
, pdfwriter
, pswriter
, style
, unit
, trafo
25 from . import writer
as writermodule
26 from . import bbox
as bboxmodule
30 # TODO: pattern should not derive from canvas but wrap a canvas
32 class pattern(canvas
.canvas
, attr
.exclusiveattr
, style
.fillstyle
):
34 def __init__(self
, painttype
=1, tilingtype
=1, xstep
=None, ystep
=None,
35 bbox
=None, trafo
=None, bboxenlarge
=5*unit
.t_pt
, **kwargs
):
36 canvas
.canvas
.__init
__(self
, **kwargs
)
37 attr
.exclusiveattr
.__init
__(self
, pattern
)
38 self
.id = "pattern%d" % id(self
)
40 if painttype
not in (1, 2):
41 raise ValueError("painttype must be 1 or 2")
42 self
.painttype
= painttype
43 if tilingtype
not in (1, 2, 3):
44 raise ValueError("tilingtype must be 1, 2, or 3")
45 self
.tilingtype
= tilingtype
48 self
.patternbbox
= bbox
49 self
.patterntrafo
= trafo
50 self
.bboxenlarge
= bboxenlarge
52 def __call__(self
, painttype
=_marker
, tilingtype
=_marker
, xstep
=_marker
, ystep
=_marker
,
53 bbox
=_marker
, trafo
=_marker
, bboxenlarge
=_marker
):
54 if painttype
is _marker
:
55 painttype
= self
.painttype
56 if tilingtype
is _marker
:
57 tilingtype
= self
.tilingtype
66 if bboxenlarge
is _marker
:
67 bboxenlarge
= self
.bboxenlarge
68 return pattern(painttype
, tilingtype
, xstep
, ystep
, bbox
, trafo
, bboxenlarge
)
70 def processPS(self
, file, writer
, context
, registry
):
71 # process pattern, letting it register its resources and calculate the bbox of the pattern
72 patternfile
= writermodule
.writer(io
.BytesIO())
73 realpatternbbox
= bboxmodule
.empty()
74 canvas
.canvas
.processPS(self
, patternfile
, writer
, pswriter
.context(), registry
, realpatternbbox
)
75 patternproc
= patternfile
.file.getvalue()
77 if self
.xstep
is None:
78 xstep
= unit
.topt(realpatternbbox
.width())
80 xstep
= unit
.topt(self
.xstep
)
81 if self
.ystep
is None:
82 ystep
= unit
.topt(realpatternbbox
.height())
84 ystep
= unit
.topt(self
.ystep
)
86 raise ValueError("xstep in pattern cannot be zero")
88 raise ValueError("ystep in pattern cannot be zero")
89 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(self
.bboxenlarge
)
91 patternprefix
= "\n".join(("<<",
92 "/PatternType %d" % self
.patterntype
,
93 "/PaintType %d" % self
.painttype
,
94 "/TilingType %d" % self
.tilingtype
,
95 "/BBox [%g %g %g %g]" % patternbbox
.highrestuple_pt(),
98 "/PaintProc {\nbegin\n"))
99 patterntrafostring
= self
.patterntrafo
is None and "matrix" or str(self
.patterntrafo
)
100 patternsuffix
= "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring
102 registry
.add(pswriter
.PSdefinition(self
.id, patternprefix
.encode("ascii") + patternproc
+ patternsuffix
.encode("ascii")))
105 file.write("%s setpattern\n" % self
.id)
107 def processPDF(self
, file, writer
, context
, registry
):
108 # we need to keep track of the resources used by the pattern, hence
109 # we create our own registry, which we merge immediately in the main registry
110 patternregistry
= pdfwriter
.PDFregistry()
112 patternfile
= writermodule
.writer(io
.BytesIO())
113 realpatternbbox
= bboxmodule
.empty()
114 canvas
.canvas
.processPDF(self
, patternfile
, writer
, pdfwriter
.context(), patternregistry
, realpatternbbox
)
115 patternproc
= patternfile
.file.getvalue()
117 registry
.mergeregistry(patternregistry
)
119 if self
.xstep
is None:
120 xstep
= unit
.topt(realpatternbbox
.width())
122 xstep
= unit
.topt(self
.xstep
)
123 if self
.ystep
is None:
124 ystep
= unit
.topt(realpatternbbox
.height())
126 ystep
= unit
.topt(self
.ystep
)
128 raise ValueError("xstep in pattern cannot be zero")
130 raise ValueError("ystep in pattern cannot be zero")
131 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(5*unit
.pt
)
132 patterntrafo
= self
.patterntrafo
or trafo
.trafo()
134 registry
.add(PDFpattern(self
.id, self
.patterntype
, self
.painttype
, self
.tilingtype
,
135 patternbbox
, xstep
, ystep
, patterntrafo
, patternproc
, writer
, registry
, patternregistry
))
138 if context
.colorspace
!= "Pattern":
139 # we only set the fill color space (see next comment)
140 file.write("/Pattern cs\n")
141 context
.colorspace
= "Pattern"
142 if context
.strokeattr
:
143 # using patterns as stroke colors doesn't seem to work, so
144 # we just don't do this...
145 warnings
.warn("ignoring stroke color for patterns in PDF")
147 file.write("/%s scn\n"% self
.id)
150 pattern
.clear
= attr
.clearclass(pattern
)
153 _base
= 0.1 * unit
.v_cm
155 class hatched(pattern
):
156 def __init__(self
, dist
, angle
, strokestyles
=[]):
157 pattern
.__init
__(self
, painttype
=1, tilingtype
=1, xstep
=dist
, ystep
=100*unit
.t_pt
, bbox
=None, trafo
=trafo
.rotate(angle
))
158 self
.strokestyles
= attr
.mergeattrs([style
.linewidth
.THIN
] + strokestyles
)
159 attr
.checkattrs(self
.strokestyles
, [style
.strokestyle
])
162 self
.stroke(path
.line_pt(0, -50, 0, 50), self
.strokestyles
)
164 def __call__(self
, dist
=None, angle
=None, strokestyles
=None):
169 if strokestyles
is None:
170 strokestyles
= self
.strokestyles
171 return hatched(dist
, angle
, strokestyles
)
173 hatched0
= hatched(_base
, 0)
174 hatched0
.SMALL
= hatched0(_base
/math
.sqrt(64))
175 hatched0
.SMALL
= hatched0(_base
/math
.sqrt(64))
176 hatched0
.SMALl
= hatched0(_base
/math
.sqrt(32))
177 hatched0
.SMAll
= hatched0(_base
/math
.sqrt(16))
178 hatched0
.SMall
= hatched0(_base
/math
.sqrt(8))
179 hatched0
.Small
= hatched0(_base
/math
.sqrt(4))
180 hatched0
.small
= hatched0(_base
/math
.sqrt(2))
181 hatched0
.normal
= hatched0(_base
)
182 hatched0
.large
= hatched0(_base
*math
.sqrt(2))
183 hatched0
.Large
= hatched0(_base
*math
.sqrt(4))
184 hatched0
.LArge
= hatched0(_base
*math
.sqrt(8))
185 hatched0
.LARge
= hatched0(_base
*math
.sqrt(16))
186 hatched0
.LARGe
= hatched0(_base
*math
.sqrt(32))
187 hatched0
.LARGE
= hatched0(_base
*math
.sqrt(64))
189 hatched45
= hatched(_base
, 45)
190 hatched45
.SMALL
= hatched45(_base
/math
.sqrt(64))
191 hatched45
.SMALl
= hatched45(_base
/math
.sqrt(32))
192 hatched45
.SMAll
= hatched45(_base
/math
.sqrt(16))
193 hatched45
.SMall
= hatched45(_base
/math
.sqrt(8))
194 hatched45
.Small
= hatched45(_base
/math
.sqrt(4))
195 hatched45
.small
= hatched45(_base
/math
.sqrt(2))
196 hatched45
.normal
= hatched45(_base
)
197 hatched45
.large
= hatched45(_base
*math
.sqrt(2))
198 hatched45
.Large
= hatched45(_base
*math
.sqrt(4))
199 hatched45
.LArge
= hatched45(_base
*math
.sqrt(8))
200 hatched45
.LARge
= hatched45(_base
*math
.sqrt(16))
201 hatched45
.LARGe
= hatched45(_base
*math
.sqrt(32))
202 hatched45
.LARGE
= hatched45(_base
*math
.sqrt(64))
204 hatched90
= hatched(_base
, 90)
205 hatched90
.SMALL
= hatched90(_base
/math
.sqrt(64))
206 hatched90
.SMALl
= hatched90(_base
/math
.sqrt(32))
207 hatched90
.SMAll
= hatched90(_base
/math
.sqrt(16))
208 hatched90
.SMall
= hatched90(_base
/math
.sqrt(8))
209 hatched90
.Small
= hatched90(_base
/math
.sqrt(4))
210 hatched90
.small
= hatched90(_base
/math
.sqrt(2))
211 hatched90
.normal
= hatched90(_base
)
212 hatched90
.large
= hatched90(_base
*math
.sqrt(2))
213 hatched90
.Large
= hatched90(_base
*math
.sqrt(4))
214 hatched90
.LArge
= hatched90(_base
*math
.sqrt(8))
215 hatched90
.LARge
= hatched90(_base
*math
.sqrt(16))
216 hatched90
.LARGe
= hatched90(_base
*math
.sqrt(32))
217 hatched90
.LARGE
= hatched90(_base
*math
.sqrt(64))
219 hatched135
= hatched(_base
, 135)
220 hatched135
.SMALL
= hatched135(_base
/math
.sqrt(64))
221 hatched135
.SMALl
= hatched135(_base
/math
.sqrt(32))
222 hatched135
.SMAll
= hatched135(_base
/math
.sqrt(16))
223 hatched135
.SMall
= hatched135(_base
/math
.sqrt(8))
224 hatched135
.Small
= hatched135(_base
/math
.sqrt(4))
225 hatched135
.small
= hatched135(_base
/math
.sqrt(2))
226 hatched135
.normal
= hatched135(_base
)
227 hatched135
.large
= hatched135(_base
*math
.sqrt(2))
228 hatched135
.Large
= hatched135(_base
*math
.sqrt(4))
229 hatched135
.LArge
= hatched135(_base
*math
.sqrt(8))
230 hatched135
.LARge
= hatched135(_base
*math
.sqrt(16))
231 hatched135
.LARGe
= hatched135(_base
*math
.sqrt(32))
232 hatched135
.LARGE
= hatched135(_base
*math
.sqrt(64))
235 class crosshatched(pattern
):
236 def __init__(self
, dist
, angle
, strokestyles
=[]):
237 pattern
.__init
__(self
, painttype
=1, tilingtype
=1, xstep
=dist
, ystep
=dist
, bbox
=None, trafo
=trafo
.rotate(angle
))
238 self
.strokestyles
= attr
.mergeattrs([style
.linewidth
.THIN
] + strokestyles
)
239 attr
.checkattrs(self
.strokestyles
, [style
.strokestyle
])
242 self
.stroke(path
.line_pt(0, 0, 0, unit
.topt(dist
)), self
.strokestyles
)
243 self
.stroke(path
.line_pt(0, 0, unit
.topt(dist
), 0), self
.strokestyles
)
245 def __call__(self
, dist
=None, angle
=None, strokestyles
=None):
250 if strokestyles
is None:
251 strokestyles
= self
.strokestyles
252 return crosshatched(dist
, angle
, strokestyles
)
254 crosshatched0
= crosshatched(_base
, 0)
255 crosshatched0
.SMALL
= crosshatched0(_base
/math
.sqrt(64))
256 crosshatched0
.SMALl
= crosshatched0(_base
/math
.sqrt(32))
257 crosshatched0
.SMAll
= crosshatched0(_base
/math
.sqrt(16))
258 crosshatched0
.SMall
= crosshatched0(_base
/math
.sqrt(8))
259 crosshatched0
.Small
= crosshatched0(_base
/math
.sqrt(4))
260 crosshatched0
.small
= crosshatched0(_base
/math
.sqrt(2))
261 crosshatched0
.normal
= crosshatched0
262 crosshatched0
.large
= crosshatched0(_base
*math
.sqrt(2))
263 crosshatched0
.Large
= crosshatched0(_base
*math
.sqrt(4))
264 crosshatched0
.LArge
= crosshatched0(_base
*math
.sqrt(8))
265 crosshatched0
.LARge
= crosshatched0(_base
*math
.sqrt(16))
266 crosshatched0
.LARGe
= crosshatched0(_base
*math
.sqrt(32))
267 crosshatched0
.LARGE
= crosshatched0(_base
*math
.sqrt(64))
269 crosshatched45
= crosshatched(_base
, 45)
270 crosshatched45
.SMALL
= crosshatched45(_base
/math
.sqrt(64))
271 crosshatched45
.SMALl
= crosshatched45(_base
/math
.sqrt(32))
272 crosshatched45
.SMAll
= crosshatched45(_base
/math
.sqrt(16))
273 crosshatched45
.SMall
= crosshatched45(_base
/math
.sqrt(8))
274 crosshatched45
.Small
= crosshatched45(_base
/math
.sqrt(4))
275 crosshatched45
.small
= crosshatched45(_base
/math
.sqrt(2))
276 crosshatched45
.normal
= crosshatched45
277 crosshatched45
.large
= crosshatched45(_base
*math
.sqrt(2))
278 crosshatched45
.Large
= crosshatched45(_base
*math
.sqrt(4))
279 crosshatched45
.LArge
= crosshatched45(_base
*math
.sqrt(8))
280 crosshatched45
.LARge
= crosshatched45(_base
*math
.sqrt(16))
281 crosshatched45
.LARGe
= crosshatched45(_base
*math
.sqrt(32))
282 crosshatched45
.LARGE
= crosshatched45(_base
*math
.sqrt(64))
285 class PDFpattern(pdfwriter
.PDFobject
):
287 def __init__(self
, name
, patterntype
, painttype
, tilingtype
, bbox
, xstep
, ystep
, trafo
,
288 patternproc
, writer
, registry
, patternregistry
):
289 self
.patternregistry
= patternregistry
290 pdfwriter
.PDFobject
.__init
__(self
, "pattern", name
)
291 registry
.addresource("Pattern", name
, self
)
294 self
.patterntype
= patterntype
295 self
.painttype
= painttype
296 self
.tilingtype
= tilingtype
301 self
.patternproc
= patternproc
303 def write(self
, file, writer
, registry
):
306 "/PatternType %d\n" % self
.patterntype
)
307 file.write("/PaintType %d\n" % self
.painttype
)
308 file.write("/TilingType %d\n" % self
.tilingtype
)
309 file.write("/BBox [%d %d %d %d]\n" % self
.bbox
.lowrestuple_pt())
310 file.write("/XStep %f\n" % self
.xstep
)
311 file.write("/YStep %f\n" % self
.ystep
)
312 file.write("/Matrix %s\n" % str(self
.trafo
))
313 file.write("/Resources ")
314 self
.patternregistry
.writeresources(file)
317 content
= zlib
.compress(self
.patternproc
)
319 content
= self
.patternproc
321 file.write("/Length %i\n" % len(content
))
323 file.write("/Filter /FlateDecode\n")
326 file.write_bytes(content
)
327 file.write("endstream\n")