rename texmessage.loadfd to texmessage.loaddef and allow for .def and .fd files
[PyX/mjg.git] / pyx / pattern.py
blob0fa3001800077c9d30d5a1b4d5b13cc90eec0beb
1 #!/usr/bin/env python
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 from __future__ import nested_scopes
26 import cStringIO, math, warnings
27 import attr, canvas, path, pdfwriter, pswriter, style, unit, trafo
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, bbox=None, trafo=None, **kwargs):
36 canvas._canvas.__init__(self, **kwargs)
37 attr.exclusiveattr.__init__(self, pattern)
38 self.id = "pattern%d" % id(self)
39 self.patterntype = 1
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
46 self.xstep = xstep
47 self.ystep = ystep
48 self.patternbbox = bbox
49 self.patterntrafo = trafo
51 def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker,
52 bbox=_marker, trafo=_marker):
53 if painttype is _marker:
54 painttype = self.painttype
55 if tilingtype is _marker:
56 tilingtype = self.tilingtype
57 if xstep is _marker:
58 xstep = self.xstep
59 if ystep is _marker:
60 ystep = self.ystep
61 if bbox is _marker:
62 bbox = self.bbox
63 if trafo is _marker:
64 trafo = self.trafo
65 return pattern(painttype, tilingtype, xstep, ystep, bbox, trafo)
67 def bbox(self):
68 return None
70 def outputPS(self, file, writer, context):
71 file.write("%s setpattern\n" % self.id)
73 def outputPDF(self, file, writer, context):
74 if context.colorspace != "Pattern":
75 # we only set the fill color space (see next comment)
76 file.write("/Pattern cs\n")
77 context.colorspace = "Pattern"
78 if context.strokeattr:
79 # using patterns as stroke colors doesn't seem to work, so
80 # we just don't do this...
81 warnings.warn("ignoring stroke color for patterns in PDF")
82 if context.fillattr:
83 file.write("/%s scn\n"% self.id)
85 def registerPS(self, registry):
86 canvas._canvas.registerPS(self, registry)
87 realpatternbbox = canvas._canvas.bbox(self)
88 if self.xstep is None:
89 xstep = unit.topt(realpatternbbox.width())
90 else:
91 xstep = unit.topt(self.xstep)
92 if self.ystep is None:
93 ystep = unit.topt(realpatternbbox.height())
94 else:
95 ystep = unit.topt(self.ystep)
96 if not xstep:
97 raise ValueError("xstep in pattern cannot be zero")
98 if not ystep:
99 raise ValueError("ystep in pattern cannot be zero")
100 patternbbox = self.patternbbox or realpatternbbox.enlarged(5*unit.pt)
102 patternprefix = "\n".join(("<<",
103 "/PatternType %d" % self.patterntype,
104 "/PaintType %d" % self.painttype,
105 "/TilingType %d" % self.tilingtype,
106 "/BBox[%d %d %d %d]" % patternbbox.lowrestuple_pt(),
107 "/XStep %g" % xstep,
108 "/YStep %g" % ystep,
109 "/PaintProc {\nbegin\n"))
110 stringfile = cStringIO.StringIO()
111 # XXX here, we have a problem since the writer is not definined at that point
112 # for the moment, we just path None since we do not use it anyway
113 canvas._canvas.outputPS(self, stringfile, None, pswriter.context())
114 patternproc = stringfile.getvalue()
115 stringfile.close()
116 patterntrafostring = self.patterntrafo is None and "matrix" or str(self.patterntrafo)
117 patternsuffix = "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring
119 registry.add(pswriter.PSdefinition(self.id, "".join((patternprefix, patternproc, patternsuffix))))
121 def registerPDF(self, registry):
122 realpatternbbox = canvas._canvas.bbox(self)
123 if self.xstep is None:
124 xstep = unit.topt(realpatternbbox.width())
125 else:
126 xstep = unit.topt(self.xstep)
127 if self.ystep is None:
128 ystep = unit.topt(realpatternbbox.height())
129 else:
130 ystep = unit.topt(self.ystep)
131 if not xstep:
132 raise ValueError("xstep in pattern cannot be zero")
133 if not ystep:
134 raise ValueError("ystep in pattern cannot be zero")
135 patternbbox = self.patternbbox or realpatternbbox.enlarged(5*unit.pt)
136 patterntrafo = self.patterntrafo or trafo.trafo()
138 registry.add(PDFpattern(self.id, self.patterntype, self.painttype, self.tilingtype,
139 patternbbox, xstep, ystep, patterntrafo,
140 lambda file, writer, context: canvas._canvas.outputPDF(self, file, writer, context()),
141 lambda registry: canvas._canvas.registerPDF(self, registry),
142 registry))
144 pattern.clear = attr.clearclass(pattern)
147 _base = 0.1 * unit.v_cm
149 class hatched(pattern):
150 def __init__(self, dist, angle, strokestyles=[]):
151 pattern.__init__(self, painttype=1, tilingtype=1, xstep=dist, ystep=1000*unit.t_pt, bbox=None, trafo=trafo.rotate(angle))
152 self.strokestyles = attr.mergeattrs([style.linewidth.THIN] + strokestyles)
153 attr.checkattrs(self.strokestyles, [style.strokestyle])
154 self.dist = dist
155 self.angle = angle
156 self.stroke(path.line_pt(0, -500, 0, 500), self.strokestyles)
158 def __call__(self, dist=None, angle=None, strokestyles=None):
159 if dist is None:
160 dist = self.dist
161 if angle is None:
162 angle = self.angle
163 if strokestyles is None:
164 strokestyles = self.strokestyles
165 return hatched(dist, angle, strokestyles)
167 hatched0 = hatched(_base, 0)
168 hatched0.SMALL = hatched0(_base/math.sqrt(64))
169 hatched0.SMALL = hatched0(_base/math.sqrt(64))
170 hatched0.SMALl = hatched0(_base/math.sqrt(32))
171 hatched0.SMAll = hatched0(_base/math.sqrt(16))
172 hatched0.SMall = hatched0(_base/math.sqrt(8))
173 hatched0.Small = hatched0(_base/math.sqrt(4))
174 hatched0.small = hatched0(_base/math.sqrt(2))
175 hatched0.normal = hatched0(_base)
176 hatched0.large = hatched0(_base*math.sqrt(2))
177 hatched0.Large = hatched0(_base*math.sqrt(4))
178 hatched0.LArge = hatched0(_base*math.sqrt(8))
179 hatched0.LARge = hatched0(_base*math.sqrt(16))
180 hatched0.LARGe = hatched0(_base*math.sqrt(32))
181 hatched0.LARGE = hatched0(_base*math.sqrt(64))
183 hatched45 = hatched(_base, 45)
184 hatched45.SMALL = hatched45(_base/math.sqrt(64))
185 hatched45.SMALl = hatched45(_base/math.sqrt(32))
186 hatched45.SMAll = hatched45(_base/math.sqrt(16))
187 hatched45.SMall = hatched45(_base/math.sqrt(8))
188 hatched45.Small = hatched45(_base/math.sqrt(4))
189 hatched45.small = hatched45(_base/math.sqrt(2))
190 hatched45.normal = hatched45(_base)
191 hatched45.large = hatched45(_base*math.sqrt(2))
192 hatched45.Large = hatched45(_base*math.sqrt(4))
193 hatched45.LArge = hatched45(_base*math.sqrt(8))
194 hatched45.LARge = hatched45(_base*math.sqrt(16))
195 hatched45.LARGe = hatched45(_base*math.sqrt(32))
196 hatched45.LARGE = hatched45(_base*math.sqrt(64))
198 hatched90 = hatched(_base, 90)
199 hatched90.SMALL = hatched90(_base/math.sqrt(64))
200 hatched90.SMALl = hatched90(_base/math.sqrt(32))
201 hatched90.SMAll = hatched90(_base/math.sqrt(16))
202 hatched90.SMall = hatched90(_base/math.sqrt(8))
203 hatched90.Small = hatched90(_base/math.sqrt(4))
204 hatched90.small = hatched90(_base/math.sqrt(2))
205 hatched90.normal = hatched90(_base)
206 hatched90.large = hatched90(_base*math.sqrt(2))
207 hatched90.Large = hatched90(_base*math.sqrt(4))
208 hatched90.LArge = hatched90(_base*math.sqrt(8))
209 hatched90.LARge = hatched90(_base*math.sqrt(16))
210 hatched90.LARGe = hatched90(_base*math.sqrt(32))
211 hatched90.LARGE = hatched90(_base*math.sqrt(64))
213 hatched135 = hatched(_base, 135)
214 hatched135.SMALL = hatched135(_base/math.sqrt(64))
215 hatched135.SMALl = hatched135(_base/math.sqrt(32))
216 hatched135.SMAll = hatched135(_base/math.sqrt(16))
217 hatched135.SMall = hatched135(_base/math.sqrt(8))
218 hatched135.Small = hatched135(_base/math.sqrt(4))
219 hatched135.small = hatched135(_base/math.sqrt(2))
220 hatched135.normal = hatched135(_base)
221 hatched135.large = hatched135(_base*math.sqrt(2))
222 hatched135.Large = hatched135(_base*math.sqrt(4))
223 hatched135.LArge = hatched135(_base*math.sqrt(8))
224 hatched135.LARge = hatched135(_base*math.sqrt(16))
225 hatched135.LARGe = hatched135(_base*math.sqrt(32))
226 hatched135.LARGE = hatched135(_base*math.sqrt(64))
229 class crosshatched(pattern):
230 def __init__(self, dist, angle, strokestyles=[]):
231 pattern.__init__(self, painttype=1, tilingtype=1, xstep=dist, ystep=dist, bbox=None, trafo=trafo.rotate(angle))
232 self.strokestyles = attr.mergeattrs([style.linewidth.THIN] + strokestyles)
233 attr.checkattrs(self.strokestyles, [style.strokestyle])
234 self.dist = dist
235 self.angle = angle
236 self.stroke(path.line_pt(0, 0, 0, unit.topt(dist)), self.strokestyles)
237 self.stroke(path.line_pt(0, 0, unit.topt(dist), 0), self.strokestyles)
239 def __call__(self, dist=None, angle=None, strokestyles=None):
240 if dist is None:
241 dist = self.dist
242 if angle is None:
243 angle = self.angle
244 if strokestyles is None:
245 strokestyles = self.strokestyles
246 return crosshatched(dist, angle, strokestyles)
248 crosshatched0 = crosshatched(_base, 0)
249 crosshatched0.SMALL = crosshatched0(_base/math.sqrt(64))
250 crosshatched0.SMALl = crosshatched0(_base/math.sqrt(32))
251 crosshatched0.SMAll = crosshatched0(_base/math.sqrt(16))
252 crosshatched0.SMall = crosshatched0(_base/math.sqrt(8))
253 crosshatched0.Small = crosshatched0(_base/math.sqrt(4))
254 crosshatched0.small = crosshatched0(_base/math.sqrt(2))
255 crosshatched0.normal = crosshatched0
256 crosshatched0.large = crosshatched0(_base*math.sqrt(2))
257 crosshatched0.Large = crosshatched0(_base*math.sqrt(4))
258 crosshatched0.LArge = crosshatched0(_base*math.sqrt(8))
259 crosshatched0.LARge = crosshatched0(_base*math.sqrt(16))
260 crosshatched0.LARGe = crosshatched0(_base*math.sqrt(32))
261 crosshatched0.LARGE = crosshatched0(_base*math.sqrt(64))
263 crosshatched45 = crosshatched(_base, 45)
264 crosshatched45.SMALL = crosshatched45(_base/math.sqrt(64))
265 crosshatched45.SMALl = crosshatched45(_base/math.sqrt(32))
266 crosshatched45.SMAll = crosshatched45(_base/math.sqrt(16))
267 crosshatched45.SMall = crosshatched45(_base/math.sqrt(8))
268 crosshatched45.Small = crosshatched45(_base/math.sqrt(4))
269 crosshatched45.small = crosshatched45(_base/math.sqrt(2))
270 crosshatched45.normal = crosshatched45
271 crosshatched45.large = crosshatched45(_base*math.sqrt(2))
272 crosshatched45.Large = crosshatched45(_base*math.sqrt(4))
273 crosshatched45.LArge = crosshatched45(_base*math.sqrt(8))
274 crosshatched45.LARge = crosshatched45(_base*math.sqrt(16))
275 crosshatched45.LARGe = crosshatched45(_base*math.sqrt(32))
276 crosshatched45.LARGE = crosshatched45(_base*math.sqrt(64))
279 class PDFpattern(pdfwriter.PDFobject):
281 def __init__(self, name, patterntype, painttype, tilingtype, bbox, xstep, ystep, trafo,
282 canvasoutputPDF, canvasregisterPDF, registry):
283 pdfwriter.PDFobject.__init__(self, "pattern", name, "Pattern")
284 self.name = name
285 self.patterntype = patterntype
286 self.painttype = painttype
287 self.tilingtype = tilingtype
288 self.bbox = bbox
289 self.xstep = xstep
290 self.ystep = ystep
291 self.trafo = trafo
292 self.canvasoutputPDF = canvasoutputPDF
294 self.contentlength = pdfwriter.PDFcontentlength((self.type, self.id))
295 registry.add(self.contentlength)
297 # we need to keep track of the resources used by the pattern, hence
298 # we create our own registry, which we merge immediately in the main registry
299 self.patternregistry = pdfwriter.PDFregistry()
300 # XXX passing canvasregisterPDF is a Q&D way to get access to the registerPDF method
301 # of the _canvas superclass of the pattern
302 canvasregisterPDF(self.patternregistry)
303 registry.mergeregistry(self.patternregistry)
305 def outputPDF(self, file, writer, registry):
306 file.write("<<\n"
307 "/Type /Pattern\n"
308 "/PatternType %d\n" % self.patterntype)
309 file.write("/PaintType %d\n" % self.painttype)
310 file.write("/TilingType %d\n" % self.tilingtype)
311 file.write("/BBox [%d %d %d %d]\n" % self.bbox.lowrestuple_pt())
312 file.write("/XStep %f\n" % self.xstep)
313 file.write("/YStep %f\n" % self.ystep)
314 file.write("/Matrix %s\n" % str(self.trafo))
315 procset = ["PDF"]
316 resources = {}
317 for type in self.patternregistry.types.keys():
318 for resource in self.patternregistry.types[type].values():
319 if resource.pageprocset is not None and resource.pageprocset not in procset:
320 procset.append(resource.pageprocset)
321 if resource.pageresource is not None:
322 resources.setdefault(resource.pageresource, []).append(resource)
323 file.write("/Resources <<\n"
324 "/ProcSet [ %s ]\n" % " ".join(["/%s" % p for p in procset]))
325 for pageresource, resources in resources.items():
326 file.write("/%s <<\n%s\n>>\n" % (pageresource, "\n".join(["/%s %i 0 R" % (resource.name, registry.getrefno(resource))
327 for resource in resources])))
328 file.write(">>\n")
329 file.write("/Length %i 0 R\n" % registry.getrefno(self.contentlength))
330 if writer.compress:
331 file.write("/Filter /FlateDecode\n")
332 file.write(">>\n")
334 file.write("stream\n")
335 beginstreampos = file.tell()
337 if writer.compress:
338 stream = pdfwriter.compressedstream(file, writer.compresslevel)
339 else:
340 stream = file
342 acontext = pdfwriter.context()
343 self.canvasoutputPDF(stream, writer, acontext)
344 if writer.compress:
345 stream.flush()
347 self.contentlength.contentlength = file.tell() - beginstreampos
348 file.write("\n"
349 "endstream\n")