diff --git a/README b/README
deleted file mode 100644
index ee537f0a5..000000000
--- a/README
+++ /dev/null
@@ -1,12 +0,0 @@
-pdf.js is a technology demonstrator prototype to explore whether the HTML5
-platform is complete enough to faithfully and efficiently render the ISO
-32000-1:2008 Portable Document Format (PDF) without native code assistance.
-
-You can read more about pdf.js here:
-
-http://andreasgal.com/2011/06/15/pdf-js/
-http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/
-
-Or follow us on twitter: @pdfjs
-
-http://twitter.com/#!/pdfjs
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..b6ff6c19f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,27 @@
+# pdf.js
+
+pdf.js is a technology demonstrator prototype to explore whether the HTML5
+platform is complete enough to faithfully and efficiently render the ISO
+32000-1:2008 Portable Document Format (PDF) without native code assistance.
+
+pdf.js is not currently part of the Mozilla project, and there is no plan
+yet to integrate it into Firefox. We will explore that possibility once
+pdf.js is production ready. Until then we aim to publish a Firefox
+PDF reader extension powered by pdf.js.
+
+You can read more about pdf.js here:
+
+  http://andreasgal.com/2011/06/15/pdf-js/
+  http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/
+
+follow us on twitter: @pdfjs
+
+  http://twitter.com/#!/pdfjs
+
+join our mailing list:
+
+  dev-pdf-js@lists.mozilla.org
+
+and talk to us on IRC:
+
+  #pdfjs on irc.mozilla.org
diff --git a/crypto.js b/crypto.js
index 14cc21902..e888d0212 100644
--- a/crypto.js
+++ b/crypto.js
@@ -1,5 +1,5 @@
-/* -*- Mode: Java; tab-width: s; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
-/* vim: set shiftwidth=s tabstop=2 autoindent cindent expandtab: */
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 
 "use strict";
 
