Commits

Barry Schwartz committed 2b5d060

Support for an font-list page and so forth.

  • Participants
  • Parent commits 436c76f

Comments (0)

Files changed (8)

 AddHandler fcgid-script .fcgi
-
 RewriteEngine On
-
 RewriteBase /
-
 RewriteRule ^$ index.html [QSA]
 RewriteRule ^([^.]+)$ $1.html [QSA]
 RewriteCond %{REQUEST_FILENAME} !-f

File dispatch.fcgi

 
 #--------------------------------------------------------------------------
 
-import os
-import re
-import stat
+import fonts_config
+import fonts_data
+import serve_font
 
-from email.utils import formatdate
+from error_respond import error_respond
 from flup.server.fcgi_fork import WSGIServer
+#from flup.server.fcgi import WSGIServer
 
 #--------------------------------------------------------------------------
 
-font_server_directory = '/home2/crudfact/wsgi_apps/fontserver/fonts'
+def dispatch(environ, start_response):
 
-woff_re = re.compile('Firefox/[3-9]|Konqueror/[4-9]')
-eot_re = re.compile('\sMSIE\s')
-gzip_re = re.compile('(^\s*gzip\s*$|\s*gzip\s*,|,\s*gzip\s*$|,\s*gzip\s*,)')
-font_name_re = re.compile('^[0-9a-zA-Z_-]+$')
+    if 'PATH_INFO' not in environ:
+        return error_respond(start_response, 'Font service requires PATH_INFO, which is missing')
 
-def likes_woff(user_agent):
-    return woff_re.search(user_agent) is not None
+    if len(environ['PATH_INFO']) < 2 or environ['PATH_INFO'][0] != '/':
+        return fonts_data.serve_font_list(environ, start_response)
 
-def likes_eot(user_agent):
-    return eot_re.search(user_agent) is not None
+    path = environ['PATH_INFO'][1:].split('/')
 
-def likes_gzip(environ):
-    try:
-        acceptable_encodings = environ['HTTP_ACCEPT_ENCODING']
-        gzip_is_acceptable = (gzip_re.search(environ['HTTP_ACCEPT_ENCODING']) is not None)
-    except:
-        gzip_is_acceptable = False
-    return gzip_is_acceptable
+    if path[0] == 'css':
+        return fonts_data.serve_css_for_font_list(environ, start_response)
 
-def font_file(font_name, extension):
-    return os.path.join(font_server_directory, font_name + extension)
+    if path[0] == 'serve':
+        if len(path) < 2:
+            return error_respond(start_response, 'A \'serve\' request is missing the font identifier')
+        else:
+            return serve_font.serve_font(environ, start_response, path[1])
 
-def font_file_exists(font_name, extension):
-    font = font_file(font_name, extension)
-    return os.path.isfile(font)
+    if path[0] == 'index':
+        return fonts_data.serve_index(environ, start_response)
 
-def feed_file_data(f, buffer_size = 8192):
-    s = f.read(buffer_size)
-    while s != '':
-        yield s
-        s = f.read(buffer_size)
-    f.close()
+    return error_respond(start_response, 'Unrecognized font server request: ' + environ['PATH_INFO'])
 
