diff --git a/src/canvas.js b/src/canvas.js
index 7bf94a642..dd363ecf3 100644
--- a/src/canvas.js
+++ b/src/canvas.js
@@ -752,28 +752,30 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
             continue;
           }
 
-          var char = glyph.fontChar;
+          var character = glyph.fontChar;
           var charWidth = glyph.width * fontSize * 0.001 +
               Util.sign(current.fontMatrix[0]) * charSpacing;
 
-          var scaledX = x / fontSizeScale;
-          switch (textRenderingMode) {
-            default: // other unsupported rendering modes
-            case TextRenderingMode.FILL:
-            case TextRenderingMode.FILL_ADD_TO_PATH:
-              ctx.fillText(char, scaledX, 0);
-              break;
-            case TextRenderingMode.STROKE:
-            case TextRenderingMode.STROKE_ADD_TO_PATH:
-              ctx.strokeText(char, scaledX, 0);
-              break;
-            case TextRenderingMode.FILL_STROKE:
-            case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
-              ctx.fillText(char, scaledX, 0);
-              ctx.strokeText(char, scaledX, 0);
-              break;
-            case TextRenderingMode.INVISIBLE:
-              break;
+          if (!glyph.disabled) {
+            var scaledX = x / fontSizeScale;
+            switch (textRenderingMode) {
+              default: // other unsupported rendering modes
+              case TextRenderingMode.FILL:
+              case TextRenderingMode.FILL_ADD_TO_PATH:
+                ctx.fillText(character, scaledX, 0);
+                break;
+              case TextRenderingMode.STROKE:
+              case TextRenderingMode.STROKE_ADD_TO_PATH:
+                ctx.strokeText(character, scaledX, 0);
+                break;
+              case TextRenderingMode.FILL_STROKE:
+              case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
+                ctx.fillText(character, scaledX, 0);
+                ctx.strokeText(character, scaledX, 0);
+                break;
+              case TextRenderingMode.INVISIBLE:
+                break;
+            }
           }
 
           x += charWidth;
diff --git a/src/evaluator.js b/src/evaluator.js
index c0a96d382..8db082d59 100644
--- a/src/evaluator.js
+++ b/src/evaluator.js
@@ -533,9 +533,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 
         var cmap = cmapObj.getBytes(cmapObj.length);
         for (var i = 0, ii = cmap.length; i < ii; i++) {
-          var byte = cmap[i];
-          if (byte == 0x20 || byte == 0x0D || byte == 0x0A ||
-              byte == 0x3C || byte == 0x5B || byte == 0x5D) {
+          var octet = cmap[i];
+          if (octet == 0x20 || octet == 0x0D || octet == 0x0A ||
+              octet == 0x3C || octet == 0x5B || octet == 0x5D) {
             switch (token) {
               case 'usecmap':
                 error('usecmap is not implemented');
@@ -592,7 +592,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
                 tokens.push(token);
                 token = '';
             }
-            switch (byte) {
+            switch (octet) {
               case 0x5B:
                 // begin list parsing
                 tokens.push(beginArrayToken);
@@ -606,7 +606,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
                 tokens.push(items);
                 break;
             }
-          } else if (byte == 0x3E) {
+          } else if (octet == 0x3E) {
             if (token.length) {
               if (token.length <= 4) {
                 // parsing hex number
@@ -632,7 +632,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
               }
             }
           } else {
-            token += String.fromCharCode(byte);
+            token += String.fromCharCode(octet);
           }
         }
       }
diff --git a/src/fonts.js b/src/fonts.js
index b756ff2ee..74bff269c 100644
--- a/src/fonts.js
+++ b/src/fonts.js
@@ -1828,8 +1828,9 @@ var Font = (function FontClosure() {
         readGlyphNameMap(post, properties);
       }
 
-      // Replace the old CMAP table with a shiny new one
+      var glyphs, ids;
       if (properties.type == 'CIDFontType2') {
+        // Replace the old CMAP table with a shiny new one
         // Type2 composite fonts map characters directly to glyphs so the cmap
         // table must be replaced.
         // canvas fillText will reencode some characters even if the font has a
@@ -1861,7 +1862,9 @@ var Font = (function FontClosure() {
           }
         }
 
-        var glyphs = [], ids = [];
+        glyphs = [];
+        ids = [];
+
         var usedUnicodes = [];
         var unassignedUnicodeItems = [];
         for (var i = 1; i < numGlyphs; i++) {
@@ -1892,11 +1895,12 @@ var Font = (function FontClosure() {
           glyphs.push({ unicode: unicode, code: cid });
           ids.push(i);
         }
-        cmap.data = createCMapTable(glyphs, ids);
       } else {
         var cmapTable = readCMapTable(cmap, font);
-        var glyphs = cmapTable.glyphs;
-        var ids = cmapTable.ids;
+
+        glyphs = cmapTable.glyphs;
+        ids = cmapTable.ids;
+
         var hasShortCmap = !!cmapTable.hasShortCmap;
         var toFontChar = this.toFontChar;
 
@@ -2062,10 +2066,16 @@ var Font = (function FontClosure() {
 
         createGlyphNameMap(glyphs, ids, properties);
         this.glyphNameMap = properties.glyphNameMap;
-
-        cmap.data = createCMapTable(glyphs, ids);
       }
 
+      // Converting glyphs and ids into font's cmap table
+      cmap.data = createCMapTable(glyphs, ids);
+      var unicodeIsEnabled = [];
+      for (var i = 0, ii = glyphs.length; i < ii; i++) {
+        unicodeIsEnabled[glyphs[i].unicode] = true;
+      }
+      this.unicodeIsEnabled = unicodeIsEnabled;
+
       // Rewrite the 'post' table if needed
       if (requiredTables.indexOf('post') != -1) {
         tables.push({
@@ -2391,7 +2401,7 @@ var Font = (function FontClosure() {
     },
 
     charToGlyph: function fonts_charToGlyph(charcode) {
-      var fontCharCode, width, operatorList;
+      var fontCharCode, width, operatorList, disabled;
 
       var width = this.widths[charcode];
 
@@ -2464,11 +2474,14 @@ var Font = (function FontClosure() {
         unicodeChars = String.fromCharCode(unicodeChars);
 
       width = (isNum(width) ? width : this.defaultWidth) * this.widthMultiplier;
+      disabled = this.unicodeIsEnabled ?
+        !this.unicodeIsEnabled[fontCharCode] : false;
 
       return {
         fontChar: String.fromCharCode(fontCharCode),
         unicode: unicodeChars,
         width: width,
+        disabled: disabled,
         operatorList: operatorList
       };
     },
@@ -2848,7 +2861,7 @@ var Type1Parser = function type1Parser() {
       subrs: [],
       charstrings: [],
       properties: {
-        'private': {
+        'privateData': {
           'lenIV': 4
         }
       }
@@ -2877,7 +2890,7 @@ var Type1Parser = function type1Parser() {
           (token == 'RD' || token == '-|')) {
         i++;
         var data = eexec.slice(i, i + length);
-        var lenIV = program.properties.private['lenIV'];
+        var lenIV = program.properties.privateData['lenIV'];
         var encoded = decrypt(data, kCharStringsEncryptionKey, lenIV);
         var str = decodeCharString(encoded);
 
@@ -2917,7 +2930,7 @@ var Type1Parser = function type1Parser() {
                 var length = parseInt(getToken(), 10);
                 getToken(); // read in 'RD'
                 var data = eexec.slice(i + 1, i + 1 + length);
-                var lenIV = program.properties.private['lenIV'];
+                var lenIV = program.properties.privateData['lenIV'];
                 var encoded = decrypt(data, kCharStringsEncryptionKey, lenIV);
                 var str = decodeCharString(encoded);
                 i = i + 1 + length;
@@ -2933,12 +2946,12 @@ var Type1Parser = function type1Parser() {
             case '/FamilyOtherBlues':
             case '/StemSnapH':
             case '/StemSnapV':
-              program.properties.private[token.substring(1)] =
+              program.properties.privateData[token.substring(1)] =
                 readNumberArray(eexecStr, i + 1);
               break;
             case '/StdHW':
             case '/StdVW':
-              program.properties.private[token.substring(1)] =
+              program.properties.privateData[token.substring(1)] =
                 readNumberArray(eexecStr, i + 2)[0];
               break;
             case '/BlueShift':
@@ -2947,7 +2960,7 @@ var Type1Parser = function type1Parser() {
             case '/BlueScale':
             case '/LanguageGroup':
             case '/ExpansionFactor':
-              program.properties.private[token.substring(1)] =
+              program.properties.privateData[token.substring(1)] =
                 readNumber(eexecStr, i + 1);
               break;
           }
@@ -2971,14 +2984,14 @@ var Type1Parser = function type1Parser() {
     var count = headerString.length;
     for (var i = 0; i < count; i++) {
       var getToken = function getToken() {
-        var char = headerString[i];
-        while (i < count && (isSeparator(char) || char == '/'))
-          char = headerString[++i];
+        var character = headerString[i];
+        while (i < count && (isSeparator(character) || character == '/'))
+          character = headerString[++i];
 
         var token = '';
-        while (i < count && !(isSeparator(char) || char == '/')) {
-          token += char;
-          char = headerString[++i];
+        while (i < count && !(isSeparator(character) || character == '/')) {
+          token += character;
+          character = headerString[++i];
         }
 
         return token;
@@ -3344,7 +3357,7 @@ Type1Font.prototype = {
           dict += self.encodeNumber(offset) + '\x11'; // Charstrings
 
           offset = offset + fields.charstrings.length;
-          dict += self.encodeNumber(fields.private.length);
+          dict += self.encodeNumber(fields.privateData.length);
           dict += self.encodeNumber(offset) + '\x12'; // Private
 
           return header + String.fromCharCode(dict.length + 1) + dict;
@@ -3385,7 +3398,7 @@ Type1Font.prototype = {
       'charstrings': this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs),
                                                true),
 
-      'private': (function cffWrapPrivate(self) {
+      'privateData': (function cffWrapPrivate(self) {
         var data =
             '\x8b\x14' + // defaultWidth
             '\x8b\x15';  // nominalWidth
@@ -3403,9 +3416,9 @@ Type1Font.prototype = {
           ExpansionFactor: '\x0c\x18'
         };
         for (var field in fieldMap) {
-          if (!properties.private.hasOwnProperty(field))
+          if (!properties.privateData.hasOwnProperty(field))
             continue;
-          var value = properties.private[field];
+          var value = properties.privateData[field];
 
           if (isArray(value)) {
             data += self.encodeNumber(value[0]);
@@ -4362,16 +4375,18 @@ var CFFCompiler = (function CFFCompilerClosure() {
       output.add(charStrings);
 
       if (cff.isCIDFont) {
+        // For some reason FDSelect must be in front of FDArray on windows. OSX
+        // and linux don't seem to care.
+        topDictTracker.setEntryLocation('FDSelect', [output.length], output);
+        var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
+        output.add(fdSelect);
+
         var compiled = this.compileTopDicts(cff.fdArray, output.length);
         topDictTracker.setEntryLocation('FDArray', [output.length], output);
         output.add(compiled.output);
         var fontDictTrackers = compiled.trackers;
 
         this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);
-
-        topDictTracker.setEntryLocation('FDSelect', [output.length], output);
-        var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
-        output.add(fdSelect);
       }
 
       this.compilePrivateDicts([cff.topDict], [topDictTracker], output);
diff --git a/src/utils/fonts_utils.js b/src/utils/fonts_utils.js
index 2a1f0ea72..65c02fce2 100644
--- a/src/utils/fonts_utils.js
+++ b/src/utils/fonts_utils.js
@@ -122,9 +122,9 @@ function readFontDictData(aString, aMap) {
       token = '';
       var parsed = false;
       while (!parsed) {
-        var byte = aString[i++];
+        var octet = aString[i++];
 
-        var nibbles = [parseInt(byte / 16, 10), parseInt(byte % 16, 10)];
+        var nibbles = [parseInt(octet / 16, 10), parseInt(octet % 16, 10)];
         for (var j = 0; j < nibbles.length; j++) {
           var nibble = nibbles[j];
           switch (nibble) {
@@ -336,7 +336,7 @@ var Type2Parser = function type2Parser(aFilePath) {
     var privateDict = [];
     for (var i = 0; i < priv.size; i++)
       privateDict.push(aStream.getByte());
-    dump('private:' + privateDict);
+    dump('privateData:' + privateDict);
     parseAsToken(privateDict, CFFDictPrivateDataMap);
 
     for (var p in font.map)
diff --git a/test/driver.js b/test/driver.js
index 5a3263bda..8814da00e 100644
--- a/test/driver.js
+++ b/test/driver.js
@@ -266,6 +266,9 @@ function sendTaskResult(snapshot, task, failure) {
   r.onreadystatechange = function sendTaskResultOnreadystatechange(e) {
     if (r.readyState == 4) {
       inFlightRequests--;
+      // Retry until successful
+      if (r.status !== 200)
+        sendTaskResult(snapshot, task, failure);
     }
   };
   document.getElementById('inFlightCount').innerHTML = inFlightRequests++;
diff --git a/web/viewer.css b/web/viewer.css
index fdce0288a..9a0cf388c 100644
--- a/web/viewer.css
+++ b/web/viewer.css
@@ -391,11 +391,43 @@ canvas {
   }
 }
 
-#loading {
+#loadingBox {
   margin: 100px 0;
   text-align: center;
 }
 
+#loadingBar {
+  background-color: #333;
+  display: inline-block;
+  border: 1px solid black;
+  clear: both;
+  margin:0px;
+  line-height: 0;
+  border-radius: 4px;
+  width: 15em;
+  height: 1.5em;
+}
+
+#loadingBar .progress {
+  background-color: green;
+  display: inline-block;
+  float: left;
+
+  background: #b4e391;
+  background: -moz-linear-gradient(top, #b4e391 0%, #61c419 50%, #b4e391 100%);
+  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b4e391), color-stop(50%,#61c419), color-stop(100%,#b4e391));
+  background: -webkit-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);
+  background: -o-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);
+  background: -ms-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);
+  background: linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);    
+
+  border-top-left-radius: 3px;
+  border-bottom-left-radius: 3px;
+
+  width: 0%;
+  height: 100%;
+}
+
 #PDFBug {
   font-size: 10px;
   position: fixed;
diff --git a/web/viewer.html b/web/viewer.html
index 2806d3a7e..d275f77c1 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -143,7 +143,10 @@
       </div>
     </div>
 
-    <div id="loading">Loading... 0%</div>
+    <div id="loadingBox">
+        <div id="loading">Loading... 0%</div>
+        <div id="loadingBar"><div class="progress"></div></div>
+    </div>
     <div id="viewer"></div>
   </body>
 </html>
diff --git a/web/viewer.js b/web/viewer.js
index e10f6c29f..8977b53c0 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -15,6 +15,15 @@ var kMaxScale = 4.0;
 var kImageDirectory = './images/';
 var kSettingsMemory = 20;
 
+function getFileName(url) {
+  var anchor = url.indexOf('#');
+  var query = url.indexOf('?');
+  var end = Math.min(
+    anchor > 0 ? anchor : url.length,
+    query > 0 ? query : url.length);
+  return url.substring(url.lastIndexOf('/', end) + 1, end);
+}
+
 var Cache = function cacheCache(size) {
   var data = [];
   this.push = function cachePush(view) {
@@ -27,6 +36,48 @@ var Cache = function cacheCache(size) {
   };
 };
 
+var ProgressBar = (function ProgressBarClosure() {
+
+  function clamp(v, min, max) {
+    return Math.min(Math.max(v, min), max);
+  }
+
+  function ProgressBar(id, opts) {
+
+    // Fetch the sub-elements for later
+    this.div = document.querySelector(id + ' .progress');
+
+    // Get options, with sensible defaults
+    this.height = opts.height || 100;
+    this.width = opts.width || 100;
+    this.units = opts.units || '%';
+    this.percent = opts.percent || 0;
+
+    // Initialize heights
+    this.div.style.height = this.height + this.units;
+  }
+
+  ProgressBar.prototype = {
+
+    updateBar: function ProgressBar_updateBar() {
+      var progressSize = this.width * this._percent / 100;
+
+      this.div.style.width = progressSize + this.units;
+    },
+
+    get percent() {
+      return this._percent;
+    },
+
+    set percent(val) {
+      this._percent = clamp(val, 0, 100);
+      this.updateBar();
+    }
+  };
+
+  return ProgressBar;
+})();
+
 var RenderingQueue = (function RenderingQueueClosure() {
   function RenderingQueue() {
     this.items = [];
@@ -258,7 +309,13 @@ var PDFView = {
   },
 
   open: function pdfViewOpen(url, scale) {
-    document.title = this.url = url;
+    this.url = url;
+
+    document.title = decodeURIComponent(getFileName(url)) || url;
+
+    if (!PDFView.loadingBar) {
+      PDFView.loadingBar = new ProgressBar('#loadingBar', {});
+    }
 
     var self = this;
     PDFJS.getPdf(
@@ -400,6 +457,8 @@ var PDFView = {
     var percent = Math.round(level * 100);
     var loadingIndicator = document.getElementById('loading');
     loadingIndicator.textContent = 'Loading... ' + percent + '%';
+
+    PDFView.loadingBar.percent = percent;
   },
 
   load: function pdfViewLoad(data, scale) {
@@ -414,8 +473,8 @@ var PDFView = {
     var errorWrapper = document.getElementById('errorWrapper');
     errorWrapper.setAttribute('hidden', 'true');
 
-    var loadingIndicator = document.getElementById('loading');
-    loadingIndicator.setAttribute('hidden', 'true');
+    var loadingBox = document.getElementById('loadingBox');
+    loadingBox.setAttribute('hidden', 'true');
 
     var sidebar = document.getElementById('sidebarView');
     sidebar.parentNode.scrollTop = 0;