Applied upstream as r3028 r3025 r3024
[PyX/mjg.git] / pyx / pattern.py
blobd08e6125347f1611fce43f2fac92e6a3934ddf5d
1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2006 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002-2006 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 from __future__ import nested_scopes
25 import cStringIO, math, warnings
26 import attr, canvas, path, pdfwriter, pswriter, style, unit, trafo
27 import bbox as bboxmodule
29 class _marker: pass
31 # TODO: pattern should not derive from canvas but wrap a canvas
33 class pattern(canvas._canvas, attr.exclusiveattr, style.fillstyle):
35 def __init__(self, painttype=1, tilingtype=1, xstep=None, ystep=None,
36 bbox=None, trafo=None, bboxenlarge=5*unit.t_pt, **kwargs):
37 canvas._canvas.__init__(self, **kwargs)
38 attr.exclusiveattr.__init__(self, pattern)
39 self.id = "pattern%d" % id(self)
40 self.patterntype = 1
41 if painttype not in (1, 2):
42 raise ValueError("painttype must be 1 or 2")
43 self.painttype = painttype
44 if tilingtype not in (1, 2, 3):
45 raise ValueError("tilingtype must be 1, 2, or 3")
46 self.tilingtype = tilingtype
47 self.xstep = xstep
48 self.ystep = ystep
49 self.patternbbox = bbox
50 self.patterntrafo = trafo
51 self.bboxenlarge = bboxenlarge
53 def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker,
54 bbox=_marker, trafo=_marker, bboxenlarge=_marker):
55 if painttype is _marker:
56 painttype = self.painttype
57 if tilingtype is _marker:
58 tilingtype = self.tilingtype
59 if xstep is _marker:
60 xstep = self.xstep
61 if ystep is _marker:
62 ystep = self.ystep
63 if bbox is _marker:
64 bbox = self.bbox
65 if trafo is _marker:
66 trafo = self.trafo
67 if bboxenlarge is _marker:
68 bboxenlarge = self.bboxenlarge
69 return pattern(painttype, tilingtype, xstep, ystep, bbox, trafo, bboxenlarge)
71 def bbox(self):
72 return bboxmodule.empty()
74 def processPS(self, file, writer, context, registry, bbox):
75 # process pattern, letting it register its resources and calculate the bbox of the pattern
76 patternfile = cStringIO.StringIO()
77 realpatternbbox = bboxmodule.empty()
78 canvas._canvas.processPS(self, patternfile, writer, pswriter.context(), registry, realpatternbbox)
79 patternproc = patternfile.getvalue()
80 patternfile.close()
82 if self.xstep is None:
83 xstep = unit.topt(realpatternbbox.width())
84 else:
85 xstep = unit.topt(self.xstep)
86 if self.ystep is None:
87 ystep = unit.topt(realpatternbbox.height())
88 else:
89 ystep = unit.topt(self.ystep)
90 if not xstep:
91 raise ValueError("xstep in pattern cannot be zero")
92 if not ystep:
93 raise ValueError("ystep in pattern cannot be zero")
94 patternbbox = self.patternbbox or realpatternbbox.enlarged(self.bboxenlarge)
96 patternprefix = "\n".join(("<<",
97 "/PatternType %d" % self.patterntype,
98 "/PaintType %d" % self.painttype,
99 "/TilingType %d" % self.tilingtype,
100 "/BBox [%g %g %g %g]" % patternbbox.highrestuple_pt(),
101 "/XStep %g" % xstep,
102 "/YStep %g" % ystep,
103 "/PaintProc {\nbegin\n"))
104 patterntrafostring = self.patterntrafo is None and "matrix" or str(self.patterntrafo)
105 patternsuffix = "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring
107 registry.add(pswriter.PSdefinition(self.id, "".join((patternprefix, patternproc, patternsuffix))))
109 # activate pattern
110 file.write("%s setpattern\n" % self.id)
112 def processPDF(self, file, writer, context, registry, bbox):
113 # we need to keep track of the resources used by the pattern, hence
114 # we create our own registry, which we merge immediately in the main registry
115 patternregistry = pdfwriter.PDFregistry()
117 patternfile = cStringIO.StringIO()
118 realpatternbbox = bboxmodule.empty()
119 canvas._canvas.processPDF(self, patternfile, writer, pdfwriter.context(), patternregistry, realpatternbbox)
120 patternproc = patternfile.getvalue()
121 patternfile.close()
123 registry.mergeregistry(patternregistry)
125 if self.xstep is None:
126 xstep = unit.topt(realpatternbbox.width())
127 else:
128 xstep = unit.topt(self.xstep)
129 if self.ystep is None:
130 ystep = unit.topt(realpatternbbox.height())
131 else:
132 ystep = unit.topt(self.ystep)
133 if not xstep:
134 raise ValueError("xstep in pattern cannot be zero")
135 if not ystep:
136 raise ValueError("ystep in pattern cannot be zero")
137 patternbbox = self.patternbbox or realpatternbbox.enlarged(5*unit.pt)
138 patterntrafo = self.patterntrafo or trafo.trafo()
140 registry.add(PDFpattern(self.id, self.patterntype, self.painttype, self.tilingtype,
141 patternbbox, xstep, ystep, patterntrafo, patternproc, writer, registry, patternregistry))
143 # activate pattern
144 if context.colorspace != "Pattern":
145 # we only set the fill color space (see next comment)
146 file.write("/Pattern cs\n")
147 context.colorspace = "Pattern"
148 if context.strokeattr:
149 # using patterns as stroke colors doesn't seem to work, so
150 # we just don't do this...
151 warnings.warn("ignoring stroke color for patterns in PDF")
152 if context.fillattr:
153 file.write("/%s scn\n"% self.id)
156 pattern.clear = attr.clearclass(pattern)
159 _base = 0.1 * unit.v_cm
161 class hatched(pattern):
162 def __init__(self, dist, angle, strokestyles=[]):
163 pattern.__init__(self, painttype=1, tilingtype=1, xstep=dist, ystep=100*unit.t_pt, bbox=None, trafo=trafo.rotate(angle))
164 self.strokestyles = attr.mergeattrs([style.linewidth.THIN] + strokestyles)
165 attr.checkattrs(self.strokestyles, [style.strokestyle])
166 self.dist = dist
167 self.angle = angle
168 self.stroke(path.line_pt(0, -50, 0, 50), self.strokestyles)
170 def __call__(self, dist=None, angle=None, strokestyles=None):
171 if dist is None:
172 dist = self.dist
173 if angle is None:
174 angle = self.angle
175 if strokestyles is None:
176 strokestyles = self.strokestyles
177 return hatched(dist, angle, strokestyles)
179 hatched0 = hatched(_base, 0)
180 hatched0.SMALL = hatched0(_base/math.sqrt(64))
181 hatched0.SMALL = hatched0(_base/math.sqrt(64))
182 hatched0.SMALl = hatched0(_base/math.sqrt(32))
183 hatched0.SMAll = hatched0(_base/math.sqrt(16))
184 hatched0.SMall = hatched0(_base/math.sqrt(8))
185 hatched0.Small = hatched0(_base/math.sqrt(4))
186 hatched0.small = hatched0(_base/math.sqrt(2))
187 hatched0.normal = hatched0(_base)
188 hatched0.large = hatched0(_base*math.sqrt(2))
189 hatched0.Large = hatched0(_base*math.sqrt(4))
190 hatched0.LArge = hatched0(_base*math.sqrt(8))
191 hatched0.LARge = hatched0(_base*math.sqrt(16))
192 hatched0.LARGe = hatched0(_base*math.sqrt(32))
193 hatched0.LARGE = hatched0(_base*math.sqrt(64))
195 hatched45 = hatched(_base, 45)
196 hatched45.SMALL = hatched45(_base/math.sqrt(64))
197 hatched45.SMALl = hatched45(_base/math.sqrt(32))
198 hatched45.SMAll = hatched45(_base/math.sqrt(16))
199 hatched45.SMall = hatched45(_base/math.sqrt(8))
200 hatched45.Small = hatched45(_base/math.sqrt(4))
201 hatched45.small = hatched45(_base/math.sqrt(2))
202 hatched45.normal = hatched45(_base)
203 hatched45.large = hatched45(_base*math.sqrt(2))
204 hatched45.Large = hatched45(_base*math.sqrt(4))
205 hatched45.LArge = hatched45(_base*math.sqrt(8))
206 hatched45.LARge = hatched45(_base*math.sqrt(16))
207 hatched45.LARGe = hatched45(_base*math.sqrt(32))
208 hatched45.LARGE = hatched45(_base*math.sqrt(64))
210 hatched90 = hatched(_base, 90)
211 hatched90.SMALL = hatched90(_base/math.sqrt(64))
212 hatched90.SMALl = hatched90(_base/math.sqrt(32))
213 hatched90.SMAll = hatched90(_base/math.sqrt(16))
214 hatched90.SMall = hatched90(_base/math.sqrt(8))
215 hatched90.Small = hatched90(_base/math.sqrt(4))
216 hatched90.small = hatched90(_base/math.sqrt(2))
217 hatched90.normal = hatched90(_base)
218 hatched90.large = hatched90(_base*math.sqrt(2))
219 hatched90.Large = hatched90(_base*math.sqrt(4))
220 hatched90.LArge = hatched90(_base*math.sqrt(8))
221 hatched90.LARge = hatched90(_base*math.sqrt(16))
222 hatched90.LARGe = hatched90(_base*math.sqrt(32))
223 hatched90.LARGE = hatched90(_base*math.sqrt(64))
225 hatched135 = hatched(_base, 135)
226 hatched135.SMALL = hatched135(_base/math.sqrt(64))
227 hatched135.SMALl = hatched135(_base/math.sqrt(32))
228 hatched135.SMAll = hatched135(_base/math.sqrt(16))
229 hatched135.SMall = hatched135(_base/math.sqrt(8))
230 hatched135.Small = hatched135(_base/math.sqrt(4))
231 hatched135.small = hatched135(_base/math.sqrt(2))
232 hatched135.normal = hatched135(_base)
233 hatched135.large = hatched135(_base*math.sqrt(2))
234 hatched135.Large = hatched135(_base*math.sqrt(4))
235 hatched135.LArge = hatched135(_base*math.sqrt(8))
236 hatched135.LARge = hatched135(_base*math.sqrt(16))
237 hatched135.LARGe = hatched135(_base*math.sqrt(32))
238 hatched135.LARGE = hatched135(_base*math.sqrt(64))
241 class crosshatched(pattern):
242 def __init__(self, dist, angle, strokestyles=[]):
243 pattern.__init__(self, painttype=1, tilingtype=1, xstep=dist, ystep=dist, bbox=None, trafo=trafo.rotate(angle))
244 self.strokestyles = attr.mergeattrs([style.linewidth.THIN] + strokestyles)
245 attr.checkattrs(self.strokestyles, [style.strokestyle])
246 self.dist = dist
247 self.angle = angle
248 self.stroke(path.line_pt(0, 0, 0, unit.topt(dist)), self.strokestyles)
249 self.stroke(path.line_pt(0, 0, unit.topt(dist), 0), self.strokestyles)
251 def __call__(self, dist=None, angle=None, strokestyles=None):
252 if dist is None:
253 dist = self.dist
254 if angle is None:
255 angle = self.angle
256 if strokestyles is None:
257 strokestyles = self.strokestyles
258 return crosshatched(dist, angle, strokestyles)
260 crosshatched0 = crosshatched(_base, 0)
261 crosshatched0.SMALL = crosshatched0(_base/math.sqrt(64))
262 crosshatched0.SMALl = crosshatched0(_base/math.sqrt(32))
263 crosshatched0.SMAll = crosshatched0(_base/math.sqrt(16))
264 crosshatched0.SMall = crosshatched0(_base/math.sqrt(8))
265 crosshatched0.Small = crosshatched0(_base/math.sqrt(4))
266 crosshatched0.small = crosshatched0(_base/math.sqrt(2))
267 crosshatched0.normal = crosshatched0
268 crosshatched0.large = crosshatched0(_base*math.sqrt(2))
269 crosshatched0.Large = crosshatched0(_base*math.sqrt(4))
270 crosshatched0.LArge = crosshatched0(_base*math.sqrt(8))
271 crosshatched0.LARge = crosshatched0(_base*math.sqrt(16))
272 crosshatched0.LARGe = crosshatched0(_base*math.sqrt(32))
273 crosshatched0.LARGE = crosshatched0(_base*math.sqrt(64))
275 crosshatched45 = crosshatched(_base, 45)
276 crosshatched45.SMALL = crosshatched45(_base/math.sqrt(64))
277 crosshatched45.SMALl = crosshatched45(_base/math.sqrt(32))
278 crosshatched45.SMAll = crosshatched45(_base/math.sqrt(16))
279 crosshatched45.SMall = crosshatched45(_base/math.sqrt(8))
280 crosshatched45.Small = crosshatched45(_base/math.sqrt(4))
281 crosshatched45.small = crosshatched45(_base/math.sqrt(2))
282 crosshatched45.normal = crosshatched45
283 crosshatched45.large = crosshatched45(_base*math.sqrt(2))
284 crosshatched45.Large = crosshatched45(_base*math.sqrt(4))
285 crosshatched45.LArge = crosshatched45(_base*math.sqrt(8))
286 crosshatched45.LARge = crosshatched45(_base*math.sqrt(16))
287 crosshatched45.LARGe = crosshatched45(_base*math.sqrt(32))
288 crosshatched45.LARGE = crosshatched45(_base*math.sqrt(64))
291 class PDFpattern(pdfwriter.PDFobject):
293 def __init__(self, name, patterntype, painttype, tilingtype, bbox, xstep, ystep, trafo,
294 patternproc, writer, registry, patternregistry):
295 self.patternregistry = patternregistry
296 pdfwriter.PDFobject.__init__(self, "pattern", name)
297 registry.addresource("Pattern", name, self)
299 self.name = name
300 self.patterntype = patterntype
301 self.painttype = painttype
302 self.tilingtype = tilingtype
303 self.bbox = bbox
304 self.xstep = xstep
305 self.ystep = ystep
306 self.trafo = trafo
307 self.patternproc = patternproc
309 def write(self, file, writer, registry):
310 file.write("<<\n"
311 "/Type /Pattern\n"
312 "/PatternType %d\n" % self.patterntype)
313 file.write("/PaintType %d\n" % self.painttype)
314 file.write("/TilingType %d\n" % self.tilingtype)
315 file.write("/BBox [%d %d %d %d]\n" % self.bbox.lowrestuple_pt())
316 file.write("/XStep %f\n" % self.xstep)
317 file.write("/YStep %f\n" % self.ystep)
318 file.write("/Matrix %s\n" % str(self.trafo))
319 file.write("/Resources ")
320 self.patternregistry.writeresources(file)
321 if writer.compress:
322 import zlib
323 content = zlib.compress(self.patternproc)
324 else:
325 content = self.patternproc
327 file.write("/Length %i\n" % len(content))
328 if writer.compress:
329 file.write("/Filter /FlateDecode\n")
330 file.write(">>\n"
331 "stream\n")
332 file.write(content)
333 file.write("endstream\n")