-def serve_font(environ, start_response):
-
-    if 'HTTP_USER_AGENT' not in environ:
-        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
-        return ['<p>404 NOT FOUND<hr/>Font service requires HTTP_USER_AGENT, which is missing</p>']
-
-    user_agent = environ['HTTP_USER_AGENT']
-
-    if 'PATH_INFO' not in environ or len(environ['PATH_INFO']) < 2 or environ['PATH_INFO'][0] != '/':
-        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
-        return ['<p>404 NOT FOUND<hr/>Incompatible PATH_INFO</p>']
-
-    font_name = environ['PATH_INFO'].split('/')[1]
-
-    if font_name_re.search(font_name) is None:
-        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
-        return ['<p>404 NOT FOUND<hr/>Font name contains unsupported characters</p>']
-
-    gzippable = likes_gzip(environ)
-
-    # TODO: Support SVG fonts, with content type image/svg+xml.
-
-    if likes_woff(user_agent) and font_file_exists(font_name, '.woff'):
-        font = font_file(font_name, '.woff')
-        content_type = 'application/octet-stream'
-        encoding = None
-
-    elif likes_eot(user_agent) and gzippable and font_file_exists(font_name, '.eot.jgz'):
-        font = font_file(font_name, '.eot.jgz')
-        content_type = 'application/octet-stream'
-        encoding = 'gzip'
-
-    elif likes_eot(user_agent) and font_file_exists(font_name, '.eot'):
-        font = font_file(font_name, '.eot')
-        content_type = 'application/octet-stream'
-        encoding = None
-
-    elif gzippable and font_file_exists(font_name, '.ttf.jgz'):
-        font = font_file(font_name, '.ttf.jgz')
-        content_type = 'application/octet-stream'
-        encoding = 'gzip'
-
-    elif font_file_exists(font_name, '.ttf'):
-        font = font_file(font_name, '.ttf')
-        content_type = 'application/octet-stream'
-        encoding = None
-
-    elif gzippable and font_file_exists(font_name, '.otf.jgz'):
-        font = font_file(font_name, '.otf.jgz')
-        content_type = 'application/octet-stream'
-        encoding = 'gzip'
-
-    elif font_file_exists(font_name, '.otf'):
-        font = font_file(font_name, '.otf')
-        content_type = 'application/octet-stream'
-        encoding = None
-
-    else:
-        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
-        return ['<p>404 NOT FOUND<hr/>Font not found</p>']
-
-    stat_buf = os.stat(font)
-    font_size = stat_buf[stat.ST_SIZE]
-    mtime = formatdate(stat_buf[stat.ST_MTIME], usegmt = True)
-    now = formatdate(usegmt = True)
-    etag = str(stat_buf[stat.ST_MTIME]) + ':' + str(font_size)
-    one_year = 60 * 60 * 24 * 365
-
-    f = open(font, 'rb')
-    if not f:
-        start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
-        return ['<p>404 NOT FOUND<hr/>Font cannot be opened for reading</p>']
-
-    if 'HTTP_IF_NONE_MATCH' in environ and etag == environ['HTTP_IF_NONE_MATCH']:
-
-        headers = []
-        headers.append(('Date', now))
-        start_response('304 NOT MODIFIED', headers)
-        return []
-
-    else:
-        headers = []
-        headers.append(('Date', now))
-        headers.append(('Last-Modified', mtime))
-        headers.append(('Content-Type', content_type))
-        headers.append(('Content-Length', str(font_size)))
-        if encoding is not None:
-            headers.append(('Content-Encoding', encoding))
-        headers.append(('Content-Disposition', 'inline;filename=' + os.path.basename(font)))
-        headers.append(('Cache-Control', 'max-age=' + str(one_year) + ',public'))
-        headers.append(('Access-Control-Allow-Origin', '*'))
-        headers.append(('ETag', etag))
-        start_response('200 OK', headers)
-        return feed_file_data(f)
 
 #--------------------------------------------------------------------------
 
-WSGIServer(serve_font).run()
+WSGIServer(dispatch).run()
 
 #--------------------------------------------------------------------------

File error_respond.py

+# Copyright (c) 2010 Barry Schwartz
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+def error_respond(start_response, message):
+    start_response('404 NOT FOUND', [('Content-Type', 'text/html')])
+    return ['<p>404 NOT FOUND<hr/>' + message + '</p>']

File favicon.ico

Added
New image

File fonts_config.py

+# Copyright (c) 2010 Barry Schwartz
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+fonts_directory = '/home2/crudfact/wsgi_apps/fontserver/fonts'
+database_storage = '/home2/crudfact/wsgi_apps/fontserver/storage.fs'
+server_url = 'http://fontserver.crudfactory.com'
+sfnt2woff = '/home2/crudfact/bin/sfnt2woff'
+make_eot = '/home2/crudfact/bin/make-eot-at-website'
+gzip = '/usr/bin/gzip'

