store dist, angle and strokestyle in (cross)hatched instances and fix predefined...
[PyX/mjg.git] / pyx / pattern.py
blob3cb109fc922e6da83182f97ab1bf6168c590b12a
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)
34 self.patterntype = 1
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
41 self.xstep = xstep
42 self.ystep = ystep
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:
53 xstep = self.xstep
54 if ystep is helper.nodefault:
55 ystep = self.ystep
56 if bbox is helper.nodefault:
57 bbox = self.bbox
58 if trafo is helper.nodefault:
59 trafo = self.trafo
60 return pattern(painttype, tilingtype, xstep, ystep, bbox, trafo)
62 def bbox(self):
63 return None
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...
76 pass
77 if context.fillattr:
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())
85 else:
86 xstep = unit.topt(self.xstep)
87 if self.ystep is None:
88 ystep = unit.topt(realpatternbbox.height())
89 else:
90 ystep = unit.topt(self.ystep)
91 if not xstep:
92 raise ValueError("xstep in pattern cannot be zero")
93 if not ystep:
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(),
102 "/XStep %g" % xstep,
103 "/YStep %g" % ystep,
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()
110 stringfile.close()
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())
120 else:
121 xstep = unit.topt(self.xstep)
122 if self.ystep is None:
123 ystep = unit.topt(realpatternbbox.height())
124 else:
125 ystep = unit.topt(self.ystep)
126 if not xstep:
127 raise ValueError("xstep in pattern cannot be zero")
128 if not ystep:
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),
137 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])
149 self.dist = dist
150 self.angle = angle
151 self.stroke(path.line_pt(0, -500, 0, 500), self.strokestyles)
153 def __call__(self, dist=None, angle=None, strokestyles=None):
154 if dist is None:
155 dist = self.dist
156 if angle is None:
157 angle = self.angle
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])
229 self.dist = dist
230 self.angle = angle
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):
235 if dist is None:
236 dist = self.dist
237 if angle is None:
238 angle = self.angle
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)
279 self.name = name
280 self.patterntype = patterntype
281 self.painttype = painttype
282 self.tilingtype = tilingtype
283 self.bbox = bbox
284 self.xstep = xstep
285 self.ystep = ystep
286 self.trafo = trafo
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):
301 file.write("<<\n"
302 "/Type /Pattern\n"
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()]))
317 file.write(">>\n")
318 file.write("/Length %i 0 R\n" % registry.getrefno(self.contentlength))
319 if writer.compress:
320 file.write("/Filter /FlateDecode\n")
321 file.write(">>\n")
323 file.write("stream\n")
324 beginstreampos = file.tell()
326 if writer.compress:
327 stream = pdfwriter.compressedstream(file, writer.compresslevel)
328 else:
329 stream = file
331 acontext = pdfwriter.context()
332 self.canvasoutputPDF(stream, writer, acontext)
333 if writer.compress:
334 stream.flush()
336 self.contentlength.contentlength = file.tell() - beginstreampos
337 file.write("endstream\n")