Skip to content

Instantly share code, notes, and snippets.

@HalCanary
Last active June 7, 2023 12:59
Show Gist options
  • Save HalCanary/f76a7d095e1865ac6210bba527d59fce to your computer and use it in GitHub Desktop.
Save HalCanary/f76a7d095e1865ac6210bba527d59fce to your computer and use it in GitHub Desktop.
makesvg.py
#! /usr/bin/env python3
'''
Making SVGs by hand is a pain. This is a better way:
It's functional, rather than using the XML "markup language" directly.
No closing tags, just balanced parentheses!
#xml #svg #python #python3 #graphicsprogramming
https://twitter.com/halcanary/status/1458802118136111107
Updated to use `xml.dom.minidom` instead of `lxml`.
'''
import copy
import math
import os
import subprocess
import shutil
import sys
import tempfile
import numbers
import xml.dom.minidom
################################################################################
def create_svg(width, height):
def stringy(v):
return '%g' % v if isinstance(v,numbers.Number) else str(v)
class ElementFactory:
def __init__(self, doc):
self.doc = doc
def make(self, name, *children, **kws):
e = self.doc.createElement(name)
for k, v in kws.items():
e.setAttribute(k, stringy(v))
for c in children:
e.appendChild(copy.copy(c))
return e
def defs(self, **kws):
d = self.doc.createElement('def')
for k, v in kws.items():
v.setAttribute('id', k)
d.appendChild(v)
return d
def use(self, href, **kws):
use = self.doc.createElement('use')
use.setAttribute('xlink:href', href)
for k, v in kws.items():
use.setAttribute(k, stringy(v))
return use
def __getattr__(self, name):
def fn(*children, **kws):
return self.make(name, *children, **kws)
return fn
def append(self, *children):
for c in children:
self.doc.documentElement.appendChild(c)
def write(self, o):
self.doc.writexml(o, addindent=' ', newl='\n', encoding='UTF-8')
dom_impl = xml.dom.minidom.getDOMImplementation()
doc = dom_impl.createDocument('http://www.w3.org/2000/svg', 'svg',
dom_impl.createDocumentType('svg', "-//W3C//DTD SVG 1.1//EN",
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"))
doc.documentElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg')
doc.documentElement.setAttribute('xmlns:xlink',
'http://www.w3.org/1999/xlink')
doc.documentElement.setAttribute('width', '%d'%width)
doc.documentElement.setAttribute('height', '%d'%height)
return ElementFactory(doc)
def closed_path(pts):
return open_path(pts) + 'Z'
def open_path(pts):
return ''.join(('%s%g,%g' % ('L' if i else 'M', x, y) for i, (x, y) in enumerate(pts)))
def cartesian(radius, theta, x=0, y=0):
rad = math.radians(theta)
return (radius * math.cos(rad) + x, radius * math.sin(rad) + y)
################################################################################
def example(height, output):
E = create_svg(height * 1.9, height)
E.append(
E.defs(
star=E.path(fill="#FFF", d=closed_path(
(cartesian(120, 180 * (0.8 * i - 0.5)) for i in range(5)))),
stripe=E.rect(id='stripe', fill='#B22234', width=7410, height=300)),
E.g(E.rect(width=7410, height=3900, fill='#FFF'),
*[E.use('#stripe', y=(i * 300)) for i in range(0, 13, 2)],
E.rect(width=2964, height=2100, fill='#3C3B6E'),
*[E.use('#star', x=(x * 247), y=(y * 210))
for x in range(1, 12) for y in range(1, 10) if (x + y) % 2 == 0],
transform=('scale(%g)' % (height / 3900))))
E.write(output)
if __name__ == '__main__':
example(1024, sys.stdout)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment