Bug 812823 - Update pdf.js to version 0.6.141. r=bdahl
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 18 Nov 2012 21:05:21 -0500
changeset 113653 a5c51f86ef3d3ab33f94a2eafd52e2c514ed2e18
parent 113652 77c8bc0e550a2a7696ee479a109525b025123d2d
child 113654 94926b9916cba4b5c2f8eef9ddf2a105a2d32808
push id23884
push useremorley@mozilla.com
push dateMon, 19 Nov 2012 15:35:20 +0000
treeherdermozilla-central@258292c9c929 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbdahl
bugs812823
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 812823 - Update pdf.js to version 0.6.141. r=bdahl
browser/extensions/pdfjs/README.mozilla
browser/extensions/pdfjs/content/build/pdf.js
browser/extensions/pdfjs/content/web/viewer.js
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 0.6.123
+Current extension version is: 0.6.141
 
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -17,17 +17,17 @@
 
 var PDFJS = {};
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
   PDFJS.build =
-'1ff741a';
+'033545c';
 
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 /* Copyright 2012 Mozilla Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -17496,35 +17496,39 @@ var Font = (function FontClosure() {
            '\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
   };
 
-  function createNameTable(name) {
+  function createNameTable(name, proto) {
+    if (!proto) {
+      proto = [[], []]; // no strings and unicode strings
+    }
+
     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
-      '',                  // 6.Postscript name
-      'Unknown',           // 7.Trademark
-      'Unknown',           // 8.Manufacturer
-      'Unknown'            // 9.Designer
+      proto[0][0] || 'Original licence',  // 0.Copyright
+      proto[0][1] || name,                // 1.Font family
+      proto[0][2] || 'Unknown',           // 2.Font subfamily (font weight)
+      proto[0][3] || 'uniqueID',          // 3.Unique ID
+      proto[0][4] || name,                // 4.Full font name
+      proto[0][5] || 'Version 0.11',      // 5.Version
+      proto[0][6] || '',                  // 6.Postscript name
+      proto[0][7] || 'Unknown',           // 7.Trademark
+      proto[0][8] || 'Unknown',           // 8.Manufacturer
+      proto[0][9] || '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, ii = strings.length; i < ii; i++) {
-      var str = strings[i];
+      var str = proto[1][i] || strings[i];
 
       var strUnicode = '';
       for (var j = 0, jj = str.length; j < jj; j++)
         strUnicode += string16(str.charCodeAt(j));
       stringsUnicode.push(strUnicode);
     }
 
     var names = [strings, stringsUnicode];
@@ -17810,27 +17814,20 @@ var Font = (function FontClosure() {
               ids: ids
             };
           }
         }
         error('Unsupported cmap table format');
       };
 
       function sanitizeMetrics(font, header, metrics, numGlyphs) {
-        if (!header && !metrics)
-          return;
-
-        // The vhea/vmtx tables are not required, so it happens that
-        // some fonts embed a vmtx table without a vhea table. In this
-        // situation the sanitizer assume numOfLongVerMetrics = 1. As
-        // a result it tries to read numGlyphs - 1 SHORT from the vmtx
-        // table, and if it is not possible, the font is rejected.
-        // So remove the vmtx table if there is no vhea table.
-        if (!header && metrics) {
-          metrics.data = null;
+        if (!header) {
+          if (metrics) {
+            metrics.data = null;
+          }
           return;
         }
 
         font.pos = (font.start ? font.start : 0) + header.offset;
         font.pos += header.length - 2;
         var numOfMetrics = int16(font.getBytes(2));
 
         var numOfSidebearings = numGlyphs - numOfMetrics;
@@ -17984,33 +17981,37 @@ var Font = (function FontClosure() {
         for (var i = itemSize, j = 0; i < length; i += itemSize, j++) {
           var offset = itemDecode(data, i);
           if (offset == lastOffset)
             emptyGlyphIds[j] = true;
           lastOffset = offset;
         }
       }
 
-      function readPostScriptTable(post, properties) {
+      function readPostScriptTable(post, properties, maxpNumGlyphs) {
         var start = (font.start ? font.start : 0) + post.offset;
         font.pos = start;
 
         var length = post.length, end = start + length;
         var version = int32(font.getBytes(4));
         // skip rest to the tables
         font.getBytes(28);
 
         var glyphNames;
         var valid = true;
         switch (version) {
           case 0x00010000:
             glyphNames = MacStandardGlyphOrdering;
             break;
           case 0x00020000:
             var numGlyphs = int16(font.getBytes(2));
+            if (numGlyphs != maxpNumGlyphs) {
+              valid = false;
+              break;
+            }
             var glyphNameIndexes = [];
             for (var i = 0; i < numGlyphs; ++i) {
               var index = int16(font.getBytes(2));
               if (index >= 32768) {
                 valid = false;
                 break;
               }
               glyphNameIndexes.push(index);
@@ -18042,37 +18043,223 @@ var Font = (function FontClosure() {
             warn('Unknown/unsupported post table version ' + version);
             valid = false;
             break;
         }
         properties.glyphNames = glyphNames;
         return valid;
       }
 
+      function readNameTable(nameTable) {
+        var start = (font.start ? font.start : 0) + nameTable.offset;
+        font.pos = start;
+
+        var names = [[], []];
+        var length = nameTable.length, end = start + length;
+        var format = int16(font.getBytes(2));
+        var FORMAT_0_HEADER_LENGTH = 6;
+        if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {
+          // unsupported name table format or table "too" small
+          return names;
+        }
+        var numRecords = int16(font.getBytes(2));
+        var stringsStart = int16(font.getBytes(2));
+        var records = [];
+        var NAME_RECORD_LENGTH = 12;
+        for (var i = 0; i < numRecords &&
+                        font.pos + NAME_RECORD_LENGTH <= end; i++) {
+          var r = {
+            platform: int16(font.getBytes(2)),
+            encoding: int16(font.getBytes(2)),
+            language: int16(font.getBytes(2)),
+            name: int16(font.getBytes(2)),
+            length: int16(font.getBytes(2)),
+            offset: int16(font.getBytes(2))
+          };
+          // using only Macintosh and Windows platform/encoding names
+          if ((r.platform == 1 && r.encoding == 0 && r.language == 0) ||
+              (r.platform == 3 && r.encoding == 1 && r.language == 0x409)) {
+            records.push(r);
+          }
+        }
+        for (var i = 0, ii = records.length; i < ii; i++) {
+          var record = records[i];
+          var pos = start + stringsStart + record.offset;
+          if (pos + record.length > end) {
+            continue; // outside of name table, ignoring
+          }
+          font.pos = pos;
+          var nameIndex = record.name;
+          var encoding = record.encoding ? 1 : 0;
+          if (record.encoding) {
+            // unicode
+            var str = '';
+            for (var j = 0, jj = record.length; j < jj; j += 2) {
+              str += String.fromCharCode(int16(font.getBytes(2)));
+            }
+            names[1][nameIndex] = str;
+          } else {
+            names[0][nameIndex] = bytesToString(font.getBytes(record.length));
+          }
+        }
+        return names;
+      }
+
       function isOS2Valid(os2Table) {
         var data = os2Table.data;
         // usWinAscent == 0 makes font unreadable by windows
         var usWinAscent = (data[74] << 8) | data[75];
         if (usWinAscent == 0)
           return false;
 
         return true;
       }
 
+      var TTOpsStackDeltas = [
+        0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5,
+        -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, -1, -1,
+        1, -1, -999, 0, 1, 0, 0, -2, 0, -1, -2, -1, -999, -999, -1, -1,
+        0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -2, 0, -2, -2,
+        0, 0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1,
+        -1, -1, -1, -1, -1, -1, 0, 0, -1, 0, -1, -1, 0, -999, -1, -1,
+        -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0,
+        -2, -999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1,
+        -999, -2, -2, 0, 0, -1, -2, -2, 0, -999, 0, 0, 0, -1, -2];
+        // 0xC0-DF == -1 and 0xE0-FF == -2
+
+      function sanitizeTTProgram(table, ttContext) {
+        var data = table.data;
+        var i = 0, n, lastEndf = 0, lastDeff = 0;
+        var stack = [];
+        var tooComplexToFollowFunctions =
+          ttContext.tooComplexToFollowFunctions;
+        for (var ii = data.length; i < ii;) {
+          var op = data[i++];
+          // The TrueType instruction set docs can be found at
+          // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
+          if (op === 0x40) { // NPUSHB - pushes n bytes
+            n = data[i++];
+            for (var j = 0; j < n; j++) {
+              stack.push(data[i++]);
+            }
+          } else if (op === 0x41) { // NPUSHW - pushes n words
+            n = data[i++];
+            for (var j = 0; j < n; j++) {
+              var b = data[i++];
+              stack.push((b << 8) | data[i++]);
+            }
+          } else if ((op & 0xF8) === 0xB0) { // PUSHB - pushes bytes
+            n = op - 0xB0 + 1;
+            for (var j = 0; j < n; j++) {
+              stack.push(data[i++]);
+            }
+          } else if ((op & 0xF8) === 0xB8) { // PUSHW - pushes words
+            n = op - 0xB8 + 1;
+            for (var j = 0; j < n; j++) {
+              var b = data[i++];
+              stack.push((b << 8) | data[i++]);
+            }
+          } else if (op === 0x2B && !tooComplexToFollowFunctions) { // CALL
+            // collecting inforamtion about which functions are used
+            var funcId = stack[stack.length - 1];
+            ttContext.functionsUsed[funcId] = true;
+            if (i >= 2 && data[i - 2] === 0x2B) {
+              // all data in stack, calls are performed in sequence
+              tooComplexToFollowFunctions = true;
+            }
+          } else if (op === 0x2C && !tooComplexToFollowFunctions) { // FDEF
+            // collecting inforamtion about which functions are defined
+            lastDeff = i;
+            var funcId = stack[stack.length - 1];
+            ttContext.functionsDefined[funcId] = true;
+            if (i >= 2 && data[i - 2] === 0x2D) {
+              // all function ids in stack, FDEF/ENDF perfomed in sequence
+              tooComplexToFollowFunctions = true;
+            }
+          } else if (op === 0x2D) { // ENDF - end of function
+            lastEndf = i;
+          } else if (op === 0x89) { // IDEF - instruction definition
+            // recording it as a function to track ENDF
+            lastDeff = i;
+          }
+          // Adjusting stack not extactly, but just enough to get function id
+          var stackDelta = op <= 0x8E ? TTOpsStackDeltas[op] :
+            op >= 0xC0 && op <= 0xDF ? -1 : op >= 0xE0 ? -2 : 0;
+          while (stackDelta < 0 && stack.length > 0) {
+            stack.pop();
+            stackDelta++;
+          }
+          while (stackDelta > 0) {
+            stack.push(NaN); // pushing any number into stack
+            stackDelta--;
+          }
+        }
+        ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;
+        var content = [data];
+        if (i > data.length) {
+          content.push(new Uint8Array(i - data.length));
+        }
+        if (lastDeff > lastEndf) {
+          // new function definition started, but not finished
+          // complete function by [CLEAR, ENDF]
+          content.push(new Uint8Array([0x22, 0x2D]));
+        }
+        if (ttContext.defineMissingFunctions && !tooComplexToFollowFunctions) {
+          for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
+            if (!ttContext.functionsUsed[j] || ttContext.functionsDefined[j]) {
+              continue;
+            }
+            // function is used, but not defined
+            // creating empty one [PUSHB, function-id, FDEF, ENDF]
+            content.push(new Uint8Array([0xB0, j, 0x2C, 0x2D]));
+          }
+        }
+        if (content.length > 1) {
+          // concatenating the content items
+          var newLength = 0;
+          for (var j = 0, jj = content.length; j < jj; j++) {
+            newLength += content[j].length;
+          }
+          newLength = (newLength + 3) & ~3;
+          var result = new Uint8Array(newLength);
+          var pos = 0;
+          for (var j = 0, jj = content.length; j < jj; j++) {
+            result.set(content[j], pos);
+            pos += content[j].length;
+          }
+          table.data = result;
+          table.length = newLength;
+        }
+      }
+
+      function sanitizeTTPrograms(fpgm, prep) {
+        var ttContext = {
+          functionsDefined: [],
+          functionsUsed: [],
+          tooComplexToFollowFunctions: false
+        };
+        if (prep) {
+          // collecting prep functions info first
+          sanitizeTTProgram(prep, ttContext);
+        }
+        if (fpgm) {
+          ttContext.defineMissingFunctions = true;
+          sanitizeTTProgram(fpgm, ttContext);
+        }
+      }
+
       // Check that required tables are present
       var requiredTables = ['OS/2', 'cmap', 'head', 'hhea',
                              'hmtx', 'maxp', 'name', 'post'];
 
-      var optionalTables = ['cvt ', 'fpgm', 'glyf', 'loca', 'prep',
-                            'CFF ', 'VORG', 'vhea', 'vmtx'];
-
       var header = readOpenTypeHeader(font);
       var numTables = header.numTables;
 
-      var cmap, post, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf, os2;
+      var cmap, post, maxp, hhea, hmtx, head, os2;
+      var glyf, fpgm, loca, prep, cvt;
       var tables = [];
       for (var i = 0; i < numTables; i++) {
         var table = readTableEntry(font);
         var index = requiredTables.indexOf(table.tag);
         if (index != -1) {
           if (table.tag == 'cmap')
             cmap = table;
           else if (table.tag == 'post')
@@ -18084,28 +18271,29 @@ var Font = (function FontClosure() {
           else if (table.tag == 'hmtx')
             hmtx = table;
           else if (table.tag == 'head')
             head = table;
           else if (table.tag == 'OS/2')
             os2 = table;
 
           requiredTables.splice(index, 1);
-        } else if (optionalTables.indexOf(table.tag) < 0) {
-          // skipping table if it's not a required or optional table
-          continue;
-        } else {
-          if (table.tag == 'vmtx')
-            vmtx = table;
-          else if (table.tag == 'vhea')
-            vhea = table;
-          else if (table.tag == 'loca')
+        } else {
+          if (table.tag == 'loca')
             loca = table;
           else if (table.tag == 'glyf')
             glyf = table;
+          else if (table.tag == 'fpgm')
+            fpgm = table;
+          else if (table.tag == 'prep')
+            prep = table;
+          else if (table.tag == 'cvt ')
+            cvt = table;
+          else // skipping table if it's not a required or optional table
+            continue;
         }
         tables.push(table);
       }
 
       var numTables = tables.length + requiredTables.length;
 
       // header and new offsets. Table entry information is appended to the
       // end of file. The virtualOffset represents where to put the actual
@@ -18120,24 +18308,25 @@ var Font = (function FontClosure() {
       createOpenTypeHeader(header.version, ttf, numTables);
 
       // Invalid OS/2 can break the font for the Windows
       if (os2 && !isOS2Valid(os2)) {
         tables.splice(tables.indexOf(os2), 1);
         os2 = null;
       }
 
-      // Ensure the [h/v]mtx tables contains the advance width and
+      // Ensure the hmtx table contains the advance width and
       // sidebearings information for numGlyphs in the maxp table
       font.pos = (font.start || 0) + maxp.offset;
       var version = int16(font.getBytes(4));
       var numGlyphs = int16(font.getBytes(2));
 
       sanitizeMetrics(font, hhea, hmtx, numGlyphs);
-      sanitizeMetrics(font, vhea, vmtx, numGlyphs);
+
+      sanitizeTTPrograms(fpgm, prep);
 
       var isGlyphLocationsLong = int16([head.data[50], head.data[51]]);
       if (head && loca && glyf) {
         sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong);
       }
 
       var emptyGlyphIds = [];
       if (glyf)
@@ -18147,17 +18336,17 @@ var Font = (function FontClosure() {
       // Sometimes it's 0. That needs to be fixed
       if (hhea.data[10] == 0 && hhea.data[11] == 0) {
         hhea.data[10] = 0xFF;
         hhea.data[11] = 0xFF;
       }
 
       // The 'post' table has glyphs names.
       if (post) {
-        var valid = readPostScriptTable(post, properties);
+        var valid = readPostScriptTable(post, properties, numGlyphs);
         if (!valid) {
           tables.splice(tables.indexOf(post), 1);
           post = null;
         }
       }
 
       var glyphs, ids;
       if (properties.type == 'CIDFontType2') {
@@ -18440,22 +18629,32 @@ var Font = (function FontClosure() {
       // Rewrite the 'post' table if needed
       if (!post) {
         tables.push({
           tag: 'post',
           data: stringToArray(createPostTable(properties))
         });
       }
 
-      // Rewrite the 'name' table if needed
+      // Re-creating 'name' table
       if (requiredTables.indexOf('name') != -1) {
         tables.push({
           tag: 'name',
           data: stringToArray(createNameTable(this.name))
         });
+      } else {
+        // ... using existing 'name' table as prototype
+        for (var i = 0, ii = tables.length; i < ii; i++) {
+          var table = tables[i];
+          if (table.tag === 'name') {
+            var namePrototype = readNameTable(table);
+            table.data = stringToArray(createNameTable(name, namePrototype));
+            break;
+          }
+        }
       }
 
       // Tables needs to be written by ascendant alphabetic order
       tables.sort(function tables_sort(a, b) {
         return (a.tag > b.tag) - (a.tag < b.tag);
       });
 
       // rewrite the tables but tweak offsets
@@ -19360,17 +19559,19 @@ var Type1Parser = function type1Parser()
             width: str.width
           });
         } else {
           program.subrs.push(str.charstring);
         }
         i += length;
         token = '';
       } else if (isSeparator(c)) {
-        length = parseInt(token, 10);
+        // Use '| 0' to prevent setting a double into length such as the double
+        // does not flow into the loop variable.
+        length = parseInt(token, 10) | 0;
         token = '';
       } else {
         token += c;
         if (!glyphsSection) {
           switch (token) {
             case '/CharString':
               glyphsSection = true;
               break;
@@ -20012,16 +20213,91 @@ var CFFFont = (function CFFFontClosure()
       return charstrings;
     }
   };
 
   return CFFFont;
 })();
 
 var CFFParser = (function CFFParserClosure() {
+  var CharstringValidationData = [
+    null,
+    { id: 'hstem', min: 2, resetStack: true },
+    null,
+    { id: 'vstem', min: 2, resetStack: true },
+    { id: 'vmoveto', min: 1, resetStack: true },
+    { id: 'rlineto', min: 2, resetStack: true },
+    { id: 'hlineto', min: 1, resetStack: true },
+    { id: 'vlineto', min: 1, resetStack: true },
+    { id: 'rrcurveto', min: 6, resetStack: true },
+    null,
+    { id: 'callsubr', min: 1, undefStack: true },
+    { id: 'return', min: 0, resetStack: true },
+    null, // 12
+    null,
+    null, // endchar
+    null,
+    null,
+    null,
+    { id: 'hstemhm', min: 2, resetStack: true },
+    null, // hintmask
+    null, // cntrmask
+    { id: 'rmoveto', min: 2, resetStack: true },
+    { id: 'hmoveto', min: 1, resetStack: true },
+    { id: 'vstemhm', min: 2, resetStack: true },
+    { id: 'rcurveline', min: 8, resetStack: true },
+    { id: 'rlinecurve', min: 8, resetStack: true },
+    { id: 'vvcurveto', min: 4, resetStack: true },
+    { id: 'hhcurveto', min: 4, resetStack: true },
+    null, // shortint
+    { id: 'callgsubr', min: 1, undefStack: true },
+    { id: 'vhcurveto', min: 4, resetStack: true },
+    { id: 'hvcurveto', min: 4, resetStack: true }
+  ];
+  var CharstringValidationData12 = [
+    null,
+    null,
+    null,
+    { id: 'and', min: 2, stackDelta: -1 },
+    { id: 'or', min: 2, stackDelta: -1 },
+    { id: 'not', min: 2, stackDelta: -1 },
+    null,
+    null,
+    null,
+    { id: 'abs', min: 1, stackDelta: 0 },
+    { id: 'add', min: 2, stackDelta: -1 },
+    { id: 'sub', min: 2, stackDelta: -1 },
+    { id: 'div', min: 2, stackDelta: -1 },
+    null,
+    { id: 'neg', min: 1, stackDelta: 0 },
+    { id: 'eq', min: 2, stackDelta: -1 },
+    null,
+    null,
+    { id: 'drop', min: 1, stackDelta: -1 },
+    null,
+    { id: 'put', min: 2, stackDelta: -2 },
+    { id: 'get', min: 1, stackDelta: 0 },
+    { id: 'ifelse', min: 4, stackDelta: -3 },
+    { id: 'random', min: 0, stackDelta: 1 },
+    { id: 'mul', min: 2, stackDelta: -1 },
+    null,
+    { id: 'sqrt', min: 1, stackDelta: 0 },
+    { id: 'dup', min: 1, stackDelta: 1 },
+    { id: 'exch', min: 2, stackDelta: 0 },
+    { id: 'index', min: 2, stackDelta: 0 },
+    { id: 'roll', min: 3, stackDelta: -2 },
+    null,
+    null,
+    null,
+    { id: 'hflex', min: 7, resetStack: true },
+    { id: 'flex', min: 13, resetStack: true },
+    { id: 'hflex1', min: 9, resetStack: true },
+    { id: 'flex1', min: 11, resetStack: true }
+  ];
+
   function CFFParser(file, properties) {
     this.bytes = file.getBytes();
     this.properties = properties;
   }
   CFFParser.prototype = {
     parse: function CFFParser_parse(normalizeCIDData) {
       var properties = this.properties;
       var cff = new CFF();
@@ -20272,39 +20548,93 @@ var CFFParser = (function CFFParserClosu
         var key = pair[0];
         var value = pair[1];
         cffDict.setByKey(key, value);
       }
       return cffDict;
     },
     parseCharStrings: function CFFParser_parseCharStrings(charStringOffset) {
       var charStrings = this.parseIndex(charStringOffset).obj;
-      // The CFF specification state that the 'dotsection' command
-      // (12, 0) is deprecated and treated as a no-op, but all Type2
-      // charstrings processors should support them. Unfortunately
-      // the font sanitizer don't. As a workaround the sequence (12, 0)
-      // is replaced by a useless (0, hmoveto).
       var count = charStrings.count;
       for (var i = 0; i < count; i++) {
         var charstring = charStrings.get(i);
 
+        var stackSize = 0;
+        var undefStack = true;
+        var hints = 0;
+        var valid = true;
         var data = charstring;
         var length = data.length;
-        for (var j = 0; j <= length;) {
+        for (var j = 0; j < length;) {
           var value = data[j++];
-          if (value == 12 && data[j++] == 0) {
+          var validationCommand = null;
+          if (value == 12) {
+            var q = data[j++];
+            if (q == 0) {
+              // The CFF specification state that the 'dotsection' command
+              // (12, 0) is deprecated and treated as a no-op, but all Type2
+              // charstrings processors should support them. Unfortunately
+              // the font sanitizer don't. As a workaround the sequence (12, 0)
+              // is replaced by a useless (0, hmoveto).
               data[j - 2] = 139;
               data[j - 1] = 22;
-          } else if (value === 28) {
+              stackSize = 0;
+            } else {
+              validationCommand = CharstringValidationData12[q];
+            }
+          } else if (value === 28) { // number (16 bit)
             j += 2;
-          } else if (value >= 247 && value <= 254) {
+            stackSize++;
+          } else if (value == 14) {
+            if (stackSize >= 4) {
+              // TODO fix deprecated endchar construct for Windows
+              stackSize -= 4;
+            }
+          } else if (value >= 32 && value <= 246) {  // number
+            stackSize++;
+          } else if (value >= 247 && value <= 254) {  // number (+1 bytes)
             j++;
-          } else if (value == 255) {
+            stackSize++;
+          } else if (value == 255) {  // number (32 bit)
             j += 4;
-          }
+            stackSize++;
+          } else if (value == 18 || value == 23) {
+            hints += stackSize >> 1;
+            validationCommand = CharstringValidationData[value];
+          } else if (value == 19 || value == 20) {
+            hints += stackSize >> 1;
+            j += (hints + 7) >> 3; // skipping right amount of hints flag data
+            stackSize = 0;
+          } else {
+            validationCommand = CharstringValidationData[value];
+          }
+          if (validationCommand) {
+            if ('min' in validationCommand) {
+              if (!undefStack && stackSize < validationCommand.min) {
+                warn('Not enough parameters for ' + validationCommand.id +
+                     '; actual: ' + stackSize +
+                     ', expected: ' + validationCommand.min);
+                valid = false;
+                break;
+              }
+            }
+            if ('stackDelta' in validationCommand) {
+              stackSize += validationCommand.stackDelta;
+            } else if (validationCommand.resetStack) {
+              stackSize = 0;
+              undefStack = false;
+            } else if (validationCommand.undefStack) {
+              stackSize = 0;
+              undefStack = true;
+            }
+          }
+        }
+        if (!valid) {
+          // resetting invalid charstring to single 'endchar'
+          charStrings.set(i, new Uint8Array([14]));
         }
       }
       return charStrings;
     },
     parsePrivateDict: function CFFParser_parsePrivateDict(parentDict) {
       // no private dict, do nothing
       if (!parentDict.hasName('Private'))
         return;
@@ -20560,16 +20890,20 @@ var CFFIndex = (function CFFIndexClosure
     this.objects = [];
     this.length = 0;
   }
   CFFIndex.prototype = {
     add: function CFFIndex_add(data) {
       this.length += data.length;
       this.objects.push(data);
     },
+    set: function CFFIndex_set(index, data) {
+      this.length += data.length - this.objects[index].length;
+      this.objects[index] = data;
+    },
     get: function CFFIndex_get(index) {
       return this.objects[index];
     },
     get count() {
       return this.objects.length;
     }
   };
   return CFFIndex;
@@ -21160,16 +21494,24 @@ var CFFCompiler = (function CFFCompilerC
           data.push(objects[i][j]);
       }
       return data;
     }
   };
   return CFFCompiler;
 })();
 
+// Workaround for Private Use Area characters in Chrome on Windows
+// http://code.google.com/p/chromium/issues/detail?id=122465
+// https://github.com/mozilla/pdf.js/issues/1689
+(function checkChromeWindows() {
+  if (/Windows.*Chrome/.test(navigator.userAgent)) {
+    SYMBOLIC_FONT_GLYPH_OFFSET = 0xF100;
+  }
+})();
 
 
 var GlyphsUnicode = {
   A: 0x0041,
   AE: 0x00C6,
   AEacute: 0x01FC,
   AEmacron: 0x01E2,
   AEsmall: 0xF7E6,
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -2089,29 +2089,42 @@ var PageView = function pageView(contain
 
     setupAnnotations(this.pdfPage, this.viewport);
     div.setAttribute('data-loaded', true);
   };
 
   this.beforePrint = function pageViewBeforePrint() {
     var pdfPage = this.pdfPage;
     var viewport = pdfPage.getViewport(1);
-
+    // Use the same hack we use for high dpi displays for printing to get better
+    // output until bug 811002 is fixed in FF.
+    var PRINT_OUTPUT_SCALE = 2;
     var canvas = this.canvas = document.createElement('canvas');
-    canvas.width = viewport.width;
-    canvas.height = viewport.height;
-    canvas.style.width = viewport.width + 'pt';
-    canvas.style.height = viewport.height + 'pt';
+    canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
+    canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
+    canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt';
+    canvas.style.height = (PRINT_OUTPUT_SCALE * viewport.height) + 'pt';
+    var cssScale = 'scale(' + (1 / PRINT_OUTPUT_SCALE) + ', ' +
+                              (1 / PRINT_OUTPUT_SCALE) + ')';
+    CustomStyle.setProp('transform' , canvas, cssScale);
+    CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
 
     var printContainer = document.getElementById('printContainer');
     printContainer.appendChild(canvas);
 
     var self = this;
     canvas.mozPrintCallback = function(obj) {
       var ctx = obj.context;
+
+      ctx.save();
+      ctx.fillStyle = 'rgb(255, 255, 255)';
+      ctx.fillRect(0, 0, canvas.width, canvas.height);
+      ctx.restore();
+      ctx.scale(PRINT_OUTPUT_SCALE, PRINT_OUTPUT_SCALE);
+
       var renderContext = {
         canvasContext: ctx,
         viewport: viewport
       };
 
       pdfPage.render(renderContext).then(function() {
         // Tell the printEngine that rendering this canvas/page has finished.
         obj.done();