generate_font.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # Font generation script from FontCustom
  2. # https://github.com/FontCustom/fontcustom/
  3. # http://fontcustom.com/
  4. import fontforge
  5. import os
  6. import md5
  7. import subprocess
  8. import tempfile
  9. import json
  10. import copy
  11. SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
  12. INPUT_SVG_DIR = os.path.join(SCRIPT_PATH, '..', '..', 'src')
  13. OUTPUT_FONT_DIR = os.path.join(SCRIPT_PATH, '..', '..', 'fonts')
  14. MANIFEST_PATH = os.path.join(SCRIPT_PATH, '..', 'manifest.json')
  15. BUILD_DATA_PATH = os.path.join(SCRIPT_PATH, '..', 'build_data.json')
  16. AUTO_WIDTH = True
  17. KERNING = 15
  18. cp = 0xf100
  19. m = md5.new()
  20. f = fontforge.font()
  21. f.encoding = 'UnicodeFull'
  22. f.design_size = 16
  23. f.em = 512
  24. f.ascent = 448
  25. f.descent = 64
  26. manifest_file = open(MANIFEST_PATH, 'r')
  27. manifest_data = json.loads(manifest_file.read())
  28. manifest_file.close()
  29. print "Load Manifest, Icons: %s" % ( len(manifest_data['icons']) )
  30. build_data = copy.deepcopy(manifest_data)
  31. build_data['icons'] = []
  32. font_name = manifest_data['name']
  33. m.update(font_name + ';')
  34. m.update(manifest_data['prefix'] + ';')
  35. for dirname, dirnames, filenames in os.walk(INPUT_SVG_DIR):
  36. for filename in filenames:
  37. name, ext = os.path.splitext(filename)
  38. filePath = os.path.join(dirname, filename)
  39. size = os.path.getsize(filePath)
  40. if ext in ['.svg', '.eps']:
  41. # see if this file is already in the manifest
  42. chr_code = None
  43. for ionicon in manifest_data['icons']:
  44. if ionicon['name'] == name:
  45. chr_code = ionicon['code']
  46. break
  47. if chr_code is None:
  48. # this is a new src icon
  49. print 'New Icon: \n - %s' % (name)
  50. while True:
  51. chr_code = '0x%x' % (cp)
  52. already_exists = False
  53. for ionicon in manifest_data['icons']:
  54. if ionicon.get('code') == chr_code:
  55. already_exists = True
  56. cp += 1
  57. chr_code = '0x%x' % (cp)
  58. continue
  59. if not already_exists:
  60. break
  61. print ' - %s' % chr_code
  62. manifest_data['icons'].append({
  63. 'name': name,
  64. 'code': chr_code
  65. })
  66. build_data['icons'].append({
  67. 'name': name,
  68. 'code': chr_code
  69. })
  70. if ext in ['.svg']:
  71. # hack removal of <switch> </switch> tags
  72. svgfile = open(filePath, 'r+')
  73. tmpsvgfile = tempfile.NamedTemporaryFile(suffix=ext, delete=False)
  74. svgtext = svgfile.read()
  75. svgfile.seek(0)
  76. # replace the <switch> </switch> tags with 'nothing'
  77. svgtext = svgtext.replace('<switch>', '')
  78. svgtext = svgtext.replace('</switch>', '')
  79. tmpsvgfile.file.write(svgtext)
  80. svgfile.close()
  81. tmpsvgfile.file.close()
  82. filePath = tmpsvgfile.name
  83. # end hack
  84. m.update(name + str(size) + ';')
  85. glyph = f.createChar( int(chr_code, 16) )
  86. glyph.importOutlines(filePath)
  87. # if we created a temporary file, let's clean it up
  88. if tmpsvgfile:
  89. os.unlink(tmpsvgfile.name)
  90. # set glyph size explicitly or automatically depending on autowidth
  91. if AUTO_WIDTH:
  92. glyph.left_side_bearing = glyph.right_side_bearing = 0
  93. glyph.round()
  94. # resize glyphs if autowidth is enabled
  95. if AUTO_WIDTH:
  96. f.autoWidth(0, 0, 512)
  97. fontfile = '%s/ionicons' % (OUTPUT_FONT_DIR)
  98. build_hash = m.hexdigest()
  99. if build_hash == manifest_data.get('build_hash'):
  100. print "Source files unchanged, did not rebuild fonts"
  101. else:
  102. manifest_data['build_hash'] = build_hash
  103. f.fontname = font_name
  104. f.familyname = font_name
  105. f.fullname = font_name
  106. f.generate(fontfile + '.ttf')
  107. f.generate(fontfile + '.svg')
  108. # Fix SVG header for webkit
  109. # from: https://github.com/fontello/font-builder/blob/master/bin/fontconvert.py
  110. svgfile = open(fontfile + '.svg', 'r+')
  111. svgtext = svgfile.read()
  112. svgfile.seek(0)
  113. svgfile.write(svgtext.replace('''<svg>''', '''<svg xmlns="http://www.w3.org/2000/svg">'''))
  114. svgfile.close()
  115. scriptPath = os.path.dirname(os.path.realpath(__file__))
  116. try:
  117. subprocess.Popen([scriptPath + '/sfnt2woff', fontfile + '.ttf'], stdout=subprocess.PIPE)
  118. except OSError:
  119. # If the local version of sfnt2woff fails (i.e., on Linux), try to use the
  120. # global version. This allows us to avoid forcing OS X users to compile
  121. # sfnt2woff from source, simplifying install.
  122. subprocess.call(['sfnt2woff', fontfile + '.ttf'])
  123. # eotlitetool.py script to generate IE7-compatible .eot fonts
  124. subprocess.call('python ' + scriptPath + '/eotlitetool.py ' + fontfile + '.ttf -o ' + fontfile + '.eot', shell=True)
  125. subprocess.call('mv ' + fontfile + '.eotlite ' + fontfile + '.eot', shell=True)
  126. # Hint the TTF file
  127. subprocess.call('ttfautohint -s -f -n ' + fontfile + '.ttf ' + fontfile + '-hinted.ttf > /dev/null 2>&1 && mv ' + fontfile + '-hinted.ttf ' + fontfile + '.ttf', shell=True)
  128. manifest_data['icons'] = sorted(manifest_data['icons'], key=lambda k: k['name'])
  129. build_data['icons'] = sorted(build_data['icons'], key=lambda k: k['name'])
  130. print "Save Manifest, Icons: %s" % ( len(manifest_data['icons']) )
  131. f = open(MANIFEST_PATH, 'w')
  132. f.write( json.dumps(manifest_data, indent=2, separators=(',', ': ')) )
  133. f.close()
  134. print "Save Build, Icons: %s" % ( len(build_data['icons']) )
  135. f = open(BUILD_DATA_PATH, 'w')
  136. f.write( json.dumps(build_data, indent=2, separators=(',', ': ')) )
  137. f.close()