""" Classes for rendering SVG elements. """ from qp.fill.html import element attribute_mapping = dict( xml_space='xml:space', xmlns_xlink='xmlns:xlink', stroke_width='stroke-width', font_size='font-size', font_weight='font-weight', text_anchor='text-anchor', enable_background='enable-background', xlink_href='xlink:href', view_box='viewBox') class SVGElement (object): default_attributes = dict() def __init__(self, *args, **attributes): self.content = list(args) self.attributes = self.default_attributes.copy() self.attributes.update(attributes) def add_attributes(self, **attributes): self.attributes.update(attributes) def set_transform(self, *args): self.add_attributes(transform=" ".join(str(a) for a in args)) def add(self, *args): for arg in args: self.content.append(arg) def get_content(self): return self.content def get_attributes(self, **attributes): result = dict() attrs = self.attributes.copy() attrs.update(attributes) for key, value in attrs.items(): result[attribute_mapping.get(key, key)] = value return result def render_content(self): result = [] for item in self.content: if isinstance(item, SVGElement): result.append(item.render()) else: result.append(item) return result def render_element_name(self): return getattr(self, 'element_name', self.__class__.__name__.lower()) def render(self, **attributes): return element(self.render_element_name(), tuple(self.render_content()), **self.get_attributes(**attributes)) class SVG (SVGElement): default_attributes = dict( version="1.1", xmlns="http://www.w3.org/2000/svg", xmlns_xlink="http://www.w3.org/1999/xlink", xml_space="preserve") class Text (SVGElement): default_attributes = dict( fill="#000", stroke="none") class TextPath (SVGElement): pass class TSpan (SVGElement): pass class A (SVGElement): pass class G (SVGElement): pass class Defs (SVGElement): pass class Use (SVGElement): pass class PolyLine (SVGElement): pass class Polygon (SVGElement): pass class Circle (SVGElement): pass class Ellipse (SVGElement): pass class Rect (SVGElement): pass class Title (SVGElement): pass class Desc (SVGElement): pass class LinearGradient (SVGElement): element_name = 'linearGradient' class RadialGradient (SVGElement): element_name = 'radialGradient' class Stop (SVGElement): pass class Path (SVGElement): def __init__(self, *args, **kwargs): SVGElement.__init__(self, *args, **kwargs) self.data = [] def add_data(self, *args): for arg in args: self.data.append(arg) def move_to(self, x, y): self.add_data('M', x, y) def move_to_relative(self, x, y): self.add_data('m', x, y) def line_to(self, x, y): self.add_data('L', x, y) def line_to_relative(self, x, y): self.add_data('l', x, y) def horizontal_line_to(self, x,): self.add_data('H', x) def horizontal_line_to_relative(self, x): self.add_data('h', x) def vertical_line_to(self, y): self.add_data('V', y) def vertical_line_to_relative(self, y): self.add_data('v', y) def curve_to(self, x1, y1, x2, y2, x, y): self.add_data('C', x1, y1, x2, y2, x, y) def curve_to_relative(self, x1, y1, x2, y2, x, y): self.add_data('c', x1, y1, x2, y2, x, y) def smooth_curve_to(self, x2, y2, x, y): self.add_data('S', x2, y2, x, y) def smooth_curve_to_relative(self, x2, y2, x, y): self.add_data('s', x2, y2, x, y) def quadratic_bezier_curve_to(self, x1, y1, x, y): self.add_data('Q', x1, y1, x, y) def quadratic_bezier_curve_to_relative(self, x1, y1, x, y): self.add_data('q', x1, y1, x, y) def smooth_quadratic_bezier_curve_to(self, x, y): self.add_data('T', x, y) def smooth_quadratic_bezier_curve_to_relative(self, x, y): self.add_data('t', x, y) def arc(self, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y): """ Draws an elliptical arc from the current point to the point x,y. rx and ry are the elliptical radius in x and y direction. The x-rotation determines how much the elliptical arc is to be rotated around the x-axis. The large_arc_flag, along with the sweep_flag, determine which of the 2 possible arcs to draw. The sweep-flag determines the direction to draw the arc in. 0 means end at x, y. """ self.add_data('A', rx, ry, x_axis_rotation, flag(large_arc_flag), flag(sweep_flag), x, y) def arc_relative(self, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y): self.add_data('a', rx, ry, x_axis_rotation, flag(large_arc_flag), flag(sweep_flag), x, y) def close(self): self.add_data('z') def render(self, **attributes): attributes['d'] = ' '.join(map(str, self.data)) return SVGElement.render(self, **attributes) def flag(x): if x: return 1 else: return 0