File fonts_data.py

+# Copyright (c) 2010 Barry Schwartz
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+#--------------------------------------------------------------------------
+
+import copy
+import fontforge
+import fonts_config
+import mimeparse
+import os
+import sys
+import transaction
+
+from error_respond import error_respond
+from persistent import Persistent
+from persistent.list import PersistentList
+from persistent.mapping import PersistentMapping
+from ZODB import DB, FileStorage
+
+#--------------------------------------------------------------------------
+
+class Font(Persistent):
+
+    def __init__(self, fonts_dir, file_name):
+        self.file = file_name  # For instance, 'GoudyStMTT-Italic.ttf'
+        self.ident = file_name[:-4] # 'GoudyStMTT-Italic'
+        f = fontforge.open(os.path.join(fonts_dir, self.file))
+        self.ps_fontname = f.fontname
+        self.ps_family = f.familyname
+        self.ps_fullname = f.fullname
+        f.close()
+
+class FontsData(Persistent):
+
+    def __init__(self, fonts_dir):
+        file_list = os.listdir(fonts_dir)
+        self.fonts = PersistentMapping()
+        for file_name in file_list:
+            if file_name[-4:] == '.ttf':
+                font = Font(fonts_dir, file_name)
+                self.fonts[font.ident] = font
+        self.font_list_css = create_font_list_css(self.fonts.values())
+        self.font_list = create_font_list(self.fonts.values())
+
+def create_font_list_css(fonts):
+    css = []
+    for font in sorted_fonts(fonts):
+        section = ''
+        section += '@font-face{'
+        section += 'font-family:' + font.ident + ';'
+        section += 'src:url(' + fonts_config.server_url + '/serve/' + font.ident + ');'
+        section += '}\n'
+        section += '.' + font.ident + '{'
+        section += 'font-family:' + font.ident + ';'
+        section += 'font-size:16pt;'
+        section += '}\n'
+        css.append(section)
+    css.append('*{font-family:KisStMTT;font-size:12pt;font-style:normal;font-weight:normal;}\n')
+    css.append('.html-validator{font-family:TheanoOldStyle-Regular;font-size:12pt;}\n')
+    css.append('h1{font-family:ChunkFive;font-size:20pt;font-style:normal;font-weight:normal;text-align:center;}\n')
+    css.append('th{font-family:KisStMTT-Italic;font-weight:normal;text-align:left;text-indent:3em;}\n')
+    return css
+
+def create_font_list(fonts):
+
+    page = []
+    page.append('<!DOCTYPE html>\n')
+    page.append('<html xmlns="http://www.w3.org/1999/xhtml">\n')
+
+    page.append('<head>\n')
+    page.append('<meta charset="utf-8" />\n')
+    page.append('<title>The Crud Factory Font Server</title>\n')
+    page.append('<link rel="stylesheet" type="text/css" href="' + fonts_config.server_url + '/css" />\n')
+    page.append('<link rel="shortcut icon" href="' + fonts_config.server_url + '/favicon.ico" />\n')
+    page.append('</head>\n')
+
+    page.append('<body>\n')
+    page.append('<h1>The Crud Factory Font Server</h1>\n')
+    page.append('<table>')
+    page.append('<tr>')
+    page.append('<th>Font</th>')
+    page.append('<th>URL to use for @font-face</th>')
+    page.append('<th>Notes</th>')
+    page.append('</tr>\n')
+    for font in sorted_fonts(fonts):
+        page.append('<tr>')
+        page.append('<td class="' + font.ident + '">' + font.ps_fullname + '</td>\n')
+        page.append('<td>' + fonts_config.server_url + '/serve/' + font.ident + '</td>\n')
+        page.append('<td>' + note_contents(font.ident) + '</td>\n')
+        page.append('</tr>\n')
+    page.append('</table>\n')
+    page.append('<p>')
+    page.append('<br/>')
+    page.append('<a class="html-validator" href="http://validator.w3.org/check?uri=http%3A%2F%2Ffontserver.crudfactory.com%2F">[HTML5]</a>')
+    page.append(' ')
+    page.append('<a href="http://jigsaw.w3.org/css-validator/check/referer?profile=css3">')
+    page.append('<img style="border:0;width:88px;height:31px;vertical-align:top;"')
+    page.append(' src="http://jigsaw.w3.org/css-validator/images/vcss-blue"')
+    page.append(' alt="Valid CSS!" /></a>')
+    page.append('</p>\n')
+    page.append('</body>\n')
+
+    page.append('</html>\n')
+    return page
+
+def sorted_fonts(fonts):
+    return sorted(list(fonts), key = (lambda font: (font.ps_family, font.ps_fullname)))
+
+def serve_index(environ, start_response):
+
+    try:
+        storage = FileStorage.FileStorage(fonts_config.database_storage)
+        db = DB(storage)
+        connection = db.open()
+
+        dbroot = connection.root()
+        if not dbroot.has_key('fonts_data'):
+            dbroot['fonts_data'] = FontsData(fonts_config.fonts_directory)
+            transaction.commit()
+        fonts_data = dbroot['fonts_data']
+        font_idents = list(fonts_data.fonts.keys())
+
+        db.close()
+        storage.close()
+
+        start_response('200 OK', [('Content-Type', 'text/plain')])
+        return [ident + '\n' for ident in sorted(font_idents)]
+
+    except:
+        return error_respond(start_response, 'Python exception: ' + repr(sys.exc_info()))
+
+def note_contents(ident):
+    contents = ''
+    try:
+        f = open(os.path.join(fonts_config.fonts_directory, ident + '.note'))
+        contents = f.read()
+        f.close()
+    except:
+        pass
+    return contents
+
+def serve_some_data(environ, start_response, content_type, data_getter):
+
+    try:
+        storage = FileStorage.FileStorage(fonts_config.database_storage)
+        db = DB(storage)
+        connection = db.open()
+
+        dbroot = connection.root()
+        if not dbroot.has_key('fonts_data'):
+            dbroot['fonts_data'] = FontsData(fonts_config.fonts_directory)
+            transaction.commit()
+
+        data = data_getter(dbroot['fonts_data'])
+
+        db.close()
+        storage.close()
+
+        start_response('200 OK', [('Content-Type', content_type)])
+        return data
+
+    except:
+        return error_respond(start_response, 'Python exception: ' + repr(sys.exc_info()))
+
+
+def serve_css_for_font_list(environ, start_response):
+    return serve_some_data(environ, start_response, 'text/css',
+                           (lambda fonts_data : fonts_data.font_list_css))
+
+def choose_content_type(environ):
+    browser_is_msie = ('HTTP_USER_AGENT' in environ and environ['HTTP_USER_AGENT'].find(' MSIE ') != -1)
+    if browser_is_msie or 'HTTP_ACCEPT' not in environ:
+        content_type = 'text/html'
+    else:
+        accept = environ['HTTP_ACCEPT']
+        q1 = mimeparse.quality('application/xhtml+xml', accept)
+        q2 = mimeparse.quality('application/xml', accept)
+        q3 = mimeparse.quality('text/html', accept)
+        if q1 >= q2 and q1 >= q3:
+            content_type = 'application/xhtml+xml'
+        elif q2 >= q3:
+            content_type = 'application/xml'
+        else:
+            content_type = 'text/html'
+    return content_type
+
+def serve_font_list(environ, start_response):
+    return serve_some_data(environ, start_response, choose_content_type(environ),
+                           (lambda fonts_data : fonts_data.font_list))
+
+#--------------------------------------------------------------------------
+#!/home2/crudfact/bin/python
+
+# Copyright (c) 2010 Barry Schwartz
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+#--------------------------------------------------------------------------
+
+import fonts_config
+import fonts_data
+import serve_font
+import subprocess
+import transaction
+
+from persistent import Persistent
+from ZODB import DB, FileStorage
+
+#--------------------------------------------------------------------------
+
+def create_font(font_ident, extension):
+    if serve_font.font_file_exists(font_ident, '.ttf'):
+        try:
+            target_font = serve_font.font_file(font_ident, extension)
+            ttf_font = serve_font.font_file(font_ident, '.ttf')
+            if extension == '.woff':
+                subprocess.call([fonts_config.sfnt2woff, ttf_font],
+                                cwd = fonts_config.fonts_directory)
+            elif extension == '.ttf.jgz':
+                with open(target_font, 'wb') as f:
+                    subprocess.call([fonts_config.gzip, '-c', ttf_font],
+                                    stdout = f,
+                                    cwd = fonts_config.fonts_directory)
+            elif extension == '.eot':
+                subprocess.call([fonts_config.make_eot, '-n', 'dummyname', ttf_font],
+                                cwd = fonts_config.fonts_directory)
+            elif extension == '.eot.jgz':
+                # Make an .eot font.
+                subprocess.call([fonts_config.make_eot, '-n', 'dummyname', ttf_font],
+                                cwd = fonts_config.fonts_directory)
+                # Compress it.
+                # FIXME: Don't gzip-compress the .eot if it already is compressed.
+                eot_font = serve_font.font_file(font_ident, '.eot')
+                with open(target_font, 'wb') as f:
+                    subprocess.call([fonts_config.gzip, '-c', eot_font],
+                                    stdout = f,
+                                    cwd = fonts_config.fonts_directory)
+        except:
+            pass
+
+#--------------------------------------------------------------------------
+
+storage = FileStorage.FileStorage(fonts_config.database_storage)
+db = DB(storage)
+connection = db.open()
+dbroot = connection.root()
+
+fonts_data = fonts_data.FontsData(fonts_config.fonts_directory)
+dbroot['fonts_data'] = fonts_data
+transaction.commit()
+
+for ident in fonts_data.fonts:
+    if not serve_font.font_file_exists(ident, '.woff'):
+        create_font(ident, '.woff')
+    if not serve_font.font_file_exists(ident, '.ttf.jgz'):
+        create_font(ident, '.ttf.jgz')
+    if not serve_font.font_file_exists(ident, '.eot'):
+        create_font(ident, '.eot')
+    if not serve_font.font_file_exists(ident, '.eot.jgz'):
+        create_font(ident, '.eot.jgz')
+
+
+
+db.close()
+storage.close()
+
+#--------------------------------------------------------------------------

