#!/usr/bin/env python3 def sanitize(s): return s.replace('&', '&')\ .replace('>', '>')\ .replace('<', '<')\ .replace("'", ''')\ .replace('"', '"') class Tag: def __init__(self, name, attrs={}, **kwattrs): self.name = name self.attrs = attrs self.attrs.update(kwattrs) self.children = [] def append(self, *tags): self.children += tags return self def __getitem__(self, n): return self.children[n] def __setitem__(self, n, tag): self.children[n] = tag def __str__(self): s = '<' + self.name for name, value in self.attrs.items(): s += ' ' + name if value is not None: s += '="' + sanitize(str(value)) + '"' if len(self.children) > 0: s += '>' for child in self.children: if isinstance(child, str): s += sanitize(child) else: s += str(child) s += '' else: s += ' />' return s def __repr__(self): return str(self) def attr(self, **kwargs): self.attrs.update(kwargs) return self class Path(Tag): def __init__(self, **kwargs): Tag.__init__(self, 'path', attrs=kwargs) self.paths = [] def __str__(self): self.attrs['d'] = ' '.join(self.paths) return Tag.__str__(self) def move(self, x, y): self.paths.append('M ' + str(x) + ' ' + str(y)) return self def line(self, x, y): self.paths.append('L ' + str(x) + ' ' + str(y)) return self def x(self, x): self.paths.append('H ' + str(x)) return self def y(self, y): self.paths.append('V ' + str(y)) return self def curve(self, x1, y1, x2, y2, x, y): self.paths.append('C ' + str(x1) + ' ' + str(y1) + ', ' + str(x2) + ' ' + str(y2) + ', ' + str(x) + ' ' + str(y)) return self def arc(self, xrad, yrad, arcrotxaxis, biggeroption, sweepflag, nextx, nexty): self.paths.append('A ' + str(xrad) + ' ' + str(yrad) + ', ' + str(arcrotxaxis) + ', ' + str(biggeroption) + ', ' + str(sweepflag) + ', ' + str(nextx) + ' ' + str(nexty)) return self def close(self): self.paths.append('Z') return self print('') def body(cx, cy, w, h, c=0.03): topleft = (cx - w/2, cy - h/2) topright = (cx + w/2, cy - h/2) bottomleft = (cx - w/2, cy + h/2) bottomright = (cx + w/2, cy + h/2) return Path().move(*bottomleft)\ .arc(w*c, h/2, 0, 0, 1, *topleft)\ .arc(w/2, h*c, 0, 1, 0, *topright)\ .arc(w*c, h/2, 0, 0, 1, *bottomright)\ .close() def eye(cx, cy, w, h, lx=0, ly=0, c=0.01): def outer(cx, cy, w, h, c=0.01): topleft = (cx - w/2, cy - h/2) topright = (cx + w/2, cy - h/2) bottomleft = (cx - w/2, cy + h/2) bottomright = (cx + w/2, cy + h/2) return Path().move(*bottomleft)\ .arc(w*c, h/2, 0, 0, 1, *topleft)\ .arc(w/2, h*c, 0, 0, 1, *topright)\ .arc(w*c, h/2, 0, 0, 1, *bottomright)\ .arc(w/2, h*c, 0, 1, 0, *bottomleft) def inner(cx, cy, w, h, c=0.01): topleft = (cx - w/2, cy - h/2) topright = (cx + w/2, cy - h/2) bottomleft = (cx - w/2, cy + h/2) bottomright = (cx + w/2, cy + h/2) return Path().move(*bottomleft)\ .arc(w*c, h/2, 0, 0, 1, *topleft)\ .arc(w/2, h*c, 0, 0, 1, *topright)\ .arc(w*c, h/2, 0, 0, 1, *bottomright)\ .arc(w/2, h*c, 0, 0, 1, *bottomleft) icx = cx + lx * w/4 icy = cy + ly * h/4 return Tag('g').append( outer(cx, cy, w, h, c).attr(fill='white'), inner(icx, icy, w/2.2, h/2.2, c).attr(fill='black') ).attr(stroke='black') def eyes(cx, cy, rad, w, h): return [ eye(cx - rad, cy, w, h, 1, -1), eye(cx + rad, cy, w, h, -1, -1) ] def mouth(cx, cy, w, h): left = (cx - w/2, cy) right = (cx + w/2, cy) return Path().move(*left).arc(w/2, h, 0, 0, 0, *right).close().attr(fill='white', stroke='black') svg = Tag('svg', {'xmlns': 'http://www.w3.org/2000/svg', 'version': '1.1'}) svg.append(body(200, 200, 180, 100).attr(fill='white', stroke='black')) svg.append(*eyes(200, 185, 64, 50, 56)) svg.append(mouth(200, 210, 55, 35)) print(str(svg))