2010.03.16-using.css.fonts

Using Custom Fonts In Browser

I've been complaining for a long time that it's kind of ridiculous that browsers cannot load and use an external truetype (or other) kind of font. And I need Apple II character set here (including MouseText), people! Nothing else quite looks like it. Thanks to CSS3 spec and most browsers adopting it, web page font embedding is finally a reality.

The server-side png generating way (aka the really-old way)

Remember when all people did was generate on-the-fly images & send them? Good times. I wrote a perl script that uses the GD plugins (I recall the extensions for ruby didn't seem to work very well.)

// insert gen.pl here

The client-side javascript generating way (a good way)

Let's use javascript to transform specific elements we want to change to the Apple II font.

// vim:shiftwidth=2:softtabstop=2:expandtab

// css:
// .charbox{ position: relative; }
// .char   { position: absolute; height: 16px; width: 7px; background: url('/img/000_allfonts.png'); }
// .char40 { position: absolute; height: 16px; width: 14px; background: url('/img/000_allfonts40.png');

// font pngs can be found at http://whiskers.com/img/000_allfonts...

// example usage:

// <div class="applify">Apple II Text!</div>

// $('.applify').each( function(i) {
//   this.innerHTML = applify(this.innerHTML)[0];
// });

function applify(chunk) {
  var chunklen = chunk.length;

  var additional_y_offset = 2;    // it looks nice spaced out vertically a bit
  var escapechar = "~";
  var color = 15;
  var eightycol = true;
  var mousetext = false;
  var inverse = false;
  var tilde = false;
  var checker = false;

  // will be width, height of containing div+letters
  var size_x = 0;
  var size_y = 16;

  // pre-calculate final size
  var line_x = 0;
  for (var i=0; i<chunklen; i++) {
    var c = chunk.charAt(i);
    if (c == escapechar) {
      i++;
      c = chunk.charAt(i);
      if (c == "C") { i++; }
      if (c == "4") { eightycol = false; }
      if (c == "8") { eightycol = true; }
      continue;
    }
    if (c == "\n") {
      size_y = size_y + 16 + additional_y_offset;
      size_x = Math.max(size_x, line_x);
      line_x = 0;
      continue;
    }
    if (eightycol == true) {
      line_x = line_x + 7;
      continue;
    }
    line_x = line_x + 14;
  }
  size_x = Math.max(size_x, line_x);
  //alert( chunk+" needs "+size_x+"x "+size_y+"y avail");
  var r = '<div class="charbox" style="height: '+size_y+'px; width: '+size_x+'px; margin-bottom: 2px;">';
  var dst_xoff = 0;
  var dst_yoff = 0;

  // now iterate, parse, convert string into multiple divs
  for (var i=0; i<chunklen; i++) {
    var c = chunk.charAt(i);
    if (c == "\n") {
      dst_yoff = dst_yoff + 16 + additional_y_offset;
      dst_xoff = 0;
      continue;
    }

    if (c == escapechar) {
      i++;
      c = chunk.charAt(i);
      if (c == "C") {
        i++;
        c = chunk.charCodeAt(i);
        if (c >= 48 && c <= 57) { color = c - 48; } // 0-9
        if (c >= 65 && c <= 70) { color = c - 55; } // A-F
        continue;
      }
      if (c == "4") { eightycol = false; continue; }
      if (c == "8") { eightycol = true; continue; }
      if (c == "N") { mousetext = false; inverse = false; continue; }
      if (c == "M") { mousetext = true; inverse = false; continue; }
      if (c == "I") { mousetext = false; inverse = true; continue; }
      if (c == "X") { checker = true; }
      if (c == "~") { tilde = true; }
    }

    var ca = chunk.charCodeAt(i);
    if (inverse) {
      if (ca > 63 && ca < 96) {
        ca = ca - 64;
      }
      if (checker) {
        ca = 127;
      } else if (tilde) {
        ca = 126;
      }
    } else if (mousetext) {
      if (c == " ") {
        ca = 160;
      }
      if (checker) {
        ca = 255;
      } else if (tilde) {
        ca = 254;
      }
    } else {
      ca = ca + 128;
      if (checker) {
        ca = 255;
      } else if (tilde) {
        ca = 254;
      }
    }
    checker = false;
    tilde = false;

    var src_xoff = ca * (eightycol ? 7 : 14);

    if (eightycol) {
      r += '<div class="char" style="top: '+dst_yoff+'px; left: '+dst_xoff+'px; background-position: -'+src_xoff+'px -'+color*16+'px;"></div>';
      dst_xoff = dst_xoff + 7;
      continue;
    }
    r += '<div class="char40" style="top: '+dst_yoff+'px; left: '+dst_xoff+'px; background-position: -'+src_xoff+'px -'+color*16+'px;"></div>';
    dst_xoff = dst_xoff + 14;
    //alert("char code at "+i+" is "+this.innerHTML.charCodeAt(i));
  }
  r += "</div>";

  return [r, size_x, size_y];
}

The brand new @font-face way

IE has always had the ability to use the @font-face style property with its proprietary .eot files. Modern CSS3 enabled browsers allow us to directly use truetype files! We can specify them both in succession to support most browsers. Hooray for compatibility!

Add this to the top of your style:

<style>
@font-face {
  font-family: "PR Number 3";
  src: url("/fonts/PRNumber3.eot");
}
@font-face {
  font-family: "PR Number 3";
  src: url("/fonts/PRNumber3.ttf") format("truetype");
}
@font-face {
  font-family: "Print Char 21";
  src: url("/fonts/PrintChar21.eot");
}
@font-face {
  font-family: "Print Char 21";
  src: url("/fonts/PrintChar21.ttf") format("truetype");
}

.a2-40col { font-family: "Print Char 21"; font-size: 15px; }
.a2-80col { font-family: "PR Number 3"; font-size: 15px; }
</style>

You can then use the font in your html, like so:

<div class="a2-40col"> Big 40 column text! </div>
<div class="a2-80col"> Thin 80 column text! Perfect for reading! </div>
<textarea class="a2-80col"> Edit text with Apple II font! </txtarea>

Isn't that great!

References