File serve_font.py

+# Copyright (c) 2010 Barry Schwartz
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+# This Python script is based in part on the Ruby program "Fontue"
+# (http://fontue.com), which is
+#
+#    Copyright (c) 2010 Garrick Van Buren, Working Pathways, Inc.
+
+#--------------------------------------------------------------------------
+
+import fonts_config
+import os
+import re
+import stat
+
+from email.utils import formatdate
+from error_respond import error_respond
+
+#--------------------------------------------------------------------------
+
+woff_re = re.compile('Firefox/[3-9]|Konqueror/[4-9]')
+eot_re = re.compile('\sMSIE\s')
+gzip_re = re.compile('(^\s*gzip\s*$|\s*gzip\s*,|,\s*gzip\s*$|,\s*gzip\s*,)')
+font_ident_re = re.compile('^[0-9a-zA-Z_-]+$')
+
+def likes_woff(user_agent):
+    return woff_re.search(user_agent) is not None
+
+def likes_eot(user_agent):
+    return eot_re.search(user_agent) is not None
+
+def likes_gzip(environ):
+    try:
+        acceptable_encodings = environ['HTTP_ACCEPT_ENCODING']
+        gzip_is_acceptable = (gzip_re.search(environ['HTTP_ACCEPT_ENCODING']) is not None)
+    except:
+        gzip_is_acceptable = False
+    return gzip_is_acceptable
+
+def font_file(font_ident, extension):
+    return os.path.join(fonts_config.fonts_directory, font_ident + extension)
+
+def font_file_exists(font_ident, extension):
+    font = font_file(font_ident, extension)
+    return os.path.isfile(font)
+
+def feed_file_data(f, buffer_size = 8192):
+    s = f.read(buffer_size)
+    while s != '':
+        yield s
+        s = f.read(buffer_size)
+    f.close()
+
+#--------------------------------------------------------------------------
+
+def serve_font(environ, start_response, font_ident):
+
+    if 'HTTP_USER_AGENT' not in environ:
+        return error_respond(start_response, 'Font service requires HTTP_USER_AGENT, which is missing')
+
+    if font_ident_re.search(font_ident) is None:
+        return error_respond(start_response, 'Font identifier contains unsupported characters')
+
+    user_agent = environ['HTTP_USER_AGENT']
+    gzippable = likes_gzip(environ)
+
+    # TODO: Support SVG fonts, with content type image/svg+xml.
+
+    if likes_woff(user_agent) and font_file_exists(font_ident, '.woff'):
+        font = font_file(font_ident, '.woff')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    elif likes_eot(user_agent) and gzippable and font_file_exists(font_ident, '.eot.jgz'):
+        font = font_file(font_ident, '.eot.jgz')
+        content_type = 'application/octet-stream'
+        encoding = 'gzip'
+
+    elif likes_eot(user_agent) and font_file_exists(font_ident, '.eot'):
+        font = font_file(font_ident, '.eot')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    elif gzippable and font_file_exists(font_ident, '.ttf.jgz'):
+        font = font_file(font_ident, '.ttf.jgz')
+        content_type = 'application/octet-stream'
+        encoding = 'gzip'
+
+    elif font_file_exists(font_ident, '.ttf'):
+        font = font_file(font_ident, '.ttf')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    elif gzippable and font_file_exists(font_ident, '.otf.jgz'):
+        font = font_file(font_ident, '.otf.jgz')
+        content_type = 'application/octet-stream'
+        encoding = 'gzip'
+
+    elif font_file_exists(font_ident, '.otf'):
+        font = font_file(font_ident, '.otf')
+        content_type = 'application/octet-stream'
+        encoding = None
+
+    else:
+        return error_respond(start_response, 'Font not found')
+
+    stat_buf = os.stat(font)
+    font_size = stat_buf[stat.ST_SIZE]
+    mtime = formatdate(stat_buf[stat.ST_MTIME], usegmt = True)
+    now = formatdate(usegmt = True)
+    etag = str(stat_buf[stat.ST_MTIME]) + ':' + str(font_size)
+    one_year = 60 * 60 * 24 * 365
+
+    f = open(font, 'rb')
+    if not f:
+        return error_respond(start_response, 'Font cannot be opened for reading')
+
+    if 'HTTP_IF_NONE_MATCH' in environ and etag == environ['HTTP_IF_NONE_MATCH']:
+
+        headers = []
+        headers.append(('Date', now))
+        start_response('304 NOT MODIFIED', headers)
+        return []
+
+    else:
+        headers = []
+        headers.append(('Date', now))
+        headers.append(('Last-Modified', mtime))
+        headers.append(('Content-Type', content_type))
+        headers.append(('Content-Length', str(font_size)))
+        if encoding is not None:
+            headers.append(('Content-Encoding', encoding))
+        headers.append(('Content-Disposition', 'inline;filename=' + os.path.basename(font)))
+        headers.append(('Cache-Control', 'max-age=' + str(one_year) + ',public'))
+        headers.append(('Access-Control-Allow-Origin', '*'))
+        headers.append(('ETag', etag))
+        start_response('200 OK', headers)
+        return feed_file_data(f)
+
+#--------------------------------------------------------------------------