Last active
June 7, 2023 12:59
-
-
Save HalCanary/f76a7d095e1865ac6210bba527d59fce to your computer and use it in GitHub Desktop.
makesvg.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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