/[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 2352 - (hide annotations)
Mon Sep 27 09:56:13 2004 UTC (20 years, 5 months ago) by bernhard
File MIME type: text/x-python
File size: 24015 byte(s)
More fixes to svgexport to make used ids unique and
conforming to XML's Name production.

* Extensions/svgexport/test/test_svgmapwriter.py: Added new tests
test_xml_id_constraints(), test_make_ide_nosetbaseid() and
test_make_id_nonintegersetid().  Switched SetID and SetBaseID.
Added Bernhard R. as author.
* Extensions/svgexport/svgmapwriter.py (make_id): Using "_" as
concatenation char now (makes test_make_ide_nosetbaseid() valid).
Also transform second id part with "%d" and catch the TypeError
to raise SVGMapWriterError (making test_make_id_nonintegersetid() ok).
Corrected typo inBernhard's author line.
(SetBaseID): Return the transformed base id. Transform characters
which are not alnum() or in ".-_" to binascii.b2a_hex(). Added
import binascii. If to be set string starts with "xml" or so, add "t".
(draw_shape_layer_incrementally): use the returned value of SetBaseID
for used_baseids checks.

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