Add font-reduce.py and related post.
authorW. Trevor King <wking@drexel.edu>
Thu, 9 Feb 2012 06:28:02 +0000 (01:28 -0500)
committerW. Trevor King <wking@drexel.edu>
Thu, 9 Feb 2012 06:28:02 +0000 (01:28 -0500)
posts/font-reduce.mdwn [new file with mode: 0644]
posts/font-reduce/font-reduce.py [new file with mode: 0755]

diff --git a/posts/font-reduce.mdwn b/posts/font-reduce.mdwn
new file mode 100644 (file)
index 0000000..fa2b073
--- /dev/null
@@ -0,0 +1,14 @@
+I've been playing with [[fonts]] recently, and wrote
+[[font-reduce.py]] to generate compressed subsets of free fonts to
+[WOFF][] for use on my website.  It uses [FontForge][]'s [[Python]]
+interface, and has a few options to customize the generated [WOFF][]
+metadata.
+
+If this doesn't quite scratch your itch, you may also want to consider
+the similar [subset.py][] from the Google font directory.  They do a
+better job creating custom subsets.  I do a better job preserving and
+generating metadata.
+
+[WOFF]: http://en.wikipedia.org/wiki/Web_Open_Font_Format
+[FontForge]: http://fontforge.sourceforge.net/
+[subset.py]: http://code.google.com/p/googlefontdirectory/source/browse/tools/subset/subset.py
diff --git a/posts/font-reduce/font-reduce.py b/posts/font-reduce/font-reduce.py
new file mode 100755 (executable)
index 0000000..03c8aa7
--- /dev/null
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2012 W. Trevor King <wking@drexel.edu>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program.  If not, see
+# <http://www.gnu.org/licenses/>.
+
+"""Extract the printable ASCII range of Unicode characters to WOFF.
+
+This is useful for generating smaller font packages for use with CSS's
+`@font-face`.
+"""
+
+import os.path as _os_path
+
+import fontforge as _fontforge
+
+
+__version__ = '0.1'
+
+
+def convert(input_file, output_file=None, **kwargs):
+    if output_file is None:
+        base,ext = _os_path.splitext(input_file)
+        output_file = '{}-reduced.woff'.format(base)
+    i = _fontforge.open(input_file)
+    i.selection.select(("ranges",), 0x20, 0x7e)
+    i.copy()
+    o = _fontforge.font()
+    o.selection.select(("ranges",), 0x20, 0x7e)
+    o.paste()
+    for attr in ['comment', 'copyright', 'encoding', 'familyname',
+                 'fontname', 'fullname', 'sfntRevision', 'sfnt_names',
+                 'userdata', 'version', 'woffMetadata']:
+        value = getattr(i, attr)
+        if attr in ['fontname', 'fullname'] and value:
+            value = 'Reduced{}'.format(value)
+        setattr(o, attr, value)
+    fontlog = ('Reduced to Unicode characters 0x20 through 0x7e by '
+               'font-reduce.py version {}, '
+               '`http://blog.tremily.us/posts/font-reduce/`.'
+               ).format(__version__)
+    if i.fontlog:
+        fontlog = '{}\n\n{}'.format(i.fontlog.rstrip('\n'), fontlog)
+    o.fontlog = fontlog
+
+    if not o.woffMetadata:
+        o.woffMetadata = generate_woff_metadata(o, **kwargs)
+    print o.woffMetadata
+    print [a for a in dir(o) if 'lic' in a.lower()]
+
+    # the `PfEd-comments` flag is required for Fontforge to save
+    # `.comment` and `.fontlog`.
+    o.generate(output_file, flags=('PfEd-comments',))
+
+
+def generate_woff_metadata(font, license=None, source=None):
+    metadata = [
+        '<?xml version="1.0" encoding="UTF-8"?>',
+        '<metadata version="1.0">',
+        '  <uniqueid id="us.tremily.font-reduce.{}.{}.{}/>'.format(
+            __version__, font.fontname, font.version),
+        '  <description>',
+        '    <text lang="en">{}</text>'.format(font.fontlog),
+        '  </description>',
+        ]
+    if source:
+        metadata.extend([
+                '  <credits>',
+                '    <credit',
+                '        name="Original source for this font"',
+                '        url="{}"'.format(source),
+                '  </credits>',
+                ])
+    if license:
+        metadata.extend([
+                '  <license>',
+                '    <text lang="en">{}</text>'.format(license),
+                '  </license>',
+                ])
+    if font.copyright:
+        metadata.extend([
+                '  <copyright>',
+                '    <text lang="en">{}</text>'.format(font.copyright),
+                '  </copyright>',
+                ])
+    metadata.extend([
+            '</metadata>',
+            '',  # for trailing endline
+            ])
+    return '\n'.join(metadata)
+
+
+if __name__ == '__main__':
+    import argparse
+
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument(
+        '-l', '--license', help='override font license text')
+    parser.add_argument(
+        '-s', '--source', help='override font source URL')
+    parser.add_argument(
+        'font', nargs='+', help='path to source font')
+
+    args = parser.parse_args()
+
+    for font in args.font:
+        convert(font, license=args.license, source=args.source)