Permalink
Browse files

added transform/remap tools

  • Loading branch information...
1 parent 5673942 commit e5628a1a0326f0968de0150026da5a782ae20e39 @puzrin puzrin committed Apr 8, 2012
Showing with 432 additions and 8 deletions.
  1. +137 −0 bin/font_mkconfig.py
  2. +119 −0 bin/font_remap.py
  3. +164 −0 bin/font_transform.py
  4. +12 −8 config.example.yml
View
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+
+import argparse
+import yaml
+import fontforge
+
+parser = argparse.ArgumentParser(description='Font config generation tool')
+parser.add_argument('-i', '--src_font', type=str, required=True,
+ help='Input font')
+parser.add_argument('-c', '--config', type=str, required=True,
+ help='Output config')
+
+def get_attrs(font, attrs):
+ result = {}
+ for a in attrs:
+ result[a] = getattr(font, a)
+ return result
+
+args = parser.parse_args()
+
+font = fontforge.open(args.src_font)
+
+# add comment
+config = """---
+# This is configuration file for font builder and other support scripts.
+# Format is descriped below.
+#
+#
+# css-prefix: "icon-" # prefix for css-generated classes
+# demo-columns: 4 # used for html demo page generation
+#
+# font: # all vars from here will be used as font
+# # params in fontforge
+# # http://fontforge.sourceforge.net/python.html
+# version: "1.0"
+#
+# # use !!!small!!! letters a-z, or Opera will fail under OS X
+# # fontname will be also used as file name.
+# fontname: myfont
+#
+# fullname: MyFont
+# familyname: Myfont
+#
+# copyright: Copyright (C) 2012 by xxxxxxxx
+#
+# ascent: 1638
+# descent: 410
+# weight: Medium
+#
+#
+#
+# # Optional. You can apply global resize + offset to all font glyphs.
+# # Param values ar 0..1, where 1 = 100%.
+# #
+# transform:
+# baseline: 0.2 # baseline for rescale. Default value calculated
+# # from ascent/decsent
+# rescale: 0.68 # rescale glyphs and center around baseline
+# offset: -0.1 # shift up/down
+#
+# glyphs:
+# - glyph1_file: # file name, without extention
+# from: 0xNNN # Symbol code 0x - hex, original
+# code: 0xNNN # Symbol code 0x - hex, remapped
+# css: icon-gpyph1-name # For generated CSS
+# search: [word1, word2] # Search aliases (array). CSS name will be
+# # included automatically
+#
+# transform: # personal glyph transformation.
+# rescale_rel: 0.9 # *_rel - applyed after global.
+# offset: 0.2 # without *_rel - override global
+#
+################################################################################
+#
+# Mapping rules:
+#
+# 1. Downshift 1Fxxx -> Fxxx, because 1Fxxx codes not shown in Chrome/Opera
+#
+"""
+
+font_attrs = [
+ "version",
+ "fontname",
+ "fullname",
+ "familyname",
+ "copyright",
+ "ascent",
+ "descent",
+ "weight"
+]
+
+# add beginning part
+config += """
+
+css-prefix: "icon-"
+demo-columns: 4
+
+transform:
+ rescale: 1.0
+ offset: 0.0
+
+font:
+ version: "{version}"
+
+ # use !!!small!!! letters a-z, or Opera will fail under OS X
+ # fontname will be also used as file name.
+ fontname: "{fontname}"
+
+ fullname: "{fullname}"
+ familyname: "{familyname}"
+
+ copyright: "{copyright}"
+
+ ascent: {ascent}
+ descent: {descent}
+ weight: "{weight}"
+
+
+""".format(**get_attrs(font, font_attrs))
+
+# add glyphs part
+config += """glyphs:
+"""
+
+for i, glyph in enumerate(font.glyphs()):
+ if glyph.unicode == -1:
+ continue
+
+ code = '0x%04x' % glyph.unicode
+
+ config += """
+ - glyph{i}:
+ code: {code}
+""".format(i=i, code=code)
+
+
+open(args.config, "w").write(config)
View
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+
+import sys
+import argparse
+import yaml
+import fontforge
+
+
+error = sys.stderr.write
+
+
+# returns dict representing duplicate values of seq
+# in seq = [1,1,2,3,3,3,3,4,5], out dict {1: 2, 3: 4}
+def get_dups(seq):
+ count = {}
+ for s in seq:
+ count[s] = count.get(s, 0) + 1
+ dups = dict((k, v) for k, v in count.iteritems() if v > 1)
+ return dups
+
+
+# returns list of tuples:
+# [(from_code1, to_code1), (from_code2, to_code2), ...]
+def get_remap_config(config):
+ def get_remap_item(glyph):
+ name, glyph = glyph.items()[0]
+ return (glyph.get('from', glyph['code']), glyph['code'])
+ return [get_remap_item(glyph) for glyph in config['glyphs']]
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Font remap tool')
+ parser.add_argument('-c', '--config', type=str, required=True,
+ help='Config example: ../config.yml')
+ parser.add_argument('-i', '--src_font', type=str, required=True,
+ help='Input font')
+ parser.add_argument('-o', '--dst_font', type=str, required=True,
+ help='Output font')
+
+ args = parser.parse_args()
+
+ try:
+ config = yaml.load(open(args.config, 'r'))
+ except IOError as (errno, strerror):
+ error("Cannot open %s: %s\n" % (args.config, strerror))
+ sys.exit(1)
+ except yaml.YAMLError, e:
+ if hasattr(e, 'problem_mark'):
+ mark = e.problem_mark
+ error("YAML parser error in file %s at line %d, col %d\n" %
+ (args.config, mark.line + 1, mark.column + 1))
+ else:
+ error("YAML parser error in file %s: %s\n" % (args.config, e))
+ sys.exit(1)
+
+ remap_config = get_remap_config(config)
+
+ from_codes, to_codes = zip(*remap_config)
+
+ # validate config: 'from:' codes
+ dups = get_dups(from_codes)
+ if len(dups) > 0:
+ error("Error in file %s: glyph codes aren't unique:\n" % args.config)
+ for k in sorted(dups.keys()):
+ error("Duplicate 'from:' 0x%04x\n" % k)
+ sys.exit(1)
+
+ # validate config: 'code:' codes
+ dups = get_dups(to_codes)
+ if len(dups) > 0:
+ error("Error in file %s: glyph codes aren't unique:\n" % args.config)
+ for k in sorted(dups.keys()):
+ error("Duplicate 'code:' 0x%04x\n" % k)
+ sys.exit(1)
+
+ try:
+ font = fontforge.open(args.src_font)
+ except:
+ sys.exit(1)
+
+ # tmp font for cut()/paste()
+ tmp_font = fontforge.font()
+ tmp_font.encoding = 'UnicodeFull'
+
+ # set font encoding so we can select any unicode code point
+ font.encoding = 'UnicodeFull'
+
+ for from_code, to_code in remap_config:
+ try:
+ font[from_code]
+ except TypeError:
+ error("Warning: no such glyph in the source font (code=0x%04x)\n" %
+ from_code)
+ continue
+
+ if from_code == to_code:
+ continue
+
+ font.selection.select(("unicode",), from_code)
+ font.cut()
+ tmp_font.selection.select(("unicode",), to_code)
+ tmp_font.paste()
+
+ for from_code, to_code in remap_config:
+ if from_code == to_code:
+ continue
+
+ tmp_font.selection.select(("unicode",), to_code)
+ tmp_font.cut()
+ font.selection.select(("unicode",), to_code)
+ font.paste()
+
+ try:
+ font.generate(args.dst_font)
+ except:
+ error("Cannot write to file %s\n" % args.dst_font)
+ sys.exit(1)
+
+ sys.exit(0)
Oops, something went wrong.

0 comments on commit e5628a1

Please sign in to comment.