/[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 2482 - (hide annotations)
Sat Dec 18 02:36:59 2004 UTC (20 years, 2 months ago) by bernhard
File MIME type: text/x-python
File size: 26074 byte(s)
	svgexport 1.0.0: Treats holes and islands nicely. Documentation added.

	* Extensions/svgexport/test/test_svgmapwriter.py:
	Added new tests: test_export_polygon_with_hole()
	and test_polygon_with_hole().

	* Extensions/svgexport/svgmapwriter.py
	(draw_polygon_shape()): Uses DrawPath correctly now.

	* Doc/manual/thuban-manual.xml: Added documentation for stable
	extention svgexport.
	* Doc/manual/thuban-manual-de.xml: Copied English section about
	svexport over.

 	* Extensions/svgexport/__init__.py: Bumped version number to 1.0.0.

	* Extensions/svgexport/svgsaver.py,maplegend.py:
	Moved from experimental to stable extension menu.

	* Extensions/svgexport/TODO: updated.

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