#!/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:])
