diff --git a/fonts.js b/fonts.js
index ad3d4fd35..d5943b7a3 100644
--- a/fonts.js
+++ b/fonts.js
@@ -103,7 +103,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 || properties.type == "TrueType" || kDisableFonts) {
+    if (properties.ignore || kDisableFonts) {
       Fonts[name] = {
         data: file,
         loading: false,
@@ -242,7 +242,7 @@ var Font = (function () {
     return ranges;
   };
 
-  function createCMAPTable(glyphs) {
+  function createCMapTable(glyphs) {
     var ranges = getRanges(glyphs);
 
     var headerSize = (12 * 2 + (ranges.length * 4 * 2));
@@ -274,7 +274,7 @@ var Font = (function () {
     var bias = 0;
     for (var i = 0; i < segCount - 1; i++) {
       var range = ranges[i];
-       var start = range[0];
+      var start = range[0];
       var end = range[1];
       var delta = (((start - 1) - bias) ^ 0xffff) + 1;
       bias += (end - start + 1);
@@ -284,8 +284,8 @@ var Font = (function () {
       idDeltas += string16(delta);
       idRangeOffsets += string16(0);
 
-      for (var j = start; j <= end; j++)
-        glyphsIds += String.fromCharCode(j);
+      for (var j = 0; j < range.length; j++)
+        glyphsIds += String.fromCharCode(range[j]);
     }
 
     startCount += "\xFF\xFF";
@@ -368,11 +368,11 @@ var Font = (function () {
         var length = FontsUtils.bytesToInteger(file.getBytes(4));
 
         // Read the table associated data
-        var currentPosition = file.pos;
-        file.pos = file.start + offset;
-
+        var previousPosition = file.pos;
+        file.pos = file.start ? file.start : 0;
+        file.skip(offset);
         var data = file.getBytes(length);
-        file.pos = currentPosition;
+        file.pos = previousPosition;
 
         return {
           tag: tag,
@@ -393,6 +393,76 @@ var Font = (function () {
         }
       };
 
+      function replaceCMapTable(cmap, font, properties) {
+        var version = FontsUtils.bytesToInteger(font.getBytes(2));
+        var numTables = FontsUtils.bytesToInteger(font.getBytes(2));
+
+        for (var i = 0; i < numTables; i++) {
+          var platformID = FontsUtils.bytesToInteger(font.getBytes(2));
+          var encodingID = FontsUtils.bytesToInteger(font.getBytes(2));
+          var offset = FontsUtils.bytesToInteger(font.getBytes(4));
+          var format = FontsUtils.bytesToInteger(font.getBytes(2));
+          var length = FontsUtils.bytesToInteger(font.getBytes(2));
+          var language = FontsUtils.bytesToInteger(font.getBytes(2));
+
+          if ((format == 0 && numTables == 1) || 
+              (format == 6 && numTables == 1 && !properties.encoding.empty)) {
+            // Format 0 alone is not allowed by the sanitizer so let's rewrite
+            // that to a 3-1-4 Unicode BMP table
+            var charset = properties.charset;
+            var glyphs = [];
+            for (var j = 0; j < charset.length; j++) {
+              glyphs.push({
+                unicode: GlyphsUnicode[charset[j]] || 0
+              });
+            }
+
+            cmap.data = createCMapTable(glyphs);
+          } else if (format == 6 && numTables == 1) {
+            // Format 6 is a 2-bytes dense mapping, which means the font data
+            // lives glue together even if they are pretty far in the unicode
+            // table. (This looks weird, so I can have missed something), this
+            // works on Linux but seems to fails on Mac so let's rewrite the
+            // cmap table to a 3-1-4 style
+            var firstCode = FontsUtils.bytesToInteger(font.getBytes(2));
+            var entryCount = FontsUtils.bytesToInteger(font.getBytes(2));
+
+            var glyphs = [];
+            var min = 0xffff, max = 0;
+            for (var j = 0; j < entryCount; j++) {
+              var charcode = FontsUtils.bytesToInteger(font.getBytes(2));
+              glyphs.push(charcode);
+
+              if (charcode < min)
+                min = charcode;
+              if (charcode > max)
+                max = charcode;
+            }
+
+            // Since Format 6 is a dense array, check for gaps
+            for (var j = min; j < max; j++) {
+              if (glyphs.indexOf(j) == -1)
+                glyphs.push(j);
+            }
+
+            for (var j = 0; j < glyphs.length; j++)
+              glyphs[j] = { unicode: glyphs[j] + firstCode };
+
+            var ranges= getRanges(glyphs);
+            assert(ranges.length == 1, "Got " + ranges.length + " ranges in a dense array");
+
+            var encoding = properties.encoding;
+            var denseRange = ranges[0];
+            var start = denseRange[0];
+            var end = denseRange[1];
+            var index = firstCode;
+            for (var j = start; j <= end; j++)
+              encoding[index++] = glyphs[j - firstCode - 1].unicode;
+            cmap.data = createCMapTable(glyphs);
+          }
+        }
+      };
+
       // Check that required tables are present
       var requiredTables = [ "OS/2", "cmap", "head", "hhea",
                              "hmtx", "maxp", "name", "post" ];
@@ -425,7 +495,7 @@ var Font = (function () {
       if (requiredTables.length && requiredTables[0] == "OS/2") {
         // Create a new file to hold the new version of our truetype with a new
         // header and new offsets
-        var ttf = Uint8Array(kMaxFontFileSize);
+        var ttf = new Uint8Array(kMaxFontFileSize);
 
         // The offsets object holds at the same time a representation of where
         // to write the table entry information about a table and another offset
@@ -448,18 +518,8 @@ var Font = (function () {
           data: OS2
         });
 
-        // If the font is missing a OS/2 table it's could be an old mac font
-        // without a 3-1-4 Unicode BMP table, so let's rewrite it.
-        var charset = properties.charset;
-        var glyphs = [];
-        for (var i = 0; i < charset.length; i++) {
-          glyphs.push({
-            unicode: GlyphsUnicode[charset[i]]
-          });
-        }
-
         // Replace the old CMAP table with a shiny new one
-        cmap.data = createCMAPTable(glyphs);
+        replaceCMapTable(cmap, font, properties);
 
         // Rewrite the 'post' table if needed
         if (!post) {
@@ -521,7 +581,7 @@ var Font = (function () {
     },
 
     convert: function font_convert(name, font, properties) {
-      var otf = Uint8Array(kMaxFontFileSize);
+      var otf = new Uint8Array(kMaxFontFileSize);
 
       function createNameTable(name) {
         var names = [
@@ -599,7 +659,7 @@ var Font = (function () {
       var charstrings = font.getOrderedCharStrings(properties.glyphs);
 
       /** CMAP */
-      cmap = createCMAPTable(charstrings);
+      cmap = createCMapTable(charstrings);
       createTableEntry(otf, offsets, "cmap", cmap);
 
       /** HEAD */
@@ -1088,7 +1148,7 @@ var Type1Parser = function() {
  * The CFF class takes a Type1 file and wrap it into a 'Compact Font Format',
  * which itself embed Type2 charstrings.
  */
-const CFFStrings = [
+var CFFStrings = [
   ".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand",
   "quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period",
   "slash","zero","one","two","three","four","five","six","seven","eight","nine",
diff --git a/pdf.js b/pdf.js
index a43dcf559..d8891a380 100644
--- a/pdf.js
+++ b/pdf.js
@@ -58,7 +58,7 @@ function bytesToString(bytes) {
 
 var Stream = (function() {
     function constructor(arrayBuffer, start, length, dict) {
-        this.bytes = Uint8Array(arrayBuffer);
+        this.bytes = new Uint8Array(arrayBuffer);
         this.start = start || 0;
         this.pos = this.start;
         this.end = (start + length) || this.bytes.length;
@@ -125,7 +125,7 @@ var Stream = (function() {
 var StringStream = (function() {
     function constructor(str) {
         var length = str.length;
-        var bytes = Uint8Array(length);
+        var bytes = new Uint8Array(length);
         for (var n = 0; n < length; ++n)
             bytes[n] = str.charCodeAt(n);
         Stream.call(this, bytes);
@@ -154,7 +154,7 @@ var DecodeStream = (function() {
             var size = 512;
             while (size < requested)
                 size <<= 1;
-            var buffer2 = Uint8Array(size);
+            var buffer2 = new Uint8Array(size);
             for (var i = 0; i < current; ++i)
                 buffer2[i] = buffer[i];
             return this.buffer = buffer2;
@@ -220,13 +220,50 @@ var DecodeStream = (function() {
 })();
 
 
+var FakeStream = (function() {
+    function constructor(stream) {
+        this.dict = stream.dict;
+        DecodeStream.call(this);
+    };
+
+    constructor.prototype = Object.create(DecodeStream.prototype);
+    constructor.prototype.readBlock = function() {
+        var bufferLength = this.bufferLength;
+        bufferLength += 1024;
+        var buffer = this.ensureBuffer(bufferLength);
+        this.bufferLength = bufferLength;
+    };
+    constructor.prototype.getBytes = function(length) {
+        var pos = this.pos;
+
+        if (length) {
+            this.ensureBuffer(pos + length);
+            var end = pos + length;
+
+            while (!this.eof && this.bufferLength < end)
+                this.readBlock();
+
+            var bufEnd = this.bufferLength;
+            if (end > bufEnd)
+                end = bufEnd;
+        } else {
+            this.eof = true;
+            var end = this.bufferLength;
+        }
+
+        this.pos = end;
+        return this.buffer.subarray(pos, end)
+    };
+
+    return constructor;
+})();
 
 var FlateStream = (function() {
-    const codeLenCodeMap = Uint32Array([
+    var codeLenCodeMap = new Uint32Array([
         16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
     ]);
 
-    const lengthDecode = Uint32Array([
+    var lengthDecode = new Uint32Array([
         0x00003, 0x00004, 0x00005, 0x00006, 0x00007, 0x00008, 0x00009,
         0x0000a, 0x1000b, 0x1000d, 0x1000f, 0x10011, 0x20013, 0x20017,
         0x2001b, 0x2001f, 0x30023, 0x3002b, 0x30033, 0x3003b, 0x40043,
@@ -234,7 +271,7 @@ var FlateStream = (function() {
         0x00102, 0x00102, 0x00102
     ]);
 
-    const distDecode = Uint32Array([
+    var distDecode = new Uint32Array([
         0x00001, 0x00002, 0x00003, 0x00004, 0x10005, 0x10007, 0x20009,
         0x2000d, 0x30011, 0x30019, 0x40021, 0x40031, 0x50041, 0x50061,
         0x60081, 0x600c1, 0x70101, 0x70181, 0x80201, 0x80301, 0x90401,
@@ -242,7 +279,7 @@ var FlateStream = (function() {
         0xd4001, 0xd6001
     ]);
 
-    const fixedLitCodeTab = [Uint32Array([
+    var fixedLitCodeTab = [new Uint32Array([
         0x70100, 0x80050, 0x80010, 0x80118, 0x70110, 0x80070, 0x80030,
         0x900c0, 0x70108, 0x80060, 0x80020, 0x900a0, 0x80000, 0x80080,
         0x80040, 0x900e0, 0x70104, 0x80058, 0x80018, 0x90090, 0x70114,
@@ -319,7 +356,7 @@ var FlateStream = (function() {
         0x900ff
     ]), 9];
 
-    const fixedDistCodeTab = [Uint32Array([
+    var fixedDistCodeTab = [new Uint32Array([
         0x50000, 0x50010, 0x50008, 0x50018, 0x50004, 0x50014, 0x5000c,
         0x5001c, 0x50002, 0x50012, 0x5000a, 0x5001a, 0x50006, 0x50016,
         0x5000e, 0x00000, 0x50001, 0x50011, 0x50009, 0x50019, 0x50005,
@@ -410,7 +447,7 @@ var FlateStream = (function() {
 
         // build the table
         var size = 1 << maxLen;
-        var codes = Uint32Array(size);
+        var codes = new Uint32Array(size);
         for (var len = 1, code = 0, skip = 2;
                 len <= maxLen;
                 ++len, code <<= 1, skip <<= 1) {
@@ -597,9 +634,6 @@ var PredictorStream = (function() {
     constructor.prototype = Object.create(DecodeStream.prototype);
 
     constructor.prototype.readBlockTiff = function() {
-        var buffer = this.buffer;
-        var pos = this.pos;
-
         var rowBytes = this.rowBytes;
         var pixBytes = this.pixBytes;
 
@@ -660,9 +694,6 @@ var PredictorStream = (function() {
         this.bufferLength += rowBytes;
     };
     constructor.prototype.readBlockPng = function() {
-        var buffer = this.buffer;
-        var pos = this.pos;
-
         var rowBytes = this.rowBytes;
         var pixBytes = this.pixBytes;
 
@@ -782,8 +813,8 @@ var Ascii85Stream = (function() {
 
     constructor.prototype = Object.create(DecodeStream.prototype);
     constructor.prototype.readBlock = function() {
-        const tildaCode = "~".charCodeAt(0);
-        const zCode = "z".charCodeAt(0);
+        var tildaCode = "~".charCodeAt(0);
+        var zCode = "z".charCodeAt(0);
         var str = this.str;
 
         var c = str.getByte();
@@ -1945,10 +1976,10 @@ var Lexer = (function() {
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0    // fx
     ];
 
-    const MIN_INT = (1<<31) | 0;
-    const MAX_INT = (MIN_INT - 1) | 0;
-    const MIN_UINT = 0;
-    const MAX_UINT = ((1<<30) * 4) - 1;
+    var MIN_INT = (1<<31) | 0;
+    var MAX_INT = (MIN_INT - 1) | 0;
+    var MIN_UINT = 0;
+    var MAX_UINT = ((1<<30) * 4) - 1;
 
     function ToHexDigit(ch) {
         if (ch >= "0" && ch <= "9")
@@ -2392,6 +2423,9 @@ var Parser = (function() {
                 return new JpegStream(bytes, stream.dict);
             } else if (name == "ASCII85Decode") {
                 return new Ascii85Stream(stream);
+            } else if (name == "CCITTFaxDecode") {
+                TODO("implement fax stream");
+                return new FakeStream(stream);
             } else {
                 error("filter '" + name + "' not supported yet");
             }
@@ -2975,7 +3009,7 @@ var PDFDoc = (function() {
     return constructor;
 })();
 
-const IDENTITY_MATRIX = [ 1, 0, 0, 1, 0, 0 ];
+var IDENTITY_MATRIX = [ 1, 0, 0, 1, 0, 0 ];
 
 // <canvas> contexts store most of the state we need natively.
 // However, PDF needs a bit more state, which we store here.
@@ -2999,7 +3033,7 @@ var CanvasExtraState = (function() {
     return constructor;
 })();
 
-const Encodings = {
+var Encodings = {
   get ExpertEncoding() {
     return shadow(this, "ExpertEncoding", [ ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
       "space","exclamsmall","Hungarumlautsmall",,"dollaroldstyle","dollarsuperior",
@@ -3277,13 +3311,13 @@ var CanvasGraphics = (function() {
         };
     }
 
-    const LINE_CAP_STYLES = [ "butt", "round", "square" ];
-    const LINE_JOIN_STYLES = [ "miter", "round", "bevel" ];
-    const NORMAL_CLIP = {};
-    const EO_CLIP = {};
+    var LINE_CAP_STYLES = [ "butt", "round", "square" ];
+    var LINE_JOIN_STYLES = [ "miter", "round", "bevel" ];
+    var NORMAL_CLIP = {};
+    var EO_CLIP = {};
 
     // Used for tiling patterns
-    const PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
+    var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
 
     constructor.prototype = {
         translateFont: function(fontDict, xref, resources) {
@@ -3306,7 +3340,7 @@ var CanvasGraphics = (function() {
             // Fonts with an embedded cmap but without any assignment in
             // it are not yet supported, so ask the fonts loader to ignore
             // them to not pay a stupid one sec latence.
-            var ignoreFont = true;
+            var ignoreFont = false;
 
             var encodingMap = {};
             var charset = [];
@@ -3350,20 +3384,24 @@ var CanvasGraphics = (function() {
                     }
                 }
             } else if (fontDict.has("ToUnicode")) {
+                encodingMap = {empty: true};
                 var cmapObj = xref.fetchIfRef(fontDict.get("ToUnicode"));
                 if (IsName(cmapObj)) {
                     error("ToUnicode file cmap translation not implemented");
                 } else if (IsStream(cmapObj)) {
                     var encoding = Encodings["WinAnsiEncoding"];
                     var firstChar = xref.fetchIfRef(fontDict.get("FirstChar"));
-                    for (var i = firstChar; i < encoding.length; i++)
-                        encodingMap[i] = new Name(encoding[i]);
 
                     var tokens = [];
                     var token = "";
 
-                    var buffer = cmapObj.ensureBuffer ? cmapObj.ensureBuffer() : cmapObj;
-                    var cmap = cmapObj.getBytes(buffer.byteLength);
+                    var length = cmapObj.length;
+                    if (cmapObj instanceof FlateStream) {
+                      cmapObj.readBlock();
+                      length = cmapObj.bufferLength;
+                    }
+
+                    var cmap = cmapObj.getBytes(length);
                     for (var i =0; i < cmap.length; i++) {
                       var byte = cmap[i];
                       if (byte == 0x20 || byte == 0x0A || byte == 0x3C || byte == 0x3E) {
@@ -3390,8 +3428,10 @@ var CanvasGraphics = (function() {
                               var code = parseInt("0x" + tokens[j+2]);
 
                               for (var k = startRange; k <= endRange; k++) {
-                                encodingMap[k] = GlyphsUnicode[encoding[code]];
-                                charset.push(encoding[code++]);
+                                // The encoding mapping table will be filled
+                                // later during the building phase
+                                //encodingMap[k] = GlyphsUnicode[encoding[code]];
+                                charset.push(encoding[code++] || ".notdef");
                               }
                             }
                             break;
@@ -3696,7 +3736,7 @@ var CanvasGraphics = (function() {
             }
 
             this.current.fontSize = size;
-            this.ctx.font = this.current.fontSize +'px "' + fontName + '"';
+            this.ctx.font = this.current.fontSize +'px "' + fontName + '", Symbol';
         },
         setTextRenderingMode: function(mode) {
             TODO("text rendering mode");
@@ -3725,7 +3765,9 @@ var CanvasGraphics = (function() {
             this.ctx.transform.apply(this.ctx, this.current.textMatrix);
             this.ctx.scale(1, -1);
             this.ctx.translate(0, -2 * this.current.y);
-            this.ctx.fillText(Fonts.charsToUnicode(text), this.current.x, this.current.y);
+
+            text = Fonts.charsToUnicode(text);
+            this.ctx.fillText(text, this.current.x, this.current.y);
             this.current.x += this.ctx.measureText(text).width;
 
             this.ctx.restore();
@@ -3812,7 +3854,7 @@ var CanvasGraphics = (function() {
 
                     var pattern = xref.fetchIfRef(patternRes.get(patternName.name));
                     var patternDict = IsStream(pattern) ? pattern.dict : pattern;
-                    const types = [null, this.tilingFill,
+                    var types = [null, this.tilingFill,
                                    function() { TODO("Shading Patterns"); }];
                     var typeNum = patternDict.get("PatternType");
                     var patternFn = types[typeNum];
@@ -3961,7 +4003,7 @@ var CanvasGraphics = (function() {
             if (background)
                 TODO("handle background colors");
 
-            const types = [null,
+            var types = [null,
                            this.fillFunctionShading,
                            this.fillAxialShading,
                            this.fillRadialShading];
@@ -4398,7 +4440,7 @@ var PDFFunction = (function() {
         if (!dict)
            dict = fn;
 
-        const types = [this.constructSampled,
+        var types = [this.constructSampled,
                        null,
                        this.constructInterpolated,
                        this.constructStiched,
diff --git a/test.py b/test.py
index 0c326ec09..9eab0e80e 100644
--- a/test.py
+++ b/test.py
@@ -9,6 +9,7 @@ def prompt(question):
 
 ANAL = True
 DEFAULT_MANIFEST_FILE = 'test_manifest.json'
+EQLOG_FILE = 'eq.log'
 REFDIR = 'ref'
 TMPDIR = 'tmp'
 VERBOSE = False
@@ -16,7 +17,7 @@ VERBOSE = False
 MIMEs = {
     '.css': 'text/css',
     '.html': 'text/html',
-    '.js': 'application/json',
+    '.js': 'application/javascript',
     '.json': 'application/json',
     '.pdf': 'application/pdf',
     '.xhtml': 'application/xhtml+xml',
@@ -35,6 +36,7 @@ class State:
     numEqNoSnapshot = 0
     numFBFFailures = 0
     numLoadFailures = 0
+    eqLog = None
 
 class Result:
     def __init__(self, snapshot, failure):
@@ -115,8 +117,8 @@ def setUp(manifestFile, masterMode):
     assert not os.path.isdir(TMPDIR)
 
     testBrowsers = [ b for b in
-                     ( 'firefox5', )
-#'chrome12', 'chrome13', 'firefox4', 'firefox6','opera11' ):
+                     ( 'firefox5', 'firefox6', )
+#'chrome12', 'chrome13', 'firefox4', 'opera11' ):
                      if os.access(b, os.R_OK | os.X_OK) ]
 
     mf = open(manifestFile)
@@ -150,7 +152,7 @@ def setUp(manifestFile, masterMode):
                 taskResults.append([ ])
             State.taskResults[b][id] = taskResults
 
-    State.remaining = len(manifestList)
+    State.remaining = len(testBrowsers) * len(manifestList)
 
     for b in testBrowsers:
         print 'Launching', b
@@ -190,6 +192,7 @@ def check(task, results, browser):
 def checkEq(task, results, browser):
     pfx = os.path.join(REFDIR, sys.platform, browser, task['id'])
     results = results[0]
+    taskId = task['id']
 
     passed = True
     for page in xrange(len(results)):
@@ -208,7 +211,21 @@ def checkEq(task, results, browser):
 
             eq = (ref == snapshot)
             if not eq:
-                print 'TEST-UNEXPECTED-FAIL | eq', task['id'], '| in', browser, '| rendering of page', page + 1, '!= reference rendering'
+                print 'TEST-UNEXPECTED-FAIL | eq', taskId, '| in', browser, '| rendering of page', page + 1, '!= reference rendering'
+                # XXX need to dump this always, somehow, when we have
+                # the reference repository
+                if State.masterMode:
+                    if not State.eqLog:
+                        State.eqLog = open(EQLOG_FILE, 'w')
+                    eqLog = State.eqLog
+
+                    # NB: this follows the format of Mozilla reftest
+                    # output so that we can reuse its reftest-analyzer
+                    # script
+                    print >>eqLog, 'REFTEST TEST-UNEXPECTED-FAIL |', browser +'-'+ taskId +'-page'+ str(page + 1), '| image comparison (==)'
+                    print >>eqLog, 'REFTEST   IMAGE 1 (TEST):', snapshot
+                    print >>eqLog, 'REFTEST   IMAGE 2 (REFERENCE):', ref
+
                 passed = False
                 State.numEqFailures += 1
 
diff --git a/test_slave.html b/test_slave.html
index 06b911810..718e887e0 100644
--- a/test_slave.html
+++ b/test_slave.html
@@ -1,6 +1,7 @@
 <html>
 <head>
   <title>pdf.js test slave</title>
+  <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="glyphlist.js"></script>
@@ -31,7 +32,7 @@ function load() {
   stdout = document.getElementById("stdout");
 
   log("Harness thinks this browser is '"+ browser +"'\n");
-  log("Fetching manifest ...");
+  log("Fetching manifest "+ manifestFile +"...");
 
   var r = new XMLHttpRequest();
   r.open("GET", manifestFile, false);
@@ -81,38 +82,69 @@ function nextPage() {
   }
 
   failure = '';
-  log("    drawing page "+ currentTask.pageNum +"...");
+  log("    loading page "+ currentTask.pageNum +"... ");
 
   var ctx = canvas.getContext("2d");
   clear(ctx);
 
   var fonts = [];
+  var fontsReady = true;
   var gfx = new CanvasGraphics(ctx);
   try {
     currentPage = pdfDoc.getPage(currentTask.pageNum);
     currentPage.compile(gfx, fonts);
+
+    // Inspect fonts and translate the missing ones
+    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;
+      }
+      new Font(font.name, font.file, font.properties);
+      fontsReady = false;
+    }
   } catch(e) {
     failure = 'compile: '+ e.toString();
   }
 
-  // TODO load fonts
-  setTimeout(function() {
-      if (!failure) {
-        try {
-          currentPage.display(gfx);
-        } catch(e) {
-          failure = 'render: '+ e.toString();
-        }
+  var checkFontsLoadedIntervalTimer = null;
+  function checkFontsLoaded() {
+    for (var i = 0; i < count; i++) {
+      if (Fonts[font.name].loading) {
+        return;
       }
-      currentTask.taskDone = (currentTask.pageNum == pdfDoc.numPages
-                              && (1 + currentTask.round) == currentTask.rounds);
-      sendTaskResult(canvas.toDataURL("image/png"));
-      log("done"+ (failure ? " (failed!)" : "") +"\n");
+    }
+    window.clearInterval(checkFontsLoadedIntervalTimer);
 
-      ++currentTask.pageNum, nextPage();
-    },
-    0
-  );
+    snapshotCurrentPage(gfx);
+  }
+
+  if (failure || fontsReady) {
+    snapshotCurrentPage(gfx);
+  } else {
+    checkFontsLoadedIntervalTimer = setInterval(checkFontsLoaded, 10);
+  }
+}
+
+function snapshotCurrentPage(gfx) {
+  log("done, snapshotting... ");
+
+  if (!failure) {
+    try {
+      currentPage.display(gfx);
+    } catch(e) {
+      failure = 'render: '+ e.toString();
+    }
+  }
+
+  currentTask.taskDone = (currentTask.pageNum == pdfDoc.numPages
+                          && (1 + currentTask.round) == currentTask.rounds);
+  sendTaskResult(canvas.toDataURL("image/png"));
+  log("done"+ (failure ? " (failed!)" : "") +"\n");
+
+  ++currentTask.pageNum, nextPage();
 }
 
 function done() {