2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2006 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
28 import bbox
as bboxmodule
32 # TODO: pattern should not derive from canvas but wrap a canvas
34 class pattern(canvas
._canvas
, attr
.exclusiveattr
, style
.fillstyle
):
36 def __init__(self
, painttype
=1, tilingtype
=1, xstep
=None, ystep
=None, bbox
=None, trafo
=None, **kwargs
):
37 canvas
._canvas
.__init
__(self
, **kwargs
)
38 attr
.exclusiveattr
.__init
__(self
, pattern
)
39 self
.id = "pattern%d" % id(self
)
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
49 self
.patternbbox
= bbox
50 self
.patterntrafo
= trafo
52 def __call__(self
, painttype
=_marker
, tilingtype
=_marker
, xstep
=_marker
, ystep
=_marker
,
53 bbox
=_marker
, trafo
=_marker
):
54 if painttype
is _marker
:
55 painttype
= self
.painttype
56 if tilingtype
is _marker
:
57 tilingtype
= self
.tilingtype
66 return pattern(painttype
, tilingtype
, xstep
, ystep
, bbox
, trafo
)
69 return bboxmodule
.empty()
71 def processPS(self
, file, writer
, context
, registry
, bbox
):
72 # process pattern, letting it register its resources and calculate the bbox of the pattern
73 patternfile
= cStringIO
.StringIO()
74 realpatternbbox
= bboxmodule
.empty()
75 canvas
._canvas
.processPS(self
, patternfile
, writer
, pswriter
.context(), registry
, realpatternbbox
)
76 patternproc
= patternfile
.getvalue()
79 if self
.xstep
is None:
80 xstep
= unit
.topt(realpatternbbox
.width())
82 xstep
= unit
.topt(self
.xstep
)
83 if self
.ystep
is None:
84 ystep
= unit
.topt(realpatternbbox
.height())
86 ystep
= unit
.topt(self
.ystep
)
88 raise ValueError("xstep in pattern cannot be zero")
90 raise ValueError("ystep in pattern cannot be zero")
91 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(5*unit
.pt
)
93 patternprefix
= "\n".join(("<<",
94 "/PatternType %d" % self
.patterntype
,
95 "/PaintType %d" % self
.painttype
,
96 "/TilingType %d" % self
.tilingtype
,
97 "/BBox[%d %d %d %d]" % patternbbox
.lowrestuple_pt(),
100 "/PaintProc {\nbegin\n"))
101 patterntrafostring
= self
.patterntrafo
is None and "matrix" or str(self
.patterntrafo
)
102 patternsuffix
= "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring
104 registry
.add(pswriter
.PSdefinition(self
.id, "".join((patternprefix
, patternproc
, patternsuffix
))))
107 file.write("%s setpattern\n" % self
.id)
109 def processPDF(self
, file, writer
, context
, registry
, bbox
):
110 patternfile
= cStringIO
.StringIO()
111 realpatternbbox
= bboxmodule
.empty()
112 canvas
._canvas
.processPDF(self
, patternfile
, writer
, pdfwriter
.context(), registry
, realpatternbbox
)
113 patternproc
= patternfile
.getvalue()
115 if self
.xstep
is None:
116 xstep
= unit
.topt(realpatternbbox
.width())
118 xstep
= unit
.topt(self
.xstep
)
119 if self
.ystep
is None:
120 ystep
= unit
.topt(realpatternbbox
.height())
122 ystep
= unit
.topt(self
.ystep
)
124 raise ValueError("xstep in pattern cannot be zero")
126 raise ValueError("ystep in pattern cannot be zero")
127 patternbbox
= self
.patternbbox
or realpatternbbox
.enlarged(5*unit
.pt
)
128 patterntrafo
= self
.patterntrafo
or trafo
.trafo()
130 registry
.add(PDFpattern(self
.id, self
.patterntype
, self
.painttype
, self
.tilingtype
,
131 patternbbox
, xstep
, ystep
, patterntrafo
,
132 patternproc
, registry
))
135 if context
.colorspace
!= "Pattern":
136 # we only set the fill color space (see next comment)
137 file.write("/Pattern cs\n")
138 context
.colorspace
= "Pattern"
139 if context
.strokeattr
:
140 # using patterns as stroke colors doesn't seem to work, so
141 # we just don't do this...
142 warnings
.warn("ignoring stroke color for patterns in PDF")
144 file.write("/%s scn\n"% self
.id)
147 pattern
.clear
= attr
.clearclass(pattern
)
150 _base
= 0.1 * unit
.v_cm
152 class hatched(pattern
):
153 def __init__(self
, dist
, angle
, strokestyles
=[]):
154 pattern
.__init
__(self
, painttype
=1, tilingtype
=1, xstep
=dist
, ystep
=1000*unit
.t_pt
, bbox
=None, trafo
=trafo
.rotate(angle
))
155 self
.strokestyles
= attr
.mergeattrs([style
.linewidth
.THIN
] + strokestyles
)
156 attr
.checkattrs(self
.strokestyles
, [style
.strokestyle
])
159 self
.stroke(path
.line_pt(0, -500, 0, 500), self
.strokestyles
)
161 def __call__(self
, dist
=None, angle
=None, strokestyles
=None):
166 if strokestyles
is None:
167 strokestyles
= self
.strokestyles
168 return hatched(dist
, angle
, strokestyles
)
170 hatched0
= hatched(_base
, 0)
171 hatched0
.SMALL
= hatched0(_base
/math
.sqrt(64))
172 hatched0
.SMALL
= hatched0(_base
/math
.sqrt(64))
173 hatched0
.SMALl
= hatched0(_base
/math
.sqrt(32))
174 hatched0
.SMAll
= hatched0(_base
/math
.sqrt(16))
175 hatched0
.SMall
= hatched0(_base
/math
.sqrt(8))
176 hatched0
.Small
= hatched0(_base
/math
.sqrt(4))
177 hatched0
.small
= hatched0(_base
/math
.sqrt(2))
178 hatched0
.normal
= hatched0(_base
)
179 hatched0
.large
= hatched0(_base
*math
.sqrt(2))
180 hatched0
.Large
= hatched0(_base
*math
.sqrt(4))
181 hatched0
.LArge
= hatched0(_base
*math
.sqrt(8))
182 hatched0
.LARge
= hatched0(_base
*math
.sqrt(16))
183 hatched0
.LARGe
= hatched0(_base
*math
.sqrt(32))
184 hatched0
.LARGE
= hatched0(_base
*math
.sqrt(64))
186 hatched45
= hatched(_base
, 45)
187 hatched45
.SMALL
= hatched45(_base
/math
.sqrt(64))
188 hatched45
.SMALl
= hatched45(_base
/math
.sqrt(32))
189 hatched45
.SMAll
= hatched45(_base
/math
.sqrt(16))
190 hatched45
.SMall
= hatched45(_base
/math
.sqrt(8))
191 hatched45
.Small
= hatched45(_base
/math
.sqrt(4))
192 hatched45
.small
= hatched45(_base
/math
.sqrt(2))
193 hatched45
.normal
= hatched45(_base
)
194 hatched45
.large
= hatched45(_base
*math
.sqrt(2))
195 hatched45
.Large
= hatched45(_base
*math
.sqrt(4))
196 hatched45
.LArge
= hatched45(_base
*math
.sqrt(8))
197 hatched45
.LARge
= hatched45(_base
*math
.sqrt(16))
198 hatched45
.LARGe
= hatched45(_base
*math
.sqrt(32))
199 hatched45
.LARGE
= hatched45(_base
*math
.sqrt(64))
201 hatched90
= hatched(_base
, 90)
202 hatched90
.SMALL
= hatched90(_base
/math
.sqrt(64))
203 hatched90
.SMALl
= hatched90(_base
/math
.sqrt(32))
204 hatched90
.SMAll
= hatched90(_base
/math
.sqrt(16))
205 hatched90
.SMall
= hatched90(_base
/math
.sqrt(8))
206 hatched90
.Small
= hatched90(_base
/math
.sqrt(4))
207 hatched90
.small
= hatched90(_base
/math
.sqrt(2))
208 hatched90
.normal
= hatched90(_base
)
209 hatched90
.large
= hatched90(_base
*math
.sqrt(2))
210 hatched90
.Large
= hatched90(_base
*math
.sqrt(4))
211 hatched90
.LArge
= hatched90(_base
*math
.sqrt(8))
212 hatched90
.LARge
= hatched90(_base
*math
.sqrt(16))
213 hatched90
.LARGe
= hatched90(_base
*math
.sqrt(32))
214 hatched90
.LARGE
= hatched90(_base
*math
.sqrt(64))
216 hatched135
= hatched(_base
, 135)
217 hatched135
.SMALL
= hatched135(_base
/math
.sqrt(64))
218 hatched135
.SMALl
= hatched135(_base
/math
.sqrt(32))
219 hatched135
.SMAll
= hatched135(_base
/math
.sqrt(16))
220 hatched135
.SMall
= hatched135(_base
/math
.sqrt(8))
221 hatched135
.Small
= hatched135(_base
/math
.sqrt(4))
222 hatched135
.small
= hatched135(_base
/math
.sqrt(2))
223 hatched135
.normal
= hatched135(_base
)
224 hatched135
.large
= hatched135(_base
*math
.sqrt(2))
225 hatched135
.Large
= hatched135(_base
*math
.sqrt(4))
226 hatched135
.LArge
= hatched135(_base
*math
.sqrt(8))
227 hatched135
.LARge
= hatched135(_base
*math
.sqrt(16))
228 hatched135
.LARGe
= hatched135(_base
*math
.sqrt(32))
229 hatched135
.LARGE
= hatched135(_base
*math
.sqrt(64))
232 class crosshatched(pattern
):
233 def __init__(self
, dist
, angle
, strokestyles
=[]):
234 pattern
.__init
__(self
, painttype
=1, tilingtype
=1, xstep
=dist
, ystep
=dist
, bbox
=None, trafo
=trafo
.rotate(angle
))
235 self
.strokestyles
= attr
.mergeattrs([style
.linewidth
.THIN
] + strokestyles
)
236 attr
.checkattrs(self
.strokestyles
, [style
.strokestyle
])
239 self
.stroke(path
.line_pt(0, 0, 0, unit
.topt(dist
)), self
.strokestyles
)
240 self
.stroke(path
.line_pt(0, 0, unit
.topt(dist
), 0), self
.strokestyles
)
242 def __call__(self
, dist
=None, angle
=None, strokestyles
=None):
247 if strokestyles
is None:
248 strokestyles
= self
.strokestyles
249 return crosshatched(dist
, angle
, strokestyles
)
251 crosshatched0
= crosshatched(_base
, 0)
252 crosshatched0
.SMALL
= crosshatched0(_base
/math
.sqrt(64))
253 crosshatched0
.SMALl
= crosshatched0(_base
/math
.sqrt(32))
254 crosshatched0
.SMAll
= crosshatched0(_base
/math
.sqrt(16))
255 crosshatched0
.SMall
= crosshatched0(_base
/math
.sqrt(8))
256 crosshatched0
.Small
= crosshatched0(_base
/math
.sqrt(4))
257 crosshatched0
.small
= crosshatched0(_base
/math
.sqrt(2))
258 crosshatched0
.normal
= crosshatched0
259 crosshatched0
.large
= crosshatched0(_base
*math
.sqrt(2))
260 crosshatched0
.Large
= crosshatched0(_base
*math
.sqrt(4))
261 crosshatched0
.LArge
= crosshatched0(_base
*math
.sqrt(8))
262 crosshatched0
.LARge
= crosshatched0(_base
*math
.sqrt(16))
263 crosshatched0
.LARGe
= crosshatched0(_base
*math
.sqrt(32))
264 crosshatched0
.LARGE
= crosshatched0(_base
*math
.sqrt(64))
266 crosshatched45
= crosshatched(_base
, 45)
267 crosshatched45
.SMALL
= crosshatched45(_base
/math
.sqrt(64))
268 crosshatched45
.SMALl
= crosshatched45(_base
/math
.sqrt(32))
269 crosshatched45
.SMAll
= crosshatched45(_base
/math
.sqrt(16))
270 crosshatched45
.SMall
= crosshatched45(_base
/math
.sqrt(8))
271 crosshatched45
.Small
= crosshatched45(_base
/math
.sqrt(4))
272 crosshatched45
.small
= crosshatched45(_base
/math
.sqrt(2))
273 crosshatched45
.normal
= crosshatched45
274 crosshatched45
.large
= crosshatched45(_base
*math
.sqrt(2))
275 crosshatched45
.Large
= crosshatched45(_base
*math
.sqrt(4))
276 crosshatched45
.LArge
= crosshatched45(_base
*math
.sqrt(8))
277 crosshatched45
.LARge
= crosshatched45(_base
*math
.sqrt(16))
278 crosshatched45
.LARGe
= crosshatched45(_base
*math
.sqrt(32))
279 crosshatched45
.LARGE
= crosshatched45(_base
*math
.sqrt(64))
282 class PDFpattern(pdfwriter
.PDFobject
):
284 def __init__(self
, name
, patterntype
, painttype
, tilingtype
, bbox
, xstep
, ystep
, trafo
,
285 patternproc
, registry
):
286 pdfwriter
.PDFobject
.__init
__(self
, "pattern", name
, "Pattern")
288 self
.patterntype
= patterntype
289 self
.painttype
= painttype
290 self
.tilingtype
= tilingtype
295 self
.patternproc
= patternproc
296 #self.canvasoutputPDF = canvasoutputPDF
298 self
.contentlength
= pdfwriter
.PDFcontentlength((self
.type, self
.id))
299 registry
.add(self
.contentlength
)
301 # # we need to keep track of the resources used by the pattern, hence
302 # # we create our own registry, which we merge immediately in the main registry
303 # self.patternregistry = pdfwriter.PDFregistry()
304 # # XXX passing canvasregisterPDF is a Q&D way to get access to the registerPDF method
305 # # of the _canvas superclass of the pattern
306 # canvasregisterPDF(self.patternregistry)
307 # registry.mergeregistry(self.patternregistry)
309 def outputPDF(self
, file, writer
, registry
):
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
))
321 for type in self
.patternregistry
.types
.keys():
322 for resource
in self
.patternregistry
.types
[type].values():
323 if resource
.pageprocset
is not None and resource
.pageprocset
not in procset
:
324 procset
.append(resource
.pageprocset
)
325 if resource
.pageresource
is not None:
326 resources
.setdefault(resource
.pageresource
, []).append(resource
)
327 file.write("/Resources <<\n"
328 "/ProcSet [ %s ]\n" % " ".join(["/%s" % p
for p
in procset
]))
329 for pageresource
, resources
in resources
.items():
330 file.write("/%s <<\n%s\n>>\n" % (pageresource
, "\n".join(["/%s %i 0 R" % (resource
.name
, registry
.getrefno(resource
))
331 for resource
in resources
])))
333 file.write("/Length %i 0 R\n" % registry
.getrefno(self
.contentlength
))
335 file.write("/Filter /FlateDecode\n")
338 file.write("stream\n")
339 beginstreampos
= file.tell()
342 stream
= pdfwriter
.compressedstream(file, writer
.compresslevel
)
346 stream
.write(self
.patternproc
)
350 self
.contentlength
.contentlength
= file.tell() - beginstreampos