@@ -45,12 +45,12 @@ var ARCFourCipher = (function() {
 })();
 
 var md5 = (function() {
-  const r = new Uint8Array([
+  var r = new Uint8Array([
     7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
     5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
     4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
     6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21]);
-  const k = new Int32Array([
+  var k = new Int32Array([
     -680876936, -389564586, 606105819, -1044525330, -176418897, 1200080426,
     -1473231341, -45705983, 1770035416, -1958414417, -42063, -1990404162,
     1804603682, -40341101, -1502002290, 1236535329, -165796510, -1069501632,
@@ -149,7 +149,7 @@ var CipherTransform = (function() {
 
 var CipherTransformFactory = (function() {
   function prepareKeyData(fileId, password, ownerPassword, userPassword, flags, revision, keyLength) {
-    const defaultPasswordBytes = new Uint8Array([
+    var defaultPasswordBytes = new Uint8Array([
       0x28, 0xBF, 0x4E, 0x5E, 0x4E, 0x75, 0x8A, 0x41, 0x64, 0x00, 0x4E, 0x56, 0xFF, 0xFA, 0x01, 0x08, 
       0x2E, 0x2E, 0x00, 0xB6, 0xD0, 0x68, 0x3E, 0x80, 0x2F, 0x0C, 0xA9, 0xFE, 0x64, 0x53, 0x69, 0x7A]);
     var hashData = new Uint8Array(88), i = 0, j, n;
diff --git a/fonts.js b/fonts.js
index 728bc5c68..9782fc9a1 100644
--- a/fonts.js
+++ b/fonts.js
@@ -3,6 +3,8 @@
 
 "use strict";
 
+var isWorker = (typeof window == "undefined");
+
 /**
  * Maximum file size of the font.
  */
@@ -26,69 +28,109 @@ var fontName  = "";
  */
 var kDisableFonts = false;
 
+
 /**
  * Hold a map of decoded fonts and of the standard fourteen Type1 fonts and
  * their acronyms.
  * TODO Add the standard fourteen Type1 fonts list by default
  *      http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
  */
-var Fonts = {
-  _active: null,
 
-  get active() {
-    return this._active;
-  },
+var Fonts = (function () {
+  var kScalePrecision = 40;
+  var fonts = Object.create(null);  
 
-  set active(name) {
-    this._active = this[name];
-  },
+  if (!isWorker) {
+    var ctx = document.createElement("canvas").getContext("2d");
+    ctx.scale(1 / kScalePrecision, 1);    
+  }
 
-  charsToUnicode: function fonts_chars2Unicode(chars) {
-    var active = this._active;
-    if (!active)
-      return chars;
-
-    // if we translated this string before, just grab it from the cache
-    var str = active.cache[chars];
-    if (str)
-      return str;
-
-    // translate the string using the font's encoding
-    var encoding = active.properties.encoding;
-    if (!encoding)
-      return chars;
-
-    str = "";
-    for (var i = 0; i < chars.length; ++i) {
-      var charcode = chars.charCodeAt(i);
-      var unicode = encoding[charcode];
-
-      // Check if the glyph has already been converted
-      if (unicode instanceof Name)
-        unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
-
-      // Handle surrogate pairs
-      if (unicode > 0xFFFF) {
-        str += String.fromCharCode(unicode & 0xFFFF);
-        unicode >>= 16;
+  function Font(name, data, properties) {
+    this.name = name;
+    this.data = data;
+    this.properties = properties;
+    this.loading = true;
+    this.charsCache = Object.create(null);
+    this.sizes = [];
+  }
+
+  var current;
+  var charsCache;
+  var measureCache;
+
+  return {
+    registerFont: function fonts_registerFont(fontName, data, properties) {
+      fonts[fontName] = new Font(fontName, data, properties);
+    },
+    blacklistFont: function fonts_blacklistFont(fontName) {
+      registerFont(fontName, null, {});
+      markLoaded(fontName);
+    },
+    lookup: function fonts_lookup(fontName) {
+      return fonts[fontName];
+    },
+    setActive: function fonts_setActive(fontName, size) {
+      current = fonts[fontName];
+      charsCache = current.charsCache;
+      var sizes = current.sizes;
+      if (!(measureCache = sizes[size]))
+        measureCache = sizes[size] = Object.create(null);
+      ctx.font = (size * kScalePrecision) + 'px "' + fontName + '"';
+    },
+    charsToUnicode: function fonts_chars2Unicode(chars) {
+      if (!charsCache)
+        return chars;
+
+      // if we translated this string before, just grab it from the cache
+      var str = charsCache[chars];
+      if (str)
+        return str;
+
+      // translate the string using the font's encoding
+      var encoding = current.properties.encoding;
+      if (!encoding)
+        return chars;
+
+      str = "";
+      for (var i = 0; i < chars.length; ++i) {
+        var charcode = chars.charCodeAt(i);
+        var unicode = encoding[charcode];
+
+        // Check if the glyph has already been converted
+        if (!IsNum(unicode))
+          unicode = encoding[unicode] = GlyphsUnicode[unicode.name];
+
+        // Handle surrogate pairs
+        if (unicode > 0xFFFF) {
+          str += String.fromCharCode(unicode & 0xFFFF);
+          unicode >>= 16;
+        }
+        str += String.fromCharCode(unicode);
       }
-      str += String.fromCharCode(unicode);
-    }
 
-    // Enter the translated string into the cache
-    return active.cache[chars] = str;
+      // Enter the translated string into the cache
+      return charsCache[chars] = str;
+    },
+    measureText: function fonts_measureText(text) {
+      var width;
+      if (measureCache && (width = measureCache[text]))
+        return width;
+      width = ctx.measureText(text).width / kScalePrecision;
+      if (measureCache)
+        measureCache[text] = width;
+      return width;
+    }
   }
-};
+})();
 
 var FontLoader = {
   bind: function(fonts) {
-    var worker = (typeof window == "undefined");
     var ready = true;
 
     for (var i = 0; i < fonts.length; i++) {
       var font = fonts[i];
-      if (Fonts[font.name]) {
-        ready = ready && !Fonts[font.name].loading;
+      if (Fonts.lookup(font.name)) {
+        ready = ready && !Fonts.lookup(font.name).loading;
         continue;
       }
 
@@ -97,18 +139,152 @@ var FontLoader = {
       var obj = new Font(font.name, font.file, font.properties);
 
       var str = "";
-      var data = Fonts[font.name].data;
+      var data = Fonts.lookup(font.name).data;
       var length = data.length;
       for (var j = 0; j < length; j++)
         str += String.fromCharCode(data[j]);
 
-      worker ? obj.bindWorker(str) : obj.bindDOM(str);
+      isWorker ? obj.bindWorker(str) : obj.bindDOM(str);
     }
 
     return ready;
   }
 };
 
+var UnicodeRanges = [
+  { "begin": 0x0000, "end": 0x007F }, // Basic Latin
+  { "begin": 0x0080, "end": 0x00FF }, // Latin-1 Supplement
+  { "begin": 0x0100, "end": 0x017F }, // Latin Extended-A
+  { "begin": 0x0180, "end": 0x024F }, // Latin Extended-B
+  { "begin": 0x0250, "end": 0x02AF }, // IPA Extensions
+  { "begin": 0x02B0, "end": 0x02FF }, // Spacing Modifier Letters
+  { "begin": 0x0300, "end": 0x036F }, // Combining Diacritical Marks
+  { "begin": 0x0370, "end": 0x03FF }, // Greek and Coptic
+  { "begin": 0x2C80, "end": 0x2CFF }, // Coptic
+  { "begin": 0x0400, "end": 0x04FF }, // Cyrillic
+  { "begin": 0x0530, "end": 0x058F }, // Armenian
+  { "begin": 0x0590, "end": 0x05FF }, // Hebrew
+  { "begin": 0xA500, "end": 0xA63F }, // Vai
+  { "begin": 0x0600, "end": 0x06FF }, // Arabic
+  { "begin": 0x07C0, "end": 0x07FF }, // NKo
+  { "begin": 0x0900, "end": 0x097F }, // Devanagari
+  { "begin": 0x0980, "end": 0x09FF }, // Bengali
+  { "begin": 0x0A00, "end": 0x0A7F }, // Gurmukhi
+  { "begin": 0x0A80, "end": 0x0AFF }, // Gujarati
+  { "begin": 0x0B00, "end": 0x0B7F }, // Oriya
+  { "begin": 0x0B80, "end": 0x0BFF }, // Tamil
+  { "begin": 0x0C00, "end": 0x0C7F }, // Telugu
+  { "begin": 0x0C80, "end": 0x0CFF }, // Kannada
+  { "begin": 0x0D00, "end": 0x0D7F }, // Malayalam
+  { "begin": 0x0E00, "end": 0x0E7F }, // Thai
+  { "begin": 0x0E80, "end": 0x0EFF }, // Lao
+  { "begin": 0x10A0, "end": 0x10FF }, // Georgian
+  { "begin": 0x1B00, "end": 0x1B7F }, // Balinese
+  { "begin": 0x1100, "end": 0x11FF }, // Hangul Jamo
+  { "begin": 0x1E00, "end": 0x1EFF }, // Latin Extended Additional
+  { "begin": 0x1F00, "end": 0x1FFF }, // Greek Extended
+  { "begin": 0x2000, "end": 0x206F }, // General Punctuation
+  { "begin": 0x2070, "end": 0x209F }, // Superscripts And Subscripts
+  { "begin": 0x20A0, "end": 0x20CF }, // Currency Symbol
+  { "begin": 0x20D0, "end": 0x20FF }, // Combining Diacritical Marks For Symbols
+  { "begin": 0x2100, "end": 0x214F }, // Letterlike Symbols
+  { "begin": 0x2150, "end": 0x218F }, // Number Forms
+  { "begin": 0x2190, "end": 0x21FF }, // Arrows
+  { "begin": 0x2200, "end": 0x22FF }, // Mathematical Operators
+  { "begin": 0x2300, "end": 0x23FF }, // Miscellaneous Technical
+  { "begin": 0x2400, "end": 0x243F }, // Control Pictures
+  { "begin": 0x2440, "end": 0x245F }, // Optical Character Recognition
+  { "begin": 0x2460, "end": 0x24FF }, // Enclosed Alphanumerics
+  { "begin": 0x2500, "end": 0x257F }, // Box Drawing
+  { "begin": 0x2580, "end": 0x259F }, // Block Elements
+  { "begin": 0x25A0, "end": 0x25FF }, // Geometric Shapes
+  { "begin": 0x2600, "end": 0x26FF }, // Miscellaneous Symbols
+  { "begin": 0x2700, "end": 0x27BF }, // Dingbats
+  { "begin": 0x3000, "end": 0x303F }, // CJK Symbols And Punctuation
+  { "begin": 0x3040, "end": 0x309F }, // Hiragana
+  { "begin": 0x30A0, "end": 0x30FF }, // Katakana
+  { "begin": 0x3100, "end": 0x312F }, // Bopomofo
+  { "begin": 0x3130, "end": 0x318F }, // Hangul Compatibility Jamo
+  { "begin": 0xA840, "end": 0xA87F }, // Phags-pa
+  { "begin": 0x3200, "end": 0x32FF }, // Enclosed CJK Letters And Months
+  { "begin": 0x3300, "end": 0x33FF }, // CJK Compatibility
+  { "begin": 0xAC00, "end": 0xD7AF }, // Hangul Syllables
+  { "begin": 0xD800, "end": 0xDFFF }, // Non-Plane 0 *
+  { "begin": 0x10900, "end": 0x1091F }, // Phoenicia
+  { "begin": 0x4E00, "end": 0x9FFF }, // CJK Unified Ideographs
+  { "begin": 0xE000, "end": 0xF8FF }, // Private Use Area (plane 0)
+  { "begin": 0x31C0, "end": 0x31EF }, // CJK Strokes
+  { "begin": 0xFB00, "end": 0xFB4F }, // Alphabetic Presentation Forms
+  { "begin": 0xFB50, "end": 0xFDFF }, // Arabic Presentation Forms-A
+  { "begin": 0xFE20, "end": 0xFE2F }, // Combining Half Marks
+  { "begin": 0xFE10, "end": 0xFE1F }, // Vertical Forms
+  { "begin": 0xFE50, "end": 0xFE6F }, // Small Form Variants
+  { "begin": 0xFE70, "end": 0xFEFF }, // Arabic Presentation Forms-B
+  { "begin": 0xFF00, "end": 0xFFEF }, // Halfwidth And Fullwidth Forms
+  { "begin": 0xFFF0, "end": 0xFFFF }, // Specials
+  { "begin": 0x0F00, "end": 0x0FFF }, // Tibetan
+  { "begin": 0x0700, "end": 0x074F }, // Syriac
+  { "begin": 0x0780, "end": 0x07BF }, // Thaana
+  { "begin": 0x0D80, "end": 0x0DFF }, // Sinhala
+  { "begin": 0x1000, "end": 0x109F }, // Myanmar
+  { "begin": 0x1200, "end": 0x137F }, // Ethiopic
+  { "begin": 0x13A0, "end": 0x13FF }, // Cherokee
+  { "begin": 0x1400, "end": 0x167F }, // Unified Canadian Aboriginal Syllabics
+  { "begin": 0x1680, "end": 0x169F }, // Ogham
+  { "begin": 0x16A0, "end": 0x16FF }, // Runic
+  { "begin": 0x1780, "end": 0x17FF }, // Khmer
+  { "begin": 0x1800, "end": 0x18AF }, // Mongolian
+  { "begin": 0x2800, "end": 0x28FF }, // Braille Patterns
+  { "begin": 0xA000, "end": 0xA48F }, // Yi Syllables
+  { "begin": 0x1700, "end": 0x171F }, // Tagalog
+  { "begin": 0x10300, "end": 0x1032F }, // Old Italic
+  { "begin": 0x10330, "end": 0x1034F }, // Gothic
+  { "begin": 0x10400, "end": 0x1044F }, // Deseret
+  { "begin": 0x1D000, "end": 0x1D0FF }, // Byzantine Musical Symbols
+  { "begin": 0x1D400, "end": 0x1D7FF }, // Mathematical Alphanumeric Symbols
+  { "begin": 0xFF000, "end": 0xFFFFD }, // Private Use (plane 15)
+  { "begin": 0xFE00, "end": 0xFE0F }, // Variation Selectors
+  { "begin": 0xE0000, "end": 0xE007F }, // Tags
+  { "begin": 0x1900, "end": 0x194F }, // Limbu
+  { "begin": 0x1950, "end": 0x197F }, // Tai Le
+  { "begin": 0x1980, "end": 0x19DF }, // New Tai Lue
+  { "begin": 0x1A00, "end": 0x1A1F }, // Buginese
+  { "begin": 0x2C00, "end": 0x2C5F }, // Glagolitic
+  { "begin": 0x2D30, "end": 0x2D7F }, // Tifinagh
+  { "begin": 0x4DC0, "end": 0x4DFF }, // Yijing Hexagram Symbols
+  { "begin": 0xA800, "end": 0xA82F }, // Syloti Nagri
+  { "begin": 0x10000, "end": 0x1007F }, // Linear B Syllabary
+  { "begin": 0x10140, "end": 0x1018F }, // Ancient Greek Numbers
+  { "begin": 0x10380, "end": 0x1039F }, // Ugaritic
+  { "begin": 0x103A0, "end": 0x103DF }, // Old Persian
+  { "begin": 0x10450, "end": 0x1047F }, // Shavian
+  { "begin": 0x10480, "end": 0x104AF }, // Osmanya
+  { "begin": 0x10800, "end": 0x1083F }, // Cypriot Syllabary
+  { "begin": 0x10A00, "end": 0x10A5F }, // Kharoshthi
+  { "begin": 0x1D300, "end": 0x1D35F }, // Tai Xuan Jing Symbols
+  { "begin": 0x12000, "end": 0x123FF }, // Cuneiform
+  { "begin": 0x1D360, "end": 0x1D37F }, // Counting Rod Numerals
+  { "begin": 0x1B80, "end": 0x1BBF }, // Sundanese
+  { "begin": 0x1C00, "end": 0x1C4F }, // Lepcha
+  { "begin": 0x1C50, "end": 0x1C7F }, // Ol Chiki
+  { "begin": 0xA880, "end": 0xA8DF }, // Saurashtra
+  { "begin": 0xA900, "end": 0xA92F }, // Kayah Li
+  { "begin": 0xA930, "end": 0xA95F }, // Rejang
+  { "begin": 0xAA00, "end": 0xAA5F }, // Cham
+  { "begin": 0x10190, "end": 0x101CF }, // Ancient Symbols
+  { "begin": 0x101D0, "end": 0x101FF }, // Phaistos Disc
+  { "begin": 0x102A0, "end": 0x102DF }, // Carian
+  { "begin": 0x1F030, "end": 0x1F09F }  // Domino Tiles
+];
+
+function getUnicodeRangeFor(value) {
+  for (var i = 0; i < UnicodeRanges.length; i++) {
+    var range = UnicodeRanges[i];
+    if (value >= range.begin && value < range.end)
+      return i;
+  }
+  return -1;
+};
 
 /**
  * 'Font' is the class the outside world should use, it encapsulate all the font
@@ -124,8 +300,8 @@ var Font = (function () {
     this.encoding = properties.encoding;
 
     // If the font has already been decoded simply return it
-    if (Fonts[name]) {
-      this.font = Fonts[name].data;
+    if (Fonts.lookup(name)) {
+      this.font = Fonts.lookup(name).data;
       return;
     }
     fontCount++;
@@ -134,12 +310,7 @@ var Font = (function () {
     // If the font is to be ignored, register it like an already loaded font
     // to avoid the cost of waiting for it be be loaded by the platform.
     if (properties.ignore || kDisableFonts) {
-      Fonts[name] = {
-        data: file,
-        loading: false,
-        properties: {},
-        cache: Object.create(null)
-      }
+      Fonts.blacklistFont(name);
       return;
     }
 
@@ -165,13 +336,8 @@ var Font = (function () {
         warn("Font " + properties.type + " is not supported");
         break;
     }
-
-    Fonts[name] = {
-      data: data,
-      properties: properties,
-      loading: true,
-      cache: Object.create(null)
-    };
+    this.data = data;
+    Fonts.registerFont(name, data, properties);
   };
 
   function stringToArray(str) {
@@ -221,6 +387,9 @@ var Font = (function () {
     // offset
     var offset = offsets.virtualOffset;
 
+    // length
+    var length = data.length;
+	
     // Per spec tables must be 4-bytes align so add padding as needed
     while (data.length & 3)
       data.push(0x00);
@@ -228,16 +397,10 @@ var Font = (function () {
     while (offsets.virtualOffset & 3)
       offsets.virtualOffset++;
 
-    // length
-    var length = data.length;
-
     // checksum
-    var checksum = tag.charCodeAt(0) +
-                   tag.charCodeAt(1) +
-                   tag.charCodeAt(2) +
-                   tag.charCodeAt(3) +
-                   offset +
-                   length;
+    var checksum = 0;
+    for (var i = 0; i < length; i+=4)
+      checksum += FontsUtils.bytesToInteger([data[i], data[i+1], data[i+2], data[i+3]]);
 
     var tableEntry = tag + string32(checksum) + string32(offset) + string32(length);
     tableEntry = stringToArray(tableEntry);
@@ -271,6 +434,7 @@ var Font = (function () {
   };
 
   function createCMapTable(glyphs) {
+    glyphs.push({ unicode: 0x0000 });
     var ranges = getRanges(glyphs);
 
     var headerSize = (12 * 2 + (ranges.length * 4 * 2));
@@ -304,13 +468,13 @@ var Font = (function () {
       var range = ranges[i];
       var start = range[0];
       var end = range[1];
-      var delta = (((start - 1) - bias) ^ 0xffff) + 1;
+      var delta = (((start - 1) - bias) ^ 0xffff);
       bias += (end - start + 1);
 
       startCount += string16(start);
       endCount += string16(end);
       idDeltas += string16(delta);
-      idRangeOffsets += string16(0);
+	  idRangeOffsets += string16(0);
 
       for (var j = 0; j < range.length; j++)
         glyphsIds += String.fromCharCode(range[j]);
@@ -326,11 +490,43 @@ var Font = (function () {
   };
 
   function createOS2Table(properties) {
+    var ulUnicodeRange1 = 0;
+    var ulUnicodeRange2 = 0;
+    var ulUnicodeRange3 = 0;
+    var ulUnicodeRange4 = 0;
+
+	var charset = properties.charset;
+    if (charset && charset.length) {
+	    var firstCharIndex = null;
+	    var lastCharIndex = 0;
+
+      for (var i = 1; i < charset.length; i++) {
+	      var code = GlyphsUnicode[charset[i]];
+		    if (firstCharIndex > code || !firstCharIndex)
+		      firstCharIndex = code;
+		    if (lastCharIndex < code)
+		      lastCharIndex = code;
+
+	      var position = getUnicodeRangeFor(code);
+        if (position < 32) {
+          ulUnicodeRange1 |= 1 << position;
+        } else if (position < 64) {
+          ulUnicodeRange2 |= 1 << position - 32;
+        } else if (position < 96) {
+          ulUnicodeRange3 |= 1 << position - 64;
+        } else if (position < 123) {
+          ulUnicodeRange4 |= 1 << position - 96;
+        } else {
+          error("Unicode ranges Bits > 123 are reserved for internal usage");
+        }
+      }
+    }
+
     return "\x00\x03" + // version
            "\x02\x24" + // xAvgCharWidth
            "\x01\xF4" + // usWeightClass
            "\x00\x05" + // usWidthClass
-           "\x00\x00" + // fstype
+           "\x00\x02" + // fstype
            "\x02\x8A" + // ySubscriptXSize
            "\x02\xBB" + // ySubscriptYSize
            "\x00\x00" + // ySubscriptXOffset
@@ -342,41 +538,41 @@ var Font = (function () {
            "\x00\x31" + // yStrikeOutSize
            "\x01\x02" + // yStrikeOutPosition
            "\x00\x00" + // sFamilyClass
-           "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose
-           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31)
-           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63)
-           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95)
-           "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127)
+           "\x00\x00\x06" + String.fromCharCode(properties.fixedPitch ? 0x09 : 0x00) +
+           "\x00\x00\x00\x00\x00\x00" + // Panose
+           string32(ulUnicodeRange1) + // ulUnicodeRange1 (Bits 0-31)
+           string32(ulUnicodeRange2) + // ulUnicodeRange2 (Bits 32-63)
+           string32(ulUnicodeRange3) + // ulUnicodeRange3 (Bits 64-95)
+           string32(ulUnicodeRange4) + // ulUnicodeRange4 (Bits 96-127)
            "\x2A\x32\x31\x2A" + // achVendID
-           "\x00\x20" + // fsSelection
-           "\x00\x2D" + // usFirstCharIndex
-           "\x00\x7A" + // usLastCharIndex
-           "\x00\x03" + // sTypoAscender
-           "\x00\x20" + // sTypeDescender
-           "\x00\x38" + // sTypoLineGap
+           string16(properties.italicAngle ? 1 : 0) + // fsSelection
+           string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex
+           string16(lastCharIndex || properties.lastChar) +  // usLastCharIndex
+           string16(properties.ascent) + // sTypoAscender
+           string16(properties.descent) + // sTypoDescender
+           "\x00\x64" + // sTypoLineGap (7%-10% of the unitsPerEM value)
            string16(properties.ascent)  + // usWinAscent
-           string16(properties.descent) + // usWinDescent
-           "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
-           "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
+           string16(-properties.descent) + // usWinDescent
+           "\x00\x00\x00\x00" + // ulCodePageRange1 (Bits 0-31)
+           "\x00\x00\x00\x00" + // ulCodePageRange2 (Bits 32-63)
            string16(properties.xHeight)   + // sxHeight
            string16(properties.capHeight) + // sCapHeight
-           "\x00\x01" + // usDefaultChar
-           "\x00\xCD" + // usBreakChar
-           "\x00\x02";  // usMaxContext
+           string16(0) + // usDefaultChar
+           string16(firstCharIndex || properties.firstChar) + // usBreakChar
+           "\x00\x03";  // usMaxContext
   };
 
   function createPostTable(properties) {
-    TODO("Fill with real values from the font dict");
-
-    return "\x00\x03\x00\x00"               + // Version number
-           string32(properties.italicAngle) + // italicAngle
-           "\x00\x00"                       + // underlinePosition
-           "\x00\x00"                       + // underlineThickness
-           "\x00\x00\x00\x00"               + // isFixedPitch
-           "\x00\x00\x00\x00"               + // minMemType42
-           "\x00\x00\x00\x00"               + // maxMemType42
-           "\x00\x00\x00\x00"               + // minMemType1
-           "\x00\x00\x00\x00";                // maxMemType1
+    var angle = Math.floor(properties.italicAngle * (Math.pow(2, 16)));
+    return "\x00\x03\x00\x00" + // Version number
+           string32(angle)    + // italicAngle
+           "\x00\x00"         + // underlinePosition
+           "\x00\x00"         + // underlineThickness
+           string32(properties.fixedPitch) + // isFixedPitch
+           "\x00\x00\x00\x00" + // minMemType42
+           "\x00\x00\x00\x00" + // maxMemType42
+           "\x00\x00\x00\x00" + // minMemType1
+           "\x00\x00\x00\x00";  // maxMemType1
   };
 
   constructor.prototype = {
@@ -604,44 +800,75 @@ var Font = (function () {
       var otf = new Uint8Array(kMaxFontFileSize);
 
       function createNameTable(name) {
-        var names = [
-          "See original licence",  // Copyright
-          fontName,                // Font family
-          "undefined",             // Font subfamily (font weight)
-          "uniqueID",              // Unique ID
-          fontName,                // Full font name
-          "0.1",                   // Version
-          "undefined",             // Postscript name
-          "undefined",             // Trademark
-          "undefined",             // Manufacturer
-          "undefined"              // Designer
+  	    // All the strings of the name table should be an odd number of bytes
+        if (name.length % 2)
+          name = name.slice(0, name.length - 1);
+
+        var strings = [
+          "Original licence",  // 0.Copyright
+          name,                // 1.Font family
+          "Unknown",           // 2.Font subfamily (font weight)
+          "uniqueID",          // 3.Unique ID
+          name,                // 4.Full font name
+          "Version 0.11",      // 5.Version
+          "Unknown",           // 6.Postscript name
+          "Unknown",           // 7.Trademark
+          "Unknown",           // 8.Manufacturer
+          "Unknown"            // 9.Designer
         ];
 
+        // Mac want 1-byte per character strings while Windows want
+        // 2-bytes per character, so duplicate the names table
+        var stringsUnicode = [];
+        for (var i = 0; i < strings.length; i++) {
+          var str = strings[i];
+
+          var strUnicode = "";
+          for (var j = 0; j < str.length; j++)
+            strUnicode += string16(str.charCodeAt(j));
+          stringsUnicode.push(strUnicode);
+        }
+
+        var names = [strings, stringsUnicode];
+        var platforms = ["\x00\x01", "\x00\x03"];
+        var encodings = ["\x00\x00", "\x00\x01"];
+        var languages = ["\x00\x00", "\x04\x09"];
+
+        var namesRecordCount = strings.length * platforms.length;
         var nameTable =
-          "\x00\x00" + // format
-          "\x00\x0A" + // Number of names Record
-          "\x00\x7E";  // Storage
+          "\x00\x00" +                           // format
+          string16(namesRecordCount) +           // Number of names Record
+          string16(namesRecordCount * 12 + 6);   // Storage
 
         // Build the name records field
         var strOffset = 0;
-        for (var i = 0; i < names.length; i++) {
-          var str = names[i];
-
-          var nameRecord =
-            "\x00\x01" + // platform ID
-            "\x00\x00" + // encoding ID
-            "\x00\x00" + // language ID
-            "\x00\x00" + // name ID
-            string16(str.length) +
-            string16(strOffset);
-          nameTable += nameRecord;
-
-          strOffset += str.length;
+        for (var i = 0; i < platforms.length; i++) {
+          var strs = names[i];
+          for (var j = 0; j < strs.length; j++) {
+            var str = strs[j];
+            var nameRecord =
+              platforms[i] + // platform ID
+              encodings[i] + // encoding ID
+              languages[i] + // language ID
+              string16(i) + // name ID
+              string16(str.length) +
+              string16(strOffset);
+            nameTable += nameRecord;
+            strOffset += str.length;
+          }
         }
 
-        nameTable += names.join("");
+		    nameTable += strings.join("") + stringsUnicode.join("");
         return nameTable;
       }
+	  
+	    function isFixedPitch(glyphs) {
+	      for (var i = 0; i < glyphs.length - 1; i++) {
+		    if (glyphs[i] != glyphs[i+1])
+		      return false;
+		    }
+		    return true;
+      };
 
       // Required Tables
       var CFF =
@@ -672,30 +899,31 @@ var Font = (function () {
       createTableEntry(otf, offsets, "CFF ", CFF);
 
       /** OS/2 */
+	    var charstrings = font.charstrings;
+	    properties.fixedPitch = isFixedPitch(charstrings);
       OS2 = stringToArray(createOS2Table(properties));
       createTableEntry(otf, offsets, "OS/2", OS2);
 
       /** CMAP */
-      var charstrings = font.charstrings;
-      cmap = createCMapTable(charstrings);
+      cmap = createCMapTable(charstrings.slice());
       createTableEntry(otf, offsets, "cmap", cmap);
 
       /** HEAD */
       head = stringToArray(
               "\x00\x01\x00\x00" + // Version number
-              "\x00\x00\x50\x00" + // fontRevision
+              "\x00\x00\x10\x00" + // fontRevision
               "\x00\x00\x00\x00" + // checksumAdjustement
               "\x5F\x0F\x3C\xF5" + // magicNumber
               "\x00\x00" + // Flags
               "\x03\xE8" + // unitsPerEM (defaulting to 1000)
-              "\x00\x00\x00\x00\x00\x00\x00\x00" + // creation date
-              "\x00\x00\x00\x00\x00\x00\x00\x00" + // modifification date
+              "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // creation date
+              "\x00\x00\x00\x00\x9e\x0b\x7e\x27" + // modifification date
               "\x00\x00" + // xMin
-              "\x00\x00" + // yMin
-              "\x00\x00" + // xMax
-              "\x00\x00" + // yMax
-              "\x00\x00" + // macStyle
-              "\x00\x00" + // lowestRecPPEM
+              string16(properties.descent) + // yMin
+              "\x0F\xFF" + // xMax
+              string16(properties.ascent) + // yMax
+              string16(properties.italicAngle ? 2 : 0) + // macStyle
+              "\x00\x11" + // lowestRecPPEM
               "\x00\x00" + // fontDirectionHint
               "\x00\x00" + // indexToLocFormat
               "\x00\x00"   // glyphDataFormat
@@ -705,22 +933,22 @@ var Font = (function () {
       /** HHEA */
       hhea = stringToArray(
                  "\x00\x01\x00\x00" + // Version number
-                 "\x00\x00" + // Typographic Ascent
-                 "\x00\x00" + // Typographic Descent
+                 string16(properties.ascent) + // Typographic Ascent
+                 string16(properties.descent) + // Typographic Descent
                  "\x00\x00" + // Line Gap
                  "\xFF\xFF" + // advanceWidthMax
                  "\x00\x00" + // minLeftSidebearing
                  "\x00\x00" + // minRightSidebearing
                  "\x00\x00" + // xMaxExtent
-                 "\x00\x00" + // caretSlopeRise
-                 "\x00\x00" + // caretSlopeRun
+                 string16(properties.capHeight) + // caretSlopeRise
+                 string16(Math.tan(properties.italicAngle) * properties.xHeight) + // caretSlopeRun
                  "\x00\x00" + // caretOffset
                  "\x00\x00" + // -reserved-
                  "\x00\x00" + // -reserved-
                  "\x00\x00" + // -reserved-
                  "\x00\x00" + // -reserved-
                  "\x00\x00" + // metricDataFormat
-                 string16(charstrings.length)
+                 string16(charstrings.length + 1) // Number of HMetrics
       );
       createTableEntry(otf, offsets, "hhea", hhea);
 
@@ -730,23 +958,21 @@ var Font = (function () {
       * while Windows use this data. So be careful if you hack on Linux and
       * have to touch the 'hmtx' table
       */
-      hmtx = "\x01\xF4\x00\x00"; // Fake .notdef
-      var width = 0, lsb = 0;
+      hmtx = "\x00\x00\x00\x00"; // Fake .notdef
       for (var i = 0; i < charstrings.length; i++) {
-        width = charstrings[i].charstring[1];
-        hmtx += string16(width) + string16(lsb);
+        hmtx += string16(charstrings[i].width) + string16(0);
       }
       hmtx = stringToArray(hmtx);
       createTableEntry(otf, offsets, "hmtx", hmtx);
 
       /** MAXP */
       maxp = "\x00\x00\x50\x00" + // Version number
-             string16(charstrings.length + 1); // Num of glyphs (+1 to pass the sanitizer...)
+             string16(charstrings.length + 1); // Num of glyphs
       maxp = stringToArray(maxp);
       createTableEntry(otf, offsets, "maxp", maxp);
 
       /** NAME */
-      name = stringToArray(createNameTable(name));
+      name = stringToArray(createNameTable(fontName));
       createTableEntry(otf, offsets, "name", name);
 
       /** POST */
@@ -778,9 +1004,18 @@ var Font = (function () {
       });
     },
 
-    bindDOM: function font_bindDom(data) {
+    bindDOM: function font_bindDom(data, callback) {
       var fontName = this.name;
 
+      // Just adding the font-face to the DOM doesn't make it load. It
+      // seems it's loaded once Gecko notices it's used. Therefore,
+      // add a div on the page using the loaded font.
+      var div = document.createElement("div");
+      var style = 'font-family:"' + name +
+        '";position: absolute;top:-99999;left:-99999;z-index:-99999';
+      div.setAttribute("style", style);
+      document.body.appendChild(div);
+
       /** Hack begin */
       // Actually there is not event when a font has finished downloading so
       // the following code are a dirty hack to 'guess' when a font is ready
@@ -800,15 +1035,19 @@ var Font = (function () {
 
         // For some reasons the font has not loaded, so mark it loaded for the
         // page to proceed but cry
-        if ((Date.now() - this.start) >= kMaxWaitForFontFace) {
-          window.clearInterval(interval);
-          Fonts[fontName].loading = false;
-          warn("Is " + fontName + " loaded?");
-          this.start = 0;
-        } else if (textWidth != ctx.measureText(testString).width) {
-          window.clearInterval(interval);
-          Fonts[fontName].loading = false;
-          this.start = 0;
+        if (textWidth == ctx.measureText(testString).width) {
+          if ((Date.now() - this.start) < kMaxWaitForFontFace) {
+            return;
+          } else {
+            warn("Is " + fontName + " loaded?");
+          }
+        }
+
+        window.clearInterval(interval);
+        Fonts.lookup(fontName).loading = false;
+        this.start = 0;
+        if (callback) {
+          callback();
         }
       }, 30, this);
 
@@ -839,7 +1078,7 @@ var FontsUtils = {
       bytes.set([value]);
       return bytes[0];
     } else if (bytesCount == 2) {
-      bytes.set([value >> 8, value]);
+      bytes.set([value >> 8, value & 0xff]);
       return [bytes[0], bytes[1]];
     } else if (bytesCount == 4) {
       bytes.set([value >> 24, value >> 16, value >> 8, value]);
@@ -980,16 +1219,8 @@ var Type1Parser = function() {
       "12": "div",
 
       // callothersubr is a mechanism to make calls on the postscript
-      // interpreter.
-      // TODO When decodeCharstring encounter such a command it should
-      //      directly do:
-      //        - pop the previous charstring[] command into 'index'
-      //        - pop the previous charstring[] command and ignore it, it is
-      //          normally the number of element to push on the stack before
-      //          the command but since everything will be pushed on the stack
-      //          by the PS interpreter when it will read them that is safe to
-      //          ignore this command
-      //        - push the content of the OtherSubrs[index] inside charstring[]
+      // interpreter, this is not supported by Type2 charstring but hopefully
+      // most of the default commands can be ignored safely.
       "16": "callothersubr",
 
       "17": "pop",
@@ -1009,8 +1240,13 @@ var Type1Parser = function() {
     "31": "hvcurveto"
   };
 
+  var kEscapeCommand = 12;
+
   function decodeCharString(array) {
-    var charString = [];
+    var charstring = [];
+    var lsb = 0;
+    var width = 0;
+    var used = false;
 
     var value = "";
     var count = array.length;
@@ -1019,10 +1255,48 @@ var Type1Parser = function() {
 
       if (value < 32) {
         var command = null;
-        if (value == 12) {
+        if (value == kEscapeCommand) {
           var escape = array[++i];
+
+          // TODO Clean this code
+          if (escape == 16) {
+            var index = charstring.pop();
+            var argc = charstring.pop();
+            var data = charstring.pop();
+
+            // If the flex mechanishm is not used in a font program, Adobe
+            // state that that entries 0, 1 and 2 can simply be replace by
+            // {}, which means that we can simply ignore them.
+            if (index < 3) {
+              continue;
+            }
+
+            // This is the same things about hint replacement, if it is not used
+            // entry 3 can be replaced by {3}
+            if (index == 3) {
+              charstring.push(3);
+              i++;
+              continue;
+            }
+          }
+
           command = charStringDictionary["12"][escape];
         } else {
+          // TODO Clean this code
+          if (value == 13) {
+            if (charstring.length == 2) {
+              width = charstring[1];
+            } else if (charstring.length == 4 && charstring[3] == "div") {
+              width = charstring[1] / charstring[2];
+            } else {
+              error("Unsupported hsbw format: " + charstring);
+            }
+
+            lsb = charstring[0];
+            charstring.push(lsb, "hmoveto");
+            charstring.splice(0, 1);
+            continue;
+          }
           command = charStringDictionary[value];
         }
 
@@ -1044,16 +1318,14 @@ var Type1Parser = function() {
       } else if (value <= 254) {
         value = -((value - 251) * 256) - parseInt(array[++i]) - 108;
       } else {
-        var byte = array[++i];
-        var high = (byte >> 1);
-        value = (byte - high) << 24 | array[++i] << 16 |
-                array[++i] << 8 | array[++i];
+        value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 |
+                (array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0;
       }
 
-      charString.push(value);
+      charstring.push(value);
     }
 
-    return charString;
+    return { charstring: charstring, width: width, lsb: lsb };
   };
 
   /**
@@ -1080,19 +1352,21 @@ var Type1Parser = function() {
         length = parseInt(length);
         var data = eexecString.slice(i + 3, i + 3 + length);
         var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4);
-        var subr = decodeCharString(encodedSubr);
+        var str = decodeCharString(encodedSubr);
 
-        subrs.push(subr);
+        subrs.push(str.charstring);
         i += 3 + length;
       } else if (inGlyphs && c == 0x52) {
         length = parseInt(length);
         var data = eexecString.slice(i + 3, i + 3 + length);
         var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4);
-        var subr = decodeCharString(encodedCharstring);
+        var str = decodeCharString(encodedCharstring);
 
         glyphs.push({
             glyph: glyph,
-            data: subr
+            data: str.charstring,
+            lsb: str.lsb,
+            width: str.width
         });
         i += 3 + length;
       } else if (inGlyphs && c == 0x2F) {
@@ -1254,16 +1528,18 @@ CFF.prototype = {
     var charstrings = [];
 
     for (var i = 0; i < glyphs.length; i++) {
-      var glyph = glyphs[i].glyph;
-      var unicode = GlyphsUnicode[glyph];
+      var glyph = glyphs[i];
+      var unicode = GlyphsUnicode[glyph.glyph];
       if (!unicode) {
-        if (glyph != ".notdef")
+        if (glyph.glyph != ".notdef")
           warn(glyph + " does not have an entry in the glyphs unicode dictionary");
       } else {
         charstrings.push({
           glyph: glyph,
           unicode: unicode,
-          charstring: glyphs[i].data
+          charstring: glyph.data,
+          width: glyph.width,
+          lsb: glyph.lsb
         });
       }
     };
@@ -1305,46 +1581,11 @@ CFF.prototype = {
     var i = 0;
     while (true) {
       var obj = charstring[i];
-      if (obj == null)
-        return [];
-
+      if (obj == undefined) {
+        error("unknow charstring command for " + i + " in " + charstring);
+      }
       if (obj.charAt) {
         switch (obj) {
-          case "callothersubr":
-            var index = charstring[i - 1];
-            var count = charstring[i - 2];
-            var data = charstring[i - 3];
-
-            // If the flex mechanishm is not used in a font program, Adobe
-            // state that that entries 0, 1 and 2 can simply be replace by
-            // {}, which means that we can simply ignore them.
-            if (index < 3) {
-              i -= 3;
-              continue;
-            }
-
-            // This is the same things about hint replacment, if it is not used
-            // entry 3 can be replaced by {}
-            if (index == 3) {
-              if (!data) {
-                charstring.splice(i - 2, 4, 3);
-                i -= 3;
-              } else {
-                // 5 to remove the arguments, the callothersubr call and the pop command
-                charstring.splice(i - 3, 5, 3);
-                i -= 3;
-              }
-            }
-            break;
-
-          case "hsbw":
-            var charWidthVector = charstring[1];
-            var leftSidebearing = charstring[0];
-
-            charstring.splice(i, 1, leftSidebearing, "hmoveto");
-            charstring.splice(0, 1);
-            break;
-
           case "endchar":
           case "return":
             // CharString is ready to be re-encode to commands number at this point
@@ -1356,7 +1597,7 @@ CFF.prototype = {
               } else if (command.charAt) {
                 var cmd = this.commandsMap[command];
                 if (!cmd)
-                  error(command);
+                  error("Unknow command: " + command);
 
                 if (IsArray(cmd)) {
                   charstring.splice(j, 1, cmd[0], cmd[1]);
@@ -1428,7 +1669,7 @@ CFF.prototype = {
       charset.push(bytes[1]);
     }
 
-    var charstringsIndex = this.createCFFIndexHeader([[0x40, 0x0E]].concat(glyphs), true);
+    var charstringsIndex = this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs), true);
 
     //Top Dict Index
     var topDictIndex = [
diff --git a/multi_page_viewer.css b/multi_page_viewer.css
index 2eaca4870..17b2537be 100644
--- a/multi_page_viewer.css
+++ b/multi_page_viewer.css
@@ -181,7 +181,7 @@ span {
   width: 200px;
   top: 62px;
   bottom: 18px;
-  left: -170px;
+  left: -140px;
   transition: left 0.25s ease-in-out 1s;
   -moz-transition: left 0.25s ease-in-out 1s;
   -webkit-transition: left 0.25s ease-in-out 1s;
diff --git a/multi_page_viewer.html b/multi_page_viewer.html
index e90606a23..df71d6690 100644
--- a/multi_page_viewer.html
+++ b/multi_page_viewer.html
@@ -27,9 +27,9 @@
       <select id="scaleSelect">
         <option value="50">50%</option>
         <option value="75">75%</option>
-        <option value="100" selected="selected">100%</option>
+        <option value="100">100%</option>
         <option value="125">125%</option>
-        <option value="150">150%</option>
+        <option value="150" selected="selected">150%</option>
         <option value="200">200%</option>
       </select>
       <span class="label">Zoom</span>
diff --git a/multi_page_viewer.js b/multi_page_viewer.js
index b2c0dc3ed..180e715eb 100644
--- a/multi_page_viewer.js
+++ b/multi_page_viewer.js
@@ -29,11 +29,15 @@ var PDFViewer = {
   scale: 1.0,
   
   pageWidth: function(page) {
-    return page.mediaBox[2] * PDFViewer.scale;
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var width = (page.mediaBox[2] - page.mediaBox[0]);
+    return width * PDFViewer.scale * pdfToCssUnitsCoef;
   },
   
   pageHeight: function(page) {
-    return page.mediaBox[3] * PDFViewer.scale;
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var height = (page.mediaBox[3] - page.mediaBox[1]);
+    return height * PDFViewer.scale * pdfToCssUnitsCoef;
   },
   
   lastPagesDrawn: [],
@@ -106,10 +110,11 @@ var PDFViewer = {
       canvas.id = 'thumbnail' + num;
       canvas.mozOpaque = true;
 
-      // Canvas dimensions must be specified in CSS pixels. CSS pixels
-      // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
-      canvas.width = 104;
-      canvas.height = 134;
+      var pageWidth = PDFViewer.pageWidth(page);
+      var pageHeight = PDFViewer.pageHeight(page);
+      var thumbScale = Math.min(104 / pageWidth, 134 / pageHeight);
+      canvas.width = pageWidth * thumbScale;
+      canvas.height = pageHeight * thumbScale;
       div.appendChild(canvas);
 
       var ctx = canvas.getContext('2d');
@@ -175,8 +180,6 @@ var PDFViewer = {
       canvas.id = 'page' + num;
       canvas.mozOpaque = true;
 
-      // Canvas dimensions must be specified in CSS pixels. CSS pixels
-      // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
       canvas.width = PDFViewer.pageWidth(page);
       canvas.height = PDFViewer.pageHeight(page);
       div.appendChild(canvas);
@@ -216,7 +219,6 @@ var PDFViewer = {
     
     if (PDFViewer.pdf) {
       for (i = 1; i <= PDFViewer.numberOfPages; i++) {
-        PDFViewer.createThumbnail(i);
         PDFViewer.createPage(i);
       }
     }
@@ -249,7 +251,10 @@ var PDFViewer = {
       PDFViewer.pageNumber = num;
       PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
       PDFViewer.willJumpToPage = true;
-      
+
+      if (document.location.hash.substr(1) == PDFViewer.pageNumber)
+        // Force a "scroll event" to redraw
+        setTimeout(window.onscroll, 0);
       document.location.hash = PDFViewer.pageNumber;
       
       PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
@@ -272,6 +277,12 @@ var PDFViewer = {
   openURL: function(url) {
     PDFViewer.url = url;
     document.title = url;
+
+    if (this.thumbsLoadingInterval) {
+      // cancel thumbs loading operations
+      clearInterval(this.thumbsLoadingInterval);
+      this.thumbsLoadingInterval = null;
+    }
     
     var req = new XMLHttpRequest();
     req.open('GET', url);
@@ -288,7 +299,9 @@ var PDFViewer = {
     
     req.send(null);
   },
-  
+
+  thumbsLoadingInterval: null,
+
   readPDF: function(data) {
     while (PDFViewer.element.hasChildNodes()) {
       PDFViewer.element.removeChild(PDFViewer.element.firstChild);
@@ -310,12 +323,22 @@ var PDFViewer = {
       PDFViewer.drawPage(1);
       document.location.hash = 1;
       
-      setTimeout(function() {
-        for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
-          PDFViewer.createThumbnail(i);
-          PDFViewer.drawThumbnail(i);
+      // slowly loading the thumbs (few per second)
+      // first time we are loading more images than subsequent
+      var currentPageIndex = 1, imagesToLoad = 15;
+      this.thumbsLoadingInterval = setInterval((function() {
+        while (imagesToLoad-- > 0) {
+          if (currentPageIndex > PDFViewer.numberOfPages) {
+            clearInterval(this.thumbsLoadingInterval);
+            this.thumbsLoadingInterval = null;
+            return;
+          }
+          PDFViewer.createThumbnail(currentPageIndex);
+          PDFViewer.drawThumbnail(currentPageIndex);
+          ++currentPageIndex;
         }
-      }, 500);
+        imagesToLoad = 3; // next time loading less images
+      }).bind(this), 500);
     }
     
     PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ? 'disabled' : '';
diff --git a/pdf.js b/pdf.js
index be471cfc0..3e3654087 100644
--- a/pdf.js
+++ b/pdf.js
@@ -641,7 +641,7 @@ var PredictorStream = (function() {
         var pixBytes = this.pixBytes = (colors * bits + 7) >> 3;
         // add an extra pixByte to represent the pixel left of column 0
         var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3;
-        
+
         DecodeStream.call(this);
         return this;
     }
@@ -3440,6 +3440,7 @@ var CanvasGraphics = (function() {
                     if (charset) {
                         assertWellFormed(IsString(charset), "invalid charset");
                         charset = charset.split("/");
+                        charset.shift();
                     }
                 } else if (IsName(encoding)) {
                     var encoding = Encodings[encoding.name];
@@ -3534,13 +3535,16 @@ var CanvasGraphics = (function() {
                 type: subType.name,
                 encoding: encodingMap,
                 charset: charset,
+                firstChar: fontDict.get("FirstChar"),
+                lastChar: fontDict.get("LastChar"),
                 bbox: descriptor.get("FontBBox"),
                 ascent: descriptor.get("Ascent"),
                 descent: descriptor.get("Descent"),
                 xHeight: descriptor.get("XHeight"),
                 capHeight: descriptor.get("CapHeight"),
                 flags: descriptor.get("Flags"),
-                italicAngle: descriptor.get("ItalicAngle")
+                italicAngle: descriptor.get("ItalicAngle"),
+                fixedPitch: false
             };
 
             return {
@@ -3807,18 +3811,22 @@ var CanvasGraphics = (function() {
             if (fontDescriptor && fontDescriptor.num) {
                 var fontDescriptor = this.xref.fetchIfRef(fontDescriptor);
                 fontName = fontDescriptor.get("FontName").name.replace("+", "_");
-                Fonts.active = fontName;
             }
 
             if (!fontName) {
                 // TODO: fontDescriptor is not available, fallback to default font
-                this.current.fontSize = size;
-                this.ctx.font = this.current.fontSize + 'px sans-serif';
-                return;
+                fontName = "sans-serif";
             }
 
+            this.current.fontName = fontName;
             this.current.fontSize = size;
-            this.ctx.font = this.current.fontSize +'px "' + fontName + '", Symbol';
+
+            if (this.ctx.$setFont) {
+              this.ctx.$setFont(fontName, size);
+            } else {
+              this.ctx.font = size + 'px "' + fontName + '"';
+              Fonts.setActive(fontName, size);
+            }
         },
         setTextRenderingMode: function(mode) {
             TODO("text rendering mode");
@@ -3862,7 +3870,7 @@ var CanvasGraphics = (function() {
                 text = Fonts.charsToUnicode(text);
                 this.ctx.translate(this.current.x, -1 * this.current.y);
                 this.ctx.fillText(text, 0, 0);
-                this.current.x += this.ctx.measureText(text).width;
+                this.current.x += Fonts.measureText(text);
             }
 
             this.ctx.restore();
diff --git a/test/test_slave.html b/test/test_slave.html
index d70e362af..0a330e703 100644
--- a/test/test_slave.html
+++ b/test/test_slave.html
@@ -4,6 +4,7 @@
   <style type="text/css"></style>
   <script type="text/javascript" src="/pdf.js"></script>
   <script type="text/javascript" src="/fonts.js"></script>
+  <script type="text/javascript" src="/crypto.js"></script>
   <script type="text/javascript" src="/glyphlist.js"></script>
   <script type="application/javascript">
 var appPath, browser, canvas, currentTask, currentTaskIdx, failure, manifest, numPages, pdfDoc, stdout;
@@ -103,11 +104,12 @@ function nextPage() {
   }
 
   try {
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
     // using mediaBox for the canvas size
-    var wTwips = (currentPage.mediaBox[2] - currentPage.mediaBox[0]);
-    var hTwips = (currentPage.mediaBox[3] - currentPage.mediaBox[1]);
-    canvas.width = wTwips * 96.0 / 72.0;
-    canvas.height = hTwips * 96.0 / 72.0;
+    var pageWidth = (currentPage.mediaBox[2] - currentPage.mediaBox[0]);
+    var pageHeight = (currentPage.mediaBox[3] - currentPage.mediaBox[1]);
+    canvas.width = pageWidth * pdfToCssUnitsCoef;
+    canvas.height = pageHeight * pdfToCssUnitsCoef;
     clear(ctx);
   } catch(e) {
     failure = 'page setup: '+ e.toString();
diff --git a/viewer.css b/viewer.css
index 857eed101..9987492dc 100644
--- a/viewer.css
+++ b/viewer.css
@@ -24,10 +24,11 @@ span#info {
 }
 
 #viewer {
+}
+
+#canvas {
     margin: auto;
-    border: 1px solid black;
-    width: 12.75in;
-    height: 16.5in;
+    display: block;
 }
 
 #pageNumber {
diff --git a/viewer.html b/viewer.html
index 24213f7d6..c600547f0 100644
--- a/viewer.html
+++ b/viewer.html
@@ -25,9 +25,7 @@
     </div>
 
     <div id="viewer">
-      <!-- Canvas dimensions must be specified in CSS pixels.  CSS pixels
-           are always 96 dpi.  816x1056 is 8.5x11in at 96dpi. -->
-      <canvas id="canvas" width="816" height="1056" defaultwidth="816" defaultheight="1056"></canvas>
+      <canvas id="canvas"></canvas>
     </div>
   </body>
 </html>
diff --git a/viewer.js b/viewer.js
index 5db2effda..38b0537b1 100644
--- a/viewer.js
+++ b/viewer.js
@@ -60,12 +60,12 @@ function displayPage(num) {
     var t0 = Date.now();
 
     var page = pdfDocument.getPage(pageNum = num);
-    canvas.width = parseInt(canvas.getAttribute("defaultwidth")) * pageScale;
-    canvas.height = parseInt(canvas.getAttribute("defaultheight")) * pageScale;
 
-    // scale canvas by 2
-    canvas.width = 2 * page.mediaBox[2];
-    canvas.hieght = 2 * page.mediaBox[3];
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var pageWidth = (page.mediaBox[2] - page.mediaBox[0]);
+    var pageHeight = (page.mediaBox[3] - page.mediaBox[1]);
+    canvas.width = pageScale * pageWidth * pdfToCssUnitsCoef;
+    canvas.height = pageScale * pageHeight * pdfToCssUnitsCoef;
 
     var t1 = Date.now();
     var ctx = canvas.getContext("2d");
diff --git a/viewer_worker.html b/viewer_worker.html
index 51f2b9d8a..89fb8a087 100644
--- a/viewer_worker.html
+++ b/viewer_worker.html
@@ -1,7 +1,10 @@
 <html>
     <head>
         <title>Simple pdf.js page worker viewer</title>
-        <script type="text/javascript" src="worker_client.js"></script>
+        <script type="text/javascript" src="fonts.js"></script>
+        <script type="text/javascript" src="glyphlist.js"></script>
+        <script type="text/javascript" src="pdf.js"></script>
+        <script type="text/javascript" src="worker/client.js"></script>
 <script>
 
 
@@ -36,10 +39,7 @@ window.onload = function() {
     </div>
 
     <div id="viewer">
-      <!-- Canvas dimensions must be specified in CSS pixels.  CSS pixels
-           are always 96 dpi.  816x1056 is 8.5x11in at 96dpi. -->
-      <!-- We're rendering here at 1.5x scale. -->
-      <canvas id="canvas" width="1224" height="1584"></canvas>
+      <canvas id="canvas"></canvas>
     </div>
   </body>
 </html>
diff --git a/canvas_proxy.js b/worker/canvas.js
similarity index 99%
rename from canvas_proxy.js
rename to worker/canvas.js
index d6f5a0a25..bc96d8453 100644
--- a/canvas_proxy.js
+++ b/worker/canvas.js
@@ -119,7 +119,8 @@ function CanvasProxy(width, height) {
   "$addCurrentX",
   "$saveCurrentX",
   "$restoreCurrentX",
-  "$showText"
+  "$showText",
+  "$setFont"
   ];
 
   function buildFuncCall(name) {
diff --git a/worker_client.js b/worker/client.js
similarity index 60%
rename from worker_client.js
rename to worker/client.js
index 359a5f7c1..fd98b857e 100644
--- a/worker_client.js
+++ b/worker/client.js
@@ -18,12 +18,115 @@ if (typeof console.time == "undefined") {
   };
 }
 
+function FontWorker() {
+  this.worker = new Worker("worker/font.js");
+  this.fontsWaiting = 0;
+  this.fontsWaitingCallbacks = [];
+
+  // Listen to the WebWorker for data and call actionHandler on it.
+  this.worker.onmessage = function(event) {
+    var data = event.data;
+    var actionHandler = this.actionHandler
+    if (data.action in actionHandler) {
+      actionHandler[data.action].call(this, data.data);
+    } else {
+      throw "Unkown action from worker: " + data.action;
+    }
+  }.bind(this);
+  
+  this.$handleFontLoadedCallback = this.handleFontLoadedCallback.bind(this);
+}
+
+FontWorker.prototype = {
+  handleFontLoadedCallback: function() {
+    // Decrease the number of fonts wainting to be loaded.
+    this.fontsWaiting--;
+    // If all fonts are available now, then call all the callbacks.
+    if (this.fontsWaiting == 0) {
+      var callbacks = this.fontsWaitingCallbacks;
+      for (var i = 0; i < callbacks.length; i++) {
+        callbacks[i]();
+      }
+      this.fontsWaitingCallbacks.length = 0;
+    }
+  },
+  
+  actionHandler: {
+    "log": function(data) {
+      console.log.apply(console, data);
+    },
+    
+    "fonts": function(data) {
+      // console.log("got processed fonts from worker", Object.keys(data));
+      for (name in data) {
+        // Update the encoding property.
+        var font = Fonts.lookup(name);
+        font.properties = {
+          encoding: data[name].encoding
+        }
+
+        // Call `Font.prototype.bindDOM` to make the font get loaded on the page.
+        Font.prototype.bindDOM.call(
+          font,
+          data[name].str,
+          // IsLoadedCallback.
+          this.$handleFontLoadedCallback
+        );
+      }
+    }
+  },
+
+  ensureFonts: function(data, callback) {
+    var font;
+    var notLoaded = [];
+    for (var i = 0; i < data.length; i++) {
+      font = data[i];
+      if (Fonts[font.name]) {
+        continue;
+      }
+    
+      // Register the font but don't pass in any real data. The idea is to
+      // store as less data as possible to reduce memory usage.
+      Fonts.registerFont(font.name, Object.create(null), Object.create(null));
+
+      // Mark this font to be handled later.
+      notLoaded.push(font);
+      // Increate the number of fonts to wait for.
+      this.fontsWaiting++;
+    }
+    
+    console.time("ensureFonts");
+    // If there are fonts, that need to get loaded, tell the FontWorker to get
+    // started and push the callback on the waiting-callback-stack.
+    if (notLoaded.length != 0) {
+      console.log("fonts -> FontWorker");
+      // Send the worker the fonts to work on.
+      this.worker.postMessage({
+        action: "fonts",
+        data:   notLoaded
+      });
+      if (callback) {
+        this.fontsWaitingCallbacks.push(callback);
+      }
+    }
+    // All fonts are present? Well, then just call the callback if there is one.
+    else {
+      if (callback) {
+        callback();
+      }
+    }
+  },
+}
+
 function WorkerPDFDoc(canvas) {
   var timer = null
 
   this.ctx = canvas.getContext("2d");
   this.canvas = canvas;
-  this.worker = new Worker('pdf_worker.js');
+  this.worker = new Worker('worker/pdf.js');
+  this.fontWorker = new FontWorker();
+  this.waitingForFonts = false;
+  this.waitingForFontsCallback = [];
 
   this.numPage = 1;
   this.numPages = null;
@@ -56,6 +159,7 @@ function WorkerPDFDoc(canvas) {
     },
 
     "$showText": function(y, text) {
+      text = Fonts.charsToUnicode(text);
       this.translate(currentX, -1 * y);
       this.fillText(text, 0, 0);
       currentX += this.measureText(text).width;
@@ -136,6 +240,11 @@ function WorkerPDFDoc(canvas) {
         throw "Pattern not found";
       }
       this.strokeStyle = pattern;
+    },
+
+    "$setFont": function(name, size) {
+      this.font = size + 'px "' + name + '"';
+      Fonts.setActive(name, size);
     }
   }
 
@@ -187,6 +296,25 @@ function WorkerPDFDoc(canvas) {
       div.setAttribute("style", style);
       document.body.appendChild(div);
     },
+    
+    "setup_page": function(data) {
+      var size = data.split(",");
+      var canvas = this.canvas, ctx = this.ctx;
+      canvas.width = parseInt(size[0]);
+      canvas.height = parseInt(size[1]);
+    },
+
+    "fonts": function(data) {
+      this.waitingForFonts = true;
+      this.fontWorker.ensureFonts(data, function() {
+        this.waitingForFonts = false;
+        var callbacks = this.waitingForFontsCallback;
+        for (var i = 0; i < callbacks.length; i++) {
+          callbacks[i]();
+        }
+        this.waitingForFontsCallback.length = 0;
+      }.bind(this));
+    },
 
     "jpeg_stream": function(data) {
       var img = new Image();
@@ -207,11 +335,9 @@ function WorkerPDFDoc(canvas) {
           canvasList[id] = newCanvas;
         }
 
-        // There might be fonts that need to get loaded. Shedule the
-        // rendering at the end of the event queue ensures this.
-        setTimeout(function() {
+        var renderData = function() {
           if (id == 0) {
-            console.time("canvas rendering");
+            console.time("main canvas rendering");
             var ctx = this.ctx;
             ctx.save();
             ctx.fillStyle = "rgb(255, 255, 255)";
@@ -219,12 +345,27 @@ function WorkerPDFDoc(canvas) {
             ctx.restore();
           }
           renderProxyCanvas(canvasList[id], cmdQueue);
-          if (id == 0) console.timeEnd("canvas rendering")
-        }, 0, this);
+          if (id == 0) {
+            console.timeEnd("main canvas rendering");
+            console.timeEnd(">>> total page display time:");
+          }
+        }.bind(this);
+
+        if (this.waitingForFonts) {
+          if (id == 0) {
+            console.log("want to render, but not all fonts are there", id);
+            this.waitingForFontsCallback.push(renderData);
+          } else {
+            // console.log("assume canvas doesn't have fonts", id);
+            renderData();
+          }
+        } else {
+          renderData();
+        }
     }
   }
 
-  // List to the WebWorker for data and call actionHandler on it.
+  // Listen to the WebWorker for data and call actionHandler on it.
   this.worker.onmessage = function(event) {
     var data = event.data;
     if (data.action in actionHandler) {
@@ -232,7 +373,7 @@ function WorkerPDFDoc(canvas) {
     } else {
       throw "Unkown action from worker: " + data.action;
     }
-  }
+  }.bind(this)
 }
 
 WorkerPDFDoc.prototype.open = function(url, callback) {
@@ -255,6 +396,8 @@ WorkerPDFDoc.prototype.open = function(url, callback) {
 
 WorkerPDFDoc.prototype.showPage = function(numPage) {
   this.numPage = parseInt(numPage);
+  console.log("=== start rendering page " + numPage + " ===");
+  console.time(">>> total page display time:");
   this.worker.postMessage(numPage);
   if (this.onChangePage) {
     this.onChangePage(numPage);
diff --git a/worker/console.js b/worker/console.js
new file mode 100644
index 000000000..e544db7a2
--- /dev/null
+++ b/worker/console.js
@@ -0,0 +1,27 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+"use strict";
+
+var consoleTimer = {};
+var console = {
+  log: function log() {
+    var args = Array.prototype.slice.call(arguments);
+    postMessage({
+      action: "log",
+      data: args
+    });
+  },
+  
+  time: function(name) {
+    consoleTimer[name] = Date.now();
+  },
+  
+  timeEnd: function(name) {
+    var time = consoleTimer[name];
+    if (time == null) {
+      throw "Unkown timer name " + name;
+    }
+    this.log("Timer:", name, Date.now() - time);
+  }
+}
diff --git a/worker/font.js b/worker/font.js
new file mode 100644
index 000000000..a8ec5ebd4
--- /dev/null
+++ b/worker/font.js
@@ -0,0 +1,65 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+
+"use strict";
+
+importScripts("console.js");
+
+importScripts("../pdf.js");
+importScripts("../fonts.js");
+importScripts("../glyphlist.js")
+
+function fontDataToString(font) {
+  // Doing postMessage on objects make them lose their "shape". This adds the
+  // "shape" for all required objects agains, such that the encoding works as
+  // expected.
+  var fontFileDict = new Dict();
+  fontFileDict.map = font.file.dict.map;
+
+  var fontFile = new Stream(font.file.bytes, font.file.start, font.file.end - font.file.start, fontFileDict);
+  font.file = new FlateStream(fontFile);
+  
+  // This will encode the font.
+  var fontObj = new Font(font.name, font.file, font.properties);
+
+  // Create string that is used for css later.
+  var str = "";
+  var data = fontObj.data;
+  var length = data.length;
+  for (var j = 0; j < length; j++)
+    str += String.fromCharCode(data[j]);
+  
+  return {
+    str:      str,
+    encoding: font.properties.encoding
+  }
+}
+
+/**
+* Functions to handle data sent by the MainThread.
+*/
+var actionHandler = {
+  "fonts": function(data) {
+    var fontData;
+    var result = {};
+    for (var i = 0; i < data.length; i++) {
+      fontData = data[i];
+      result[fontData.name] = fontDataToString(fontData);
+    }
+    
+    postMessage({
+      action: "fonts",
+      data:   result
+    })
+  },
+}
+
+// Listen to the MainThread for data and call actionHandler on it.
+this.onmessage = function(event) {
+  var data = event.data;
+  if (data.action in actionHandler) {
+    actionHandler[data.action].call(this, data.data);
+  } else {
+    throw "Unkown action from worker: " + data.action;
+  }
+}
\ No newline at end of file
diff --git a/pdf_worker.js b/worker/pdf.js
similarity index 72%
rename from pdf_worker.js
rename to worker/pdf.js
index fa29428c7..a1f18f694 100644
--- a/pdf_worker.js
+++ b/worker/pdf.js
@@ -27,10 +27,12 @@ var console = {
 }
 
 //
-importScripts("canvas_proxy.js");
-importScripts("pdf.js");
-importScripts("fonts.js");
-importScripts("glyphlist.js")
+importScripts("console.js")
+importScripts("canvas.js");
+importScripts("../pdf.js");
+importScripts("../fonts.js");
+importScripts("../crypto.js");
+importScripts("../glyphlist.js")
 
 // Use the JpegStreamProxy proxy.
 JpegStream = JpegStreamProxy;
@@ -58,6 +60,18 @@ onmessage = function(event) {
     // Let's try to render the first page...
     var page = pdfDocument.getPage(parseInt(data));
 
+    var pdfToCssUnitsCoef = 96.0 / 72.0;
+    var pageWidth = (page.mediaBox[2] - page.mediaBox[0]) * pdfToCssUnitsCoef;
+    var pageHeight = (page.mediaBox[3] - page.mediaBox[1]) * pdfToCssUnitsCoef;
+    postMessage({
+      action: "setup_page",
+      data: pageWidth + "," + pageHeight
+    });
+
+    // Set canvas size.
+    canvas.width = pageWidth;
+    canvas.height = pageHeight;
+
     // page.compile will collect all fonts for us, once we have loaded them
     // we can trigger the actual page rendering with page.display
     var fonts = [];
@@ -65,21 +79,14 @@ onmessage = function(event) {
     page.compile(gfx, fonts);
     console.timeEnd("compile");
 
+    // Send fonts to the main thread.
     console.time("fonts");
-    // Inspect fonts and translate the missing one.
-    var count = fonts.length;
-    for (var i = 0; i < count; i++) {
-      var font = fonts[i];
-      if (Fonts[font.name]) {
-        fontsReady = fontsReady && !Fonts[font.name].loading;
-        continue;
-      }
-
-      // This "builds" the font and sents it over to the main thread.
-      new Font(font.name, font.file, font.properties);
-    }
+    postMessage({
+      action: "fonts",
+      data:   fonts
+    });
     console.timeEnd("fonts");
-
+    
     console.time("display");
     page.display(gfx);
     canvas.flush();