# Reconstructs assets/SingeText.jpeg as assets/SingeText.svg: thresholds the logotype, traces it # with potrace into Bezier outlines, and writes a tightly cropped SVG with a transparent background. # Usage: python3 util/traceText.py assets/SingeText.jpeg assets/SingeText.svg [potrace] [check.svg] # Needs Pillow and potrace (https://potrace.sourceforge.net). check.svg keeps the full page so it # can be rendered over the original for comparison. import os import re import subprocess import sys import tempfile from PIL import Image, ImageFilter src = sys.argv[1] out = sys.argv[2] potrace = sys.argv[3] if len(sys.argv) > 3 else 'potrace' check = sys.argv[4] if len(sys.argv) > 4 else None # Trace at 4x so the anti-aliased edges of the JPEG turn into smooth curves rather than steps. scale = 4 BLUR = 3.5 grey = Image.open(src).convert('L') w, h = grey.size big = grey.resize((w * scale, h * scale), Image.LANCZOS) # A light blur removes JPEG ringing along the edges, which would otherwise trace as tiny wobbles. big = big.filter(ImageFilter.GaussianBlur(BLUR)) mono = big.point(lambda v: 255 if v >= 128 else 0).convert('1') work = tempfile.mkdtemp() pbm = os.path.join(work, 'text.pbm') mono.save(pbm) def trace(tight): args = [potrace, pbm, '-s', '--flat', '-a', '1.0', '-O', '0.2', '-u', '1', '-t', str(4 * scale * scale), '-o', '-'] if tight: args.append('--tight') svg = subprocess.run(args, check=True, capture_output=True, text=True).stdout width = float(re.search(r'width="([\d.]+)pt"', svg).group(1)) height = float(re.search(r'height="([\d.]+)pt"', svg).group(1)) transform = re.search(r'transform="([^"]+)"', svg).group(1) path = re.search(r']*\bd="([^"]+)"', svg, re.S).group(1) path = re.sub(r'\s+', ' ', path).strip() return width, height, transform, path def write(name, tight): width, height, transform, path = trace(tight) lines = ['', '' % (width / scale, height / scale, width / scale, height / scale), ' ', ' Singe', ' ' % (1.0 / scale, transform), ' ' % path, ' ', ''] open(name, 'w').write('\n'.join(lines) + '\n') print('%s: %.0fx%.0f, %d bytes' % (name, width / scale, height / scale, os.path.getsize(name))) write(out, True) if check: write(check, False)