The CSS @font-face rule is a way to embed fonts in your web pages. The ever popular Bootstrap makes use of web fonts to show Glyph Icons and there's always Font Awesome should you need more. Google Fonts and MyFonts then have 100s and 1000s free and commercial fonts should you need them.

The problem is that IE and Chrome won't always display / use these fonts until they are served correctly from the web / application server. Following an answer I recently posted on StackOverflow, I'll explain how to serve web fonts with BedSheet.

Web font files typically have the extensions .eot, .otf, svg, .ttf and .woff. For browsers to recognise them, they need to be served up with the correct Content-Type in the HTTP Header. The value of the Content-Type field is a MIME type.

MIME Types :: The Official List

Over the years, there has been a lot of confusion as what the actual MIME types for web fonts should be. These have only been resolved recently (as late as March 2013) by the official internet authorities: Internet Assigned Numbers Authority (IANA) and the W3C.

With supporting links, here are the official MIME types for web fonts:

It's worth mentioning that you can gzip (or otherwise compress) all the above font formats except for .woff, which is already gzip compressed.

How to Configure BedSheet / Fantom

When BedSheet serves up web font files, it converts the file extension (.eot, .otf, ...) to a MIME Type. It does this using the Fantom's MimeType.forExt() method which, in turn, uses the file %FAN_HOME%/etc/sys/ext2mime.props as a lookup table. By default, Fantom does not have definitions in this file for web fonts, so we have to manually add them.

Open up %FAN_HOME%/etc/sys/ext2mime.props and add the following:

eot=application/vnd.ms-fontobject
otf=application/font-sfnt
ttf=application/font-sfnt
woff=application/font-woff

Note that SVG is already defined so we do not add it. (Doing so would result in an error.)

Also note that BedSheet is configured out of the box to gzip compress web fonts, so no extra work is required for that.

And that's it! Fantom and BedSheet are now configured to serve up web font files!

How to Configure Heroku

Editing Fantom system files is fine on dev, or even a prod machine; but what do you do when deploying to a cloud platform such as Heroku? For each time your app is deployed, it downloads a fresh installation of Fantom!

Thankfully the Fantom Build Pack for Heroku has a build hook where we can automate the MIME type addition. Add the following to your build.fan:

using build

class Build : BuildPod {

  @Target { help = "Heroku pre-compile hook" }
  Void herokuPreCompile() {

    ...

    // patch web font mime types
    patchMimeTypes([
      "eot"  : "application/vnd.ms-fontobject",
      "otf"  : "application/font-sfnt",
      "svg"  : "image/svg+xml",
      "ttf"  : "application/font-sfnt",
      "woff"  : "application/font-woff"
    ])
  }

  private Void patchMimeTypes(Str:Str extToMimeTypes) {
    ext2mime := Env.cur.findFile(`etc/sys/ext2mime.props`)
    mimeTypes := ext2mime.readProps

    toAdd := extToMimeTypes.exclude |mime, ext->Bool| {
      mimeTypes.containsKey(ext)
    }

    if (!toAdd.isEmpty) {
      log.indent
      exts := toAdd.keys.sort.join(", ")
      log.info("Mime type for file extension(s) ${exts} do not exist")
      log.info("Patching `${ext2mime.normalize}`...")
      toAdd.keys.sort.each |ext| {
        mimeTypes = mimeTypes.rw.add(ext, extToMimeTypes[ext])
        log.info(" - ${ext} : " + mimeTypes[ext])
      }
      ext2mime.writeProps(mimeTypes)
      log.info("Done.")
      log.unindent
    }
  }
}

When deployed to Heroku, your console should give the following message:

Mime type for file extension(s) eot, otf, ttf, woff do not exist
  Patching `file:/C:/Apps/fantom-1.0.65/etc/sys/ext2mime.props`...
   - eot : application/vnd.ms-fontobject
   - otf : application/font-sfnt
   - ttf : application/font-sfnt
   - woff : application/font-woff
  Done.

Have fun!


Discuss