/[thuban]/trunk/thuban/Extensions/svgexport/svgmapwriter.py
ViewVC logotype

Annotation of /trunk/thuban/Extensions/svgexport/svgmapwriter.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2388 - (hide annotations)
Mon Nov 15 16:27:41 2004 UTC (20 years, 3 months ago) by bernhard
File MIME type: text/x-python
File size: 23618 byte(s)
* Extensions/svgexport/: Minor improvements to doc strings.

1 bh 2082 # Copyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH
2 bh 2074 # Authors:
3     # Markus Rechtien <[email protected]>
4 bernhard 2352 # Bernhard Reiter <[email protected]>
5 bh 2074 #
6     # This program is free software under the GPL (>=v2)
7     # Read the file COPYING coming with Thuban for details.
8    
9    
10     """
11 bernhard 2388 Classes needed to write a session in SVG format.
12 bh 2074 """
13    
14 bh 2082 # For compatibility with python 2.2
15     from __future__ import generators
16 bh 2074
17 bh 2082
18     __version__ = "$Revision$"
19     # $Source$
20     # $Id$
21    
22    
23 bh 2074 # Regular expressions used with Fontnames
24     import re
25     # Combining strings
26     from string import join
27     # We need to determine some object types
28 bh 2082 from types import ListType
29 bernhard 2352 # for SetBaseID
30     import binascii
31 bernhard 2344
32     from Thuban import _
33 bh 2074 # VirtualDC extends XMLWriter
34     from Thuban.Model.xmlwriter import XMLWriter, escape
35     # Color related classes from the model of thuban
36 bh 2082 from Thuban.Model.color import Transparent, Black
37 bh 2074 # The SVGRenderer is subclass of BaseRenderer
38     from Thuban.UI.baserenderer import BaseRenderer
39    
40     # Basic font map.
41     fontMap = { "Times" : re.compile("Times-Roman.*"),
42     "Helvetica" : re.compile("Helvetica.*"),
43     "Courier" : re.compile("Courier.*"),
44     }
45    
46     # Possible values for svg line joins.
47     svg_joins = {'miter':'miter', 'round':'round', 'bevel':'bevel'}
48     # Possible values for svg line caps.
49     svg_caps = {'':'', 'butt':'butt', 'round':'round', 'square':'square'}
50    
51     #
52     # Some pseudo classes to be compatible with the Baserenderer-class.
53     #
54     class Point:
55 bernhard 2388 """Simple Point class to save x,y coordinates."""
56 bh 2074 def __init__(self, xp=0, yp=0):
57     self.x = xp
58     self.y = yp
59    
60     class Trafo:
61 bernhard 2388 """Class for transformation properties transfer."""
62 bh 2074 def __init__(self):
63     self.trafos = []
64    
65     def Append(self, type, coeffs):
66 bh 2082 """Append a transformation to the list."""
67 bh 2074 self.trafos.append((type, coeffs))
68    
69     def Count(self):
70 bh 2082 """Get the number of transformations in list."""
71 bh 2074 return len(self.trafos)
72    
73     def Pop(self):
74 bh 2082 """Pop and return a transformation from the end of the list."""
75 bh 2074 if len(self.trafos) > 0:
76     return self.trafos.pop()
77     else: return None
78    
79     class Pattern:
80     def __init__(self, solid=1):
81     self.solid = solid
82    
83     class Pen:
84 bh 2082 """Pen object for property transfer."""
85 bh 2074 def __init__(self, pcolor = Black, pwidth = 1, pdashes = None):
86     self.color = pcolor
87     self.width = pwidth
88     self.dashes = pdashes
89     self.join = 'round'
90     self.cap = 'round'
91    
92     def GetColor(self):
93     return self.color
94    
95     def GetWidth(self):
96     return self.width
97    
98     def GetJoin(self):
99     return self.join
100    
101     def GetCap(self):
102     return self.cap
103    
104     def GetDashes(self):
105     if self.dashes is None or self.dashes is SOLID:
106     return []
107     else: return self.dashes
108    
109     class Brush:
110 bh 2082 """Brush property class."""
111 bh 2074 def __init__(self, bfill=Black, bpattern=None):
112 bh 2082 """Init the brush with the given values."""
113 bh 2074 self.fill = bfill
114     self.pattern = bpattern
115    
116     def GetColor(self):
117     return self.fill
118    
119     def GetPattern(self):
120     return self.pattern
121    
122     class Font:
123 bh 2082 """Font class that accts as property object."""
124 bh 2074 def __init__(self, ffamily='Helvetica', fsize=12):
125 bh 2082 """Init the font with the given values."""
126 bh 2074 self.family = ffamily
127     self.size = fsize
128    
129     def GetFaceName(self):
130 bh 2082 """Return the fontfamily the font belongs to."""
131 bh 2074 return self.family
132    
133     def GetPointSize(self):
134 bh 2082 """Return the size of the font in points."""
135 bh 2074 return self.size
136    
137     # Instantiate an empty pen.
138     TRANSPARENT_PEN = Pen(None, 0, None)
139     # Instantiate an empty brush.
140     TRANSPARENT_BRUSH = Brush(None, None)
141     # Instantiate a solid pattern.
142     SOLID = Pattern()
143    
144 bernhard 2344 class SVGMapWriterError(Exception):
145     """Get raised for problems when writing map-svg files.
146    
147     Occasion when this exception is raised:
148     Two layers have the same name to be used as BaseId: Name Clash
149     """
150    
151    
152 bh 2074 class SVGRenderer(BaseRenderer):
153     """Class to render a map onto a VirtualDC.
154    
155     This class, derived from BaseRenderer, will render a hole
156     session onto the VirtualDC to write all shapes as SVG code
157     to a file.
158     In opposite to other renderers it includes metadata, such as
159     shape ids and classification, when rendering the shapes.
160     """
161     def __init__(self, dc, map, scale, offset, region,
162     resolution = 1.0, honor_visibility = 1):
163 bh 2082 """Init SVGRenderer and call superclass init."""
164 bh 2074 BaseRenderer.__init__(self, dc, map, scale, offset, region,
165     resolution, honor_visibility)
166     #
167     self.factor = (abs(region[2]) + abs(region[3])) / (2.0 * 1000.0)
168 bernhard 2344 self.used_baseids=[] # needed for name clash check
169    
170 bh 2074 def make_point(self, x, y):
171 bh 2082 """Return a Point object from two values."""
172 bh 2074 return Point(x, y)
173    
174     def label_font(self):
175     """Return the font object for the label layer"""
176     return Font()
177    
178     def tools_for_property(self, prop):
179 bh 2082 """Return a pen/brush tuple build from a property object."""
180 bh 2074 fill = prop.GetFill()
181     if fill is Transparent:
182     brush = TRANSPARENT_BRUSH
183     else:
184     brush = Brush(fill, SOLID)
185    
186     stroke = prop.GetLineColor()
187     if stroke is Transparent:
188     pen = TRANSPARENT_PEN
189     else:
190     pen = Pen(stroke, prop.GetLineWidth() * self.factor, SOLID)
191     return pen, brush
192    
193     def draw_polygon_shape(self, layer, points, pen, brush):
194     """Draw a polygon shape from layer with the given brush and pen
195    
196     The shape is given by points argument which is a the return
197     value of the shape's Points() method. The coordinates in the
198     DC's coordinate system are determined with
199     self.projected_points.
200     """
201     points = self.projected_points(layer, points)
202    
203     if brush is not TRANSPARENT_BRUSH:
204     self.dc.SetBrush(brush)
205     self.dc.SetPen(pen)
206     for part in points:
207     self.dc.DrawLines(part)
208    
209     def draw_point_shape(self, layer, points, pen, brush):
210     """Draw a point shape from layer with the given brush and pen
211    
212     The shape is given by points argument which is a the return
213     value of the shape's Points() method. The coordinates in the
214     DC's coordinate system are determined with
215     self.projected_points.
216    
217     The point is drawn as a circle centered on the point.
218     """
219     points = self.projected_points(layer, points)
220     if not points:
221     return
222    
223     radius = self.factor * 2.0
224     self.dc.SetBrush(brush)
225     self.dc.SetPen(pen)
226     for part in points:
227     for p in part:
228     self.dc.DrawCircle(p.x - radius, p.y - radius,
229     2.0 * radius)
230    
231     def draw_shape_layer_incrementally(self, layer):
232     """Draw a shapelayer incrementally.
233     """
234     dc = self.dc
235     brush = TRANSPARENT_BRUSH
236     pen = TRANSPARENT_PEN
237 bernhard 2344
238 bh 2074 value = None
239     field = None
240     lc = layer.GetClassification()
241     field = layer.GetClassificationColumn()
242     defaultGroup = lc.GetDefaultGroup()
243     table = layer.ShapeStore().Table()
244    
245     if lc.GetNumGroups() == 0:
246     # There's only the default group, so we can pretend that
247     # there is no field to classifiy on which makes things
248     # faster since we don't need the attribute information at
249     # all.
250     field = None
251    
252     # Determine which render function to use.
253     useraw, draw_func, draw_func_param = \
254     self.low_level_renderer(layer)
255     tool_cache = {}
256    
257 bernhard 2352 new_baseid=dc.SetBaseID(layer.title)
258     if new_baseid in self.used_baseids:
259 bernhard 2344 raise SVGMapWriterError(_("Clash of layer names!\n")+ \
260     _("Two layers probably have the same name, try renaming one."))
261     # prefix of a shape id to be unique
262 bernhard 2352 self.used_baseids.append(new_baseid)
263 bh 2074 # Titel of current layer to the groups meta informations
264     dc.BeginGroup(meta={'Layer':layer.Title(), })
265     # Delete all MetaData
266     dc.FlushMeta()
267     for shape in self.layer_shapes(layer):
268     if field is None:
269     group = defaultGroup
270     value = group.GetDisplayText()
271     else:
272     value = table.ReadValue(shape.ShapeID(), field)
273     group = lc.FindGroup(value)
274    
275     if not group.IsVisible():
276     continue
277    
278     # Render classification
279     shapeType = layer.ShapeType()
280     props = group.GetProperties()
281    
282     # put meta infos into DC
283     if field and value:
284     dc.SetMeta({field:value, })
285     # set current shape id
286     dc.SetID(shape.ShapeID())
287    
288     try:
289     pen, brush = tool_cache[id(group)]
290     except KeyError:
291     pen, brush = tool_cache[id(group)] \
292     = self.tools_for_property(group.GetProperties())
293    
294     if useraw:
295     data = shape.RawData()
296     else:
297     data = shape.Points()
298     draw_func(draw_func_param, data, pen, brush)
299     # compatibility
300     if 0:
301     yield True
302     # reset shape id
303     dc.SetID(-1)
304     dc.SetBaseID("")
305     dc.EndGroup()
306    
307     def draw_raster_layer(self, layer):
308 bh 2082 """Draw the raster layer"""
309 bh 2074 # For now we cannot draw raster layers onto our VirtualDC
310     pass
311    
312     def draw_raster_data(self, data, format="BMP"):
313     """Draw the raster image in data onto the DC"""
314     # For now we cannot draw raster data onto our VirtualDC
315     pass
316    
317     def RenderMap(self, selected_layer, selected_shapes):
318 bh 2082 """Overriden to avoid automatic rendering of legend,
319 bh 2074 scalbar and frame.
320 bh 2082 """
321 bh 2074 dc = self.dc
322     self.selected_layer = selected_layer
323     self.selected_shapes = selected_shapes
324     minx, miny, width, height = self.region
325     # scale down to a size of 1000
326     trans = Trafo()
327     trans.Append('scale', (1000.0 / ((width + height) / 2.0)))
328     #
329     dc.BeginClipPath('mapclip')
330     dc.DrawRectangle(0, 0, width, height)
331     dc.EndClipPath()
332     #
333     dc.BeginGroup(meta={'Object':'map', }, clipid='mapclip', \
334     transform=trans)
335     self.render_map()
336     dc.EndGroup()
337    
338    
339     class VirtualDC(XMLWriter):
340 bh 2082 """This class imitates a DC and writes SVG instead.
341 bh 2074
342     All shapes and graphic objects will be turned into
343     SVG elements and will be written into a file.
344     Any properties, such as stroke width or stroke color,
345     will be written together with the SVG elementents.
346 bh 2082 """
347 bh 2074 def __init__(self, file, dim=(0,0), units=''):
348 bh 2082 """Setup some variables and objects for property collection."""
349 bh 2074 XMLWriter.__init__(self)
350     self.dim = dim
351     self.units = units
352     self.pen = {}
353     self.brush = {}
354     self.font = {}
355     self.meta = {}
356     self.style = {}
357     # Some buffers
358     self.points = []
359     self.id = -1
360     self.flush_meta = 1
361     self.write(file)
362    
363     def write_indent(self, str):
364 bh 2082 """Write a string to the file with the current indention level.
365     """
366 bh 2074 from Thuban.Model.xmlwriter import TAB
367     self.file.write("%s%s" % (TAB*self.indent_level, str))
368    
369     def AddMeta(self, key, val):
370 bh 2082 """Append some metadata to the array that will be
371 bh 2074 written with the next svg-element
372 bh 2082 """
373 bh 2074 if key is '' or val is '':
374     return
375     self.meta[key] = val
376    
377     def SetMeta(self, pairs, flush_after=1):
378 bh 2082 """Delete old meta informations and set the new ones."""
379 bh 2074 self.meta = {}
380     self.flush_meta = flush_after
381     for key, val in pairs.items():
382     self.AddMeta(key, val)
383    
384     def FlushMeta(self):
385 bh 2082 """Drop collected metadata."""
386 bh 2074 self.meta = {}
387    
388     def BeginGroup(self, **args):
389 bh 2082 """Begin a group of elements.
390 bh 2074
391     Possible arguments:
392     meta A list of key, value metadata pairs
393     style A list of key, value style attributes
394     clipid The ID of a clipPath definition to be
395     applied to this group
396 bh 2082 """
397 bh 2074 self.FlushMeta()
398     # adding meta data
399     if args.has_key('meta'):
400     for key, val in args['meta'].items():
401     self.AddMeta(key, val)
402     attribs = " "
403     # adding style attributes
404     if args.has_key('style'):
405     for key, val in args['style'].items():
406     attribs += '%s="%s" ' % (key, val)
407     # adding clip informations
408     if args.has_key("clipid"):
409     attribs += ' clip-path="url(#%s)"' % (args['clipid'],)
410     # FIXME: this shouldn't be static
411     attribs += ' clip-rule="evenodd"'
412     if args.has_key('transform'):
413     trafostr = self.parse_trafo(args['transform'])
414     if trafostr:
415     attribs += ' transform="%s"' % (trafostr)
416     # put everything together
417     self.write_indent('<g %s%s>\n' % (self.make_meta(), attribs))
418     self.indent_level += 1
419    
420     def parse_trafo(self, trafo):
421 bh 2082 """Examine a trafo object for asigned transformations details."""
422 bh 2074 if not trafo:
423     return ''
424     retval = ''
425     while trafo.Count() > 0:
426     trans, coeffs = tuple(trafo.Pop())
427     if isinstance(coeffs, ListType):
428     retval += " %s%s" % (trans, join(coeffs, ', '))
429     else: retval += " %s(%s)" % (trans, coeffs)
430     # return the string
431     return retval
432    
433     def EndGroup(self):
434 bh 2082 """End a group of elements"""
435 bh 2074 self.indent_level -= 1
436     self.write_indent('</g>\n')
437     self.FlushMeta()
438    
439     def BeginExport(self):
440 bh 2082 """Start the export process and write basic document
441 bh 2074 informations to the file.
442 bh 2082 """
443 bh 2074 self.write_indent('<?xml version="1.0" encoding="ISO-8859-1" '
444     'standalone="yes"?>\n')
445     width, height = self.dim
446     self.write_indent('<svg>\n')
447     self.indent_level += 1
448    
449     def EndExport(self):
450 bh 2082 """End the export process with closing the SVG tag and close
451     the file accessor"""
452 bh 2074 self.indent_level -= 1
453     self.write_indent('</svg>\n')
454     self.close()
455    
456     def Close(self):
457 bh 2082 """Close the file."""
458 bh 2074 self.close()
459    
460     def BeginDrawing(self):
461 bh 2082 """Dummy function to work with the Thuban renderers."""
462 bh 2074 pass
463    
464     def EndDrawing(self):
465 bh 2082 """Dummy function to work with the Thuban renderers."""
466 bh 2074 pass
467    
468     def GetSizeTuple(self):
469 bh 2082 """Return the dimension of this virtual canvas."""
470 bh 2074 return self.dim
471    
472     def GetTextExtent(self, text):
473 bh 2082 """Return the dimension of the given text."""
474 bh 2074 # FIXME: find something more appropriate
475     try:
476     if self.font:
477     return (int(self.font["font-size"]),
478     len(text) * int(self.font["font-size"]))
479     else: return (12,len(text) * 10)
480     except ValueError:
481     return (12,len(text) * 10)
482    
483 bernhard 2352
484     def SetBaseID(self, id):
485     """Set first part of ID stored by the svg elements. Return it.
486    
487     Will be used in make_id() as first part of an XML attribute "id".
488     The second part is set by SetID().
489     Check comments at make_id().
490    
491     We might add an abritrary "t" for thuban if the parameter
492     starts with XML, which is not allowed in XML 1.0.
493    
494     We need to ensure that all characters are okay as XML id attribute.
495     As there seem no easy way in Python (today 20040925) to check
496     for compliance with XML 1.0 character classes like NameChar,
497     we use Python's string method isalnum() as approximation.
498     (See http://mail.python.org/pipermail/xml-sig/2002-January/006981.html
499     and xmlgenchar.py. To be better we would need to hold our own
500     huge table of allowed unicode characters.
501     FIXME if python comes with a better funcation for XML 1.0 NameChar)
502    
503     Characters that are not in our approx of NameChar get transformed
504     get escaped to their hex value.
505     """
506     # an ID Name shall not start with xml.
507     if id[0:3].lower() == "xml":
508     id = "t" + id
509    
510     self.baseid = ""
511     for c in id:
512     if c.isalnum() or c in ".-_":
513     self.baseid += c
514     else:
515     self.baseid += binascii.b2a_hex(c)
516     return self.baseid
517    
518 bh 2074 def SetID(self, id):
519 bernhard 2350 """Set second part of ID stored by the svg elements.
520    
521     Will be used in make_id() as first part of an XML attribute "id".
522 bernhard 2352 Only set this to positive integer numbers.
523 bernhard 2350 Read comments at SetBaseID() and make_id().
524     """
525 bh 2074 self.id = id
526    
527     def SetFont(self, font):
528 bh 2082 """Set the fontproperties to use with text elements."""
529 bh 2074 if font is not None:
530     fontname = font.GetFaceName()
531     size = font.GetPointSize()
532     for svgfont, pattern in fontMap.items():
533     if pattern.match(fontname):
534     fontname = svgfont
535     break
536     if fontname:
537     self.font["font-family"] = fontname
538     else: self.font["font-family"] = None
539     if size:
540     self.font["font-size"] = str(size)
541     else: self.font["font-size"] = None
542    
543     def SetPen(self, pen):
544 bh 2082 """Set the style of the pen used to draw graphics."""
545 bh 2074 if pen is TRANSPARENT_PEN:
546     self.pen = {}
547     else:
548     self.pen["stroke"] = pen.GetColor().hex()
549     self.pen["stroke-dasharray"] = join(pen.GetDashes(), ',')
550     self.pen["stroke-width"] = pen.GetWidth()
551     self.pen["stroke-linejoin"] = svg_joins[pen.GetJoin()]
552     self.pen["stroke-linecap"] = svg_caps[pen.GetCap()]
553    
554     def SetBrush(self, brush):
555 bh 2082 """Set the fill properties."""
556 bh 2074 if brush is TRANSPARENT_BRUSH:
557     self.brush['fill'] = 'none'
558     elif brush.GetPattern() is SOLID:
559     self.brush['fill'] = brush.GetColor().hex()
560     else: # TODO Handle Patterns
561     pass
562    
563     def SetTextForeground(self, color):
564 bh 2082 """Set the color of the text foreground."""
565 bh 2074 self.font['fill'] = color.hex()
566    
567     def make_style(self, line=0, fill=0, font=0):
568 bh 2082 """Build the style attribute including desired properties
569     such as fill, forground, stroke, etc."""
570 bh 2074 result = []
571     # little helper function
572     def append(pairs):
573     for key, val in pairs.items():
574     if not val in [None, '']:
575     result.append('%s:%s' % (key, val))
576     #
577     if line and len(self.pen) > 0:
578     append(self.pen)
579     if fill and len(self.brush) > 0:
580     append(self.brush)
581     if font and len(self.font) > 0:
582     append(self.font)
583     style = join(result, '; ')
584     if style:
585     return 'style="%s"' % (style, )
586     else:
587     return ''
588    
589     def make_meta(self, meta=None):
590 bh 2082 """Build the meta attribute."""
591 bh 2074 result = []
592     if not meta:
593     meta = self.meta
594     if len(meta) is 0:
595     return ''
596     for key, val in meta.items():
597     if not val in [None, '', 'none']:
598     result.append('%s:%s' % (key, val))
599     if self.flush_meta:
600     self.meta = {}
601     return 'meta="%s"' % (join(result, '; '))
602    
603     def make_id(self):
604 bernhard 2344 """Return id= string for object out of currently set baseid and id.
605    
606     Return the empty string if no id was set.
607 bernhard 2350
608     In an XML file each id should be unique
609     (see XML 1.0 section 3.3.1 Attribute Types, Validity constraint: ID)
610     So this function should only return a unique values.
611     which also coforms to the the XML "Name production" (section 3.2).
612    
613     For this it completely depends
614     on what has been set by SetBaseID() and SetID().
615     Only call this function if you have called them w unique values before
616 bernhard 2352 (or negative x in SetID(x) to get an empty result)
617     Will raise SVGMapWriterError if SetID value cannot be converted to %d.
618 bernhard 2350
619     A check of uniqueness in this function might be time consuming,
620     because it would require to hold and search through a complete table.
621 bernhard 2344 """
622 bh 2074 if self.id < 0:
623     return ''
624 bernhard 2352 try:
625     id= 'id="%s_%d"' % (self.baseid, self.id)
626     except TypeError, inst:
627     raise SVGMapWriterError(_("Internal make_id() failure: ") \
628     + repr(inst))
629     return id
630 bh 2074
631     def DrawEllipse(self, x, y, dx, dy):
632 bh 2082 """Draw an ellipse."""
633 bh 2074 elips = '<ellipse cx="%s" cy="%s" rx="%s" ry="%s" %s %s %s/>\n'
634     self.write_indent(elips % (x, y, dx, dy, self.make_id(),
635     self.make_style(1,1,0), self.make_meta()) )
636    
637     def DrawCircle(self, x, y, radius):
638 bh 2082 """Draw a circle onto the virtual dc."""
639 bh 2074 self.write_indent('<circle cx="%s" cy="%s" r="%s" %s %s %s/>\n' %
640     (x, y, radius, self.make_id(), self.make_style(1,1,0),
641     self.make_meta()) )
642    
643     def DrawRectangle(self, x, y, width, height):
644 bh 2082 """Draw a rectangle with the given parameters."""
645 bh 2074 rect = '<rect x="%s" y="%s" width="%s" height="%s" %s %s %s/>\n'
646     self.write_indent(rect % ( x, y, width, height, self.make_id(),
647     self.make_style(1,1,0), self.make_meta()) )
648    
649     def DrawText(self, text, x, y):
650 bh 2082 """Draw Text at the given position."""
651 bh 2074 beginText = '<text x="%s" y="%s" %s %s %s>'
652     self.write_indent(beginText % ( x, y, self.make_id(),
653     self.make_style(0,0,1), self.make_meta()) )
654     self.file.write(escape(text))
655     self.file.write('</text>\n')
656    
657     def DrawLines(self, points):
658 bh 2082 """Draw some points into a Buffer that will be
659 bh 2074 written before the next object.
660 bh 2082 """
661 bh 2074 self.DrawPolygon(points,0)
662    
663     def DrawPolygon(self, polygon, closed=1):
664 bh 2082 """Draw a polygon onto the virtual dc."""
665 bh 2074 self.write_indent('<path %s ' % (self.make_style(1,1,0)))
666     data = []
667     i = 0
668     for point in polygon:
669     if i is 0:
670     data.append('M %s %s ' % (point.x, point.y))
671     else:
672     data.append('L %s %s ' % (point.x, point.y))
673     i+=1
674     # FIXME: Determine if path is closed
675     if closed:
676     data.append('Z')
677     # Put everything together and write it to the file
678     self.file.write('%s %s d="%s"/>\n' % (self.make_id(),
679     self.make_meta(), join(data, '') ) )
680    
681     def DrawSpline(self, points, closed=0):
682 bh 2082 """Draw a spline object.
683     """
684 bh 2074 self.DrawPolygon(points, 0)
685     print "TODO: DrawSpline(..)"
686     return # TODO: Implement
687    
688     def BeginClipPath(self, id):
689 bh 2082 """Build a clipping region to draw in."""
690 bh 2074 self.write_indent('<clipPath id="%s">\n' % id)
691     self.indent_level += 1
692    
693     def EndClipPath(self):
694 bh 2082 """End a clip path."""
695 bh 2074 self.indent_level -= 1
696     self.write_indent("</clipPath>\n")

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26