#!/usr/bin/python # # Copyright 2013 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # A simple script to convert svg files (generated by Illustrator plugin) # to sfd, from which Fontforge can then create a font file. # # Contributors: Raph Levien (raph@google.com) import sys import os.path import glob import xml.dom.minidom import re import math lastglyphnum = 0 char_num = 0x40 font_name = 'Untitled' header_printed = False def num_args_cmd(cmd): if cmd.upper() == 'C': return 6 elif cmd.upper() in 'HV': return 1 elif cmd.upper() == 'S': return 4 elif cmd == 'z': return 0 return 2 def print_one_cmd(cmd, args): scale = 40 yoff = 720 result = [] for i in range(len(args)): if i & 1: result.append('%f' % (yoff - scale * args[i])) else: result.append('%f' % (scale * args[i])) result.append(cmd) result.append('0') # TODO: should mark corner points print ' '.join(result) def apply_rel_xy(xy, args): x0, y0 = xy result = [] for i in range(0, len(args), 2): x = x0 + args[i] result.append(x) y = y0 + args[i + 1] result.append(y) return result def path_to_sfd(path): # convert svg path syntax into sfd # written for conciseness, not efficiency x0, y0 = 0, 0 fre = re.compile(r'(\-?[0-9\.]+)\s*,?\s*') while path.strip() != '': path = path.strip() if path[0].isalpha(): cmd = path[0] path = path[1:].lstrip() args = [] for i in range(num_args_cmd(cmd)): m = fre.match(path) if m is None: print 'no float match:', path args.append(float(m.group(1))) path = path[m.end():] #print cmd, args if cmd.upper() == 'M': if cmd.islower(): (x, y), args = apply_rel_xy([x, y], args) x0, y0 = args print_one_cmd('m', args) x, y = args[-2:] if cmd == 'm': cmd = 'l' elif cmd == 'M': cmd = 'L' elif cmd.upper() in 'CLVHS': if cmd == 'H': args = args + [y] cmd = 'L' elif cmd == 'h': args = args + [0] cmd = 'l' if cmd == 'V': args = [x] + args cmd = 'L' elif cmd == 'v': args = [0] + args cmd = 'l' if cmd.islower(): args = apply_rel_xy([x, y], args) if cmd.upper() == 'S': # smooth curveto; reflect args = [2 * x - xs, 2 * y - ys] + args cmd = 'c' print_one_cmd(cmd.lower(), args) x, y = args[-2:] if len(args) > 2: xs, ys = args[-4:-2] elif cmd.upper() == 'Z': if x != x0 or y != y0: print_one_cmd('l', [x0, y0]) def circle_to_sfd(cx, cy, r): k = 4 * (math.sqrt(2) - 1) / 3 print_one_cmd('m', [cx, cy - r]) print_one_cmd('c', [cx + k * r, cy - r, cx + r, cy - k * r, cx + r, cy]) print_one_cmd('c', [cx + r, cy + k * r, cx + k * r, cy + r, cx, cy + r]) print_one_cmd('c', [cx - k * r, cy + r, cx - r, cy + k * r, cx - r, cy]) print_one_cmd('c', [cx - r, cy - k * r, cx - k * r, cy - r, cx, cy - r]) def conv_svg(fn, char, glyphnum = None): global lastglyphnum global header_printed if not header_printed: print_header() if glyphnum == None: glyphnum = lastglyphnum + 1 lastglyphnum = glyphnum print 'StartChar:', os.path.basename(fn)[:-4] print 'Encoding: %d %d %d' % (char, glyphnum, char) print 'Width: %d' % (21 * 40) print 'Flags: W' print 'LayerCount: 2' print 'Fore' print 'SplineSet' doc = xml.dom.minidom.parse(fn) # TODO: reverse paths if fill color is white-ish (this is more code, # and in the meantime, we'll rely on correct path direction in FF) for path in doc.getElementsByTagName('path'): path_to_sfd(path.getAttribute('d')) for polygon in doc.getElementsByTagName('polygon'): path_to_sfd('M' + polygon.getAttribute('points') + 'z') for circle in doc.getElementsByTagName('circle'): cx = float(circle.getAttribute('cx')) cy = float(circle.getAttribute('cy')) r = float(circle.getAttribute('r')) circle_to_sfd(cx, cy, r) print 'EndSplineSet' print 'EndChar' def print_header(): global header_printed print '''SplineFontDB: 3.0 FontName: %s FullName: %s FamilyName: %s''' % (font_name, font_name, font_name) print '''Weight: Medium Copyright: Copyright (C) 2011 Google Inc. Version: 001.000 UnderlinePosition: -120 UnderlineWidth: 40 Ascent: 800 Descent: 200 LayerCount: 2 Layer: 0 0 "Back" 1 Layer: 1 0 "Fore" 0 Encoding: unicode OS2TypoAscent: 800 OS2TypeAOffset: 0 OS2TypoDescent: -200 OS2TypoDOffset: 0 OS2WinAscent: 800 OS2WinAOffset: 0 OS2WinDescent: 200 OS2WinDOffset: 0 HheadAscent: 800 HheadAOffset: 0 HheadDescent: 200 HheadDOffset: 0 BeginChars: 57600 57600 ''' header_printed = True def print_footer(): print '''EndChars EndSplineFont''' def parse_int(x): if x.startswith('0x'): return int(x[2:], 16) else: return int(x) def run_file(fn): global char_num global font_name directory = '' for l in file(fn).xreadlines(): if l.startswith('#'): continue s = l.strip().split() if len(s) == 0: continue if s[0] == 'dir': directory = s[1] elif s[0] == 'fontname': font_name = s[1] elif s[0] == 'unicode': char_num = parse_int(s[1]) elif s[0] == 'icon': icon_fn = s[1] if not icon_fn.endswith('.svg'): icon_fn += '.svg' if len(s) > 2: char_num = parse_int(s[2]) conv_svg(os.path.join(directory, icon_fn), char_num) char_num += 1 def main(args): global char_num for arg in args: if os.path.isdir(arg): for fn in glob.glob(arg + '/*.svg'): conv_svg(fn, char_num) char_num += 1 elif arg.endswith('.svg'): conv_svg(arg, char_num) char_num += 1 else: run_file(arg) print_footer() if __name__ == '__main__': main(sys.argv[1:])