]> git.parisson.com Git - pdf.js.git/commitdiff
Get rid of the TrueType class, adapt the code to conventions and new code
authorVivien Nicolas <21@vingtetun.org>
Tue, 21 Jun 2011 04:49:59 +0000 (06:49 +0200)
committerVivien Nicolas <21@vingtetun.org>
Tue, 21 Jun 2011 04:49:59 +0000 (06:49 +0200)
fonts.js
pdf.js

index ff749a3f5a51bc9d6e2fa836765095439d3c1b0e..ad3d4fd35b3415334fdf8d312a28c7f12c96b12b 100644 (file)
--- a/fonts.js
+++ b/fonts.js
@@ -39,8 +39,8 @@ var Fonts = {
     return this._active;
   },
 
-  set active(aName) {
-    this._active = this[aName];
+  set active(name) {
+    this._active = this[name];
   },
 
   charsToUnicode: function fonts_chars2Unicode(chars) {
@@ -49,7 +49,7 @@ var Fonts = {
       return chars;
 
     // if we translated this string before, just grab it from the cache
-    var str = active.cache[chars] || "";
+    var str = active.cache[chars];
     if (str)
       return str;
 
@@ -58,6 +58,7 @@ var Fonts = {
     if (!encoding)
       return chars;
 
+    str = "";
     for (var i = 0; i < chars.length; ++i) {
       var charcode = chars.charCodeAt(i);
       var unicode = encoding[charcode];
@@ -88,23 +89,23 @@ var Fonts = {
  *   type1Font.bind();
  */
 var Font = (function () {
-  var constructor = function font_constructor(aName, aFile, aProperties) {
-    this.name = aName;
-    this.encoding = aProperties.encoding;
+  var constructor = function font_constructor(name, file, properties) {
+    this.name = name;
+    this.encoding = properties.encoding;
 
     // If the font has already been decoded simply return it
-    if (Fonts[aName]) {
-      this.font = Fonts[aName].data;
+    if (Fonts[name]) {
+      this.font = Fonts[name].data;
       return;
     }
     fontCount++;
-    fontName = aName;
+    fontName = name;
 
     // 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 (aProperties.ignore || kDisableFonts) {
-      Fonts[aName] = {
-        data: aFile,
+    if (properties.ignore || properties.type == "TrueType" || kDisableFonts) {
+      Fonts[name] = {
+        data: file,
         loading: false,
         properties: {},
         cache: Object.create(null)
@@ -112,41 +113,31 @@ var Font = (function () {
       return;
     }
 
-    switch (aProperties.type) {
+    switch (properties.type) {
       case "Type1":
-        var cff = new CFF(aName, aFile, aProperties);
+        var cff = new CFF(name, file, properties);
         this.mimetype = "font/opentype";
 
         // Wrap the CFF data inside an OTF font file
-        this.font = this.cover(aName, cff, aProperties);
+        this.font = this.convert(name, cff, properties);
         break;
 
       case "TrueType":
-        // TrueType is disabled for the moment since the sanitizer prevent it
-        // from loading due to missing tables
-        return Fonts[aName] = {
-          data: null,
-          properties: {
-            encoding: {},
-            charset: null
-          },
-          loading: false,
-          cache: Object.create(null)
-        };
-
         this.mimetype = "font/opentype";
-        var ttf = new TrueType(aName, aFile, aProperties);
-        this.font = ttf.data;
+
+        // Repair the TrueType file if it is can be damaged in the point of
+        // view of the sanitizer
+        this.font = this.checkAndRepair(name, file, properties);
         break;
 
       default:
-        warn("Font " + aProperties.type + " is not supported");
+        warn("Font " + properties.type + " is not supported");
         break;
     }
 
-    Fonts[aName] = {
+    Fonts[name] = {
       data: this.font,
-      properties: aProperties,
+      properties: properties,
       loading: true,
       cache: Object.create(null)
     }
@@ -155,6 +146,200 @@ var Font = (function () {
     this.bind();
   };
 
+  function stringToArray(str) {
+    var array = [];
+    for (var i = 0; i < str.length; ++i)
+      array[i] = str.charCodeAt(i);
+
+    return array;
+  };
+
+  function string16(value) {
+    return String.fromCharCode((value >> 8) & 0xff) +
+           String.fromCharCode(value & 0xff);
+  };
+
+  function string32(value) {
+    return String.fromCharCode((value >> 24) & 0xff) +
+           String.fromCharCode((value >> 16) & 0xff) +
+           String.fromCharCode((value >> 8) & 0xff) +
+           String.fromCharCode(value & 0xff);
+  };
+
+  function createOpenTypeHeader(sfnt, file, offsets, numTables) {
+    // sfnt version (4 bytes)
+    var header = sfnt;
+
+    // numTables (2 bytes)
+    header += string16(numTables);
+
+    // searchRange (2 bytes)
+    var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
+    var searchRange = tablesMaxPower2 * 16;
+    header += string16(searchRange);
+
+    // entrySelector (2 bytes)
+    header += string16(Math.log(tablesMaxPower2) / Math.log(2));
+
+    // rangeShift (2 bytes)
+    header += string16(numTables * 16 - searchRange);
+
+    file.set(stringToArray(header), offsets.currentOffset);
+    offsets.currentOffset += header.length;
+    offsets.virtualOffset += header.length;
+  };
+
+  function createTableEntry(file, offsets, tag, data) {
+    // offset
+    var offset = offsets.virtualOffset;
+
+    // Per spec tables must be 4-bytes align so add padding as needed
+    while (data.length & 3)
+      data.push(0x00);
+
+    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 tableEntry = tag + string32(checksum) + string32(offset) + string32(length);
+    tableEntry = stringToArray(tableEntry);
+    file.set(tableEntry, offsets.currentOffset);
+
+    offsets.currentOffset += tableEntry.length;
+    offsets.virtualOffset += data.length;
+  };
+
+  function getRanges(glyphs) {
+    // Array.sort() sorts by characters, not numerically, so convert to an
+    // array of characters.
+    var codes = [];
+    var length = glyphs.length;
+    for (var n = 0; n < length; ++n)
+      codes.push(String.fromCharCode(glyphs[n].unicode))
+    codes.sort();
+
+    // Split the sorted codes into ranges.
+    var ranges = [];
+    for (var n = 0; n < length; ) {
+      var start = codes[n++].charCodeAt(0);
+      var end = start;
+      while (n < length && end + 1 == codes[n].charCodeAt(0)) {
+        ++end;
+        ++n;
+      }
+      ranges.push([start, end]);
+    }
+    return ranges;
+  };
+
+  function createCMAPTable(glyphs) {
+    var ranges = getRanges(glyphs);
+
+    var headerSize = (12 * 2 + (ranges.length * 4 * 2));
+    var segCount = ranges.length + 1;
+    var segCount2 = segCount * 2;
+    var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
+    var searchEntry = Math.log(segCount) / Math.log(2);
+    var rangeShift = 2 * segCount - searchRange;
+
+    var cmap = "\x00\x00" + // version
+               "\x00\x01" + // numTables
+               "\x00\x03" + // platformID
+               "\x00\x01" + // encodingID
+               "\x00\x00\x00\x0C" + // start of the table record
+               "\x00\x04" + // format
+               string16(headerSize) + // length
+               "\x00\x00" + // languages
+               string16(segCount2) +
+               string16(searchRange) +
+               string16(searchEntry) +
+               string16(rangeShift);
+
+    // Fill up the 4 parallel arrays describing the segments.
+    var startCount = "";
+    var endCount = "";
+    var idDeltas = "";
+    var idRangeOffsets = "";
+    var glyphsIds = "";
+    var bias = 0;
+    for (var i = 0; i < segCount - 1; i++) {
+      var range = ranges[i];
+       var start = range[0];
+      var end = range[1];
+      var delta = (((start - 1) - bias) ^ 0xffff) + 1;
+      bias += (end - start + 1);
+
+      startCount += string16(start);
+      endCount += string16(end);
+      idDeltas += string16(delta);
+      idRangeOffsets += string16(0);
+
+      for (var j = start; j <= end; j++)
+        glyphsIds += String.fromCharCode(j);
+    }
+
+    startCount += "\xFF\xFF";
+    endCount += "\xFF\xFF";
+    idDeltas += "\x00\x01";
+    idRangeOffsets += "\x00\x00";
+
+    return stringToArray(cmap + endCount + "\x00\x00" + startCount +
+                         idDeltas + idRangeOffsets + glyphsIds);
+  };
+
+  function createOS2Table() {
+    var OS2 = stringToArray(
+        "\x00\x03" + // version
+        "\x02\x24" + // xAvgCharWidth
+        "\x01\xF4" + // usWeightClass
+        "\x00\x05" + // usWidthClass
+        "\x00\x00" + // fstype
+        "\x02\x8A" + // ySubscriptXSize
+        "\x02\xBB" + // ySubscriptYSize
+        "\x00\x00" + // ySubscriptXOffset
+        "\x00\x8C" + // ySubscriptYOffset
+        "\x02\x8A" + // ySuperScriptXSize
+        "\x02\xBB" + // ySuperScriptYSize
+        "\x00\x00" + // ySuperScriptXOffset
+        "\x01\xDF" + // ySuperScriptYOffset
+        "\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)
+        "\x2A\x32\x31\x2A" + // achVendID
+        "\x00\x20" + // fsSelection
+        "\x00\x2D" + // usFirstCharIndex
+        "\x00\x7A" + // usLastCharIndex
+        "\x00\x03" + // sTypoAscender
+        "\x00\x20" + // sTypeDescender
+        "\x00\x38" + // sTypoLineGap
+        "\x00\x5A" + // usWinAscent
+        "\x02\xB4" + // usWinDescent
+        "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
+        "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
+        "\x00\x00" + // sxHeight
+        "\x00\x00" + // sCapHeight
+        "\x00\x01" + // usDefaultChar
+        "\x00\xCD" + // usBreakChar
+        "\x00\x02"   // usMaxContext
+      );
+    return OS2;
+  };
+
   /**
    * A bunch of the OpenType code is duplicate between this class and the
    * TrueType code, this is intentional and will merge in a future version
@@ -169,178 +354,182 @@ var Font = (function () {
     mimetype: null,
     encoding: null,
 
-    bind: function font_bind() {
-      var data = this.font;
-      var fontName = this.name;
-
-      /** 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
-      var canvas = document.createElement("canvas");
-      var style = "border: 1px solid black; position:absolute; top: " +
-                   (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px";
-      canvas.setAttribute("style", style);
-      canvas.setAttribute("width", 340);
-      canvas.setAttribute("heigth", 100);
-      document.body.appendChild(canvas);
-
-      // Get the font size canvas think it will be for 'spaces'
-      var ctx = canvas.getContext("2d");
-      ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
-      var testString = "   ";
-
-      // When debugging use the characters provided by the charsets to visually
-      // see what's happening instead of 'spaces'
-      var debug = false;
-      if (debug) {
-        var name = document.createElement("font");
-        name.setAttribute("style", "position: absolute; left: 20px; top: " +
-                          (100 * fontCount + 60) + "px");
-        name.innerHTML = fontName;
-        document.body.appendChild(name);
-
-        // Retrieve font charset
-        var charset = Fonts[fontName].properties.charset || [];
-
-        // if the charset is too small make it repeat a few times
-        var count = 30;
-        while (count-- && charset.length <= 30)
-          charset = charset.concat(charset.slice());
-
-        for (var i = 0; i < charset.length; i++) {
-          var unicode = GlyphsUnicode[charset[i]];
-          if (!unicode)
-            continue;
-          testString += String.fromCharCode(unicode);
+    checkAndRepair: function font_checkAndRepair(name, font, properties) {
+      function readTableEntry(file) {
+        // tag
+        var tag = file.getBytes(4);
+        tag = String.fromCharCode(tag[0]) +
+              String.fromCharCode(tag[1]) +
+              String.fromCharCode(tag[2]) +
+              String.fromCharCode(tag[3]);
+
+        var checksum = FontsUtils.bytesToInteger(file.getBytes(4));
+        var offset = FontsUtils.bytesToInteger(file.getBytes(4));
+        var length = FontsUtils.bytesToInteger(file.getBytes(4));
+
+        // Read the table associated data
+        var currentPosition = file.pos;
+        file.pos = file.start + offset;
+
+        var data = file.getBytes(length);
+        file.pos = currentPosition;
+
+        return {
+          tag: tag,
+          checksum: checksum,
+          length: offset,
+          offset: length,
+          data: data
         }
+      };
 
-        ctx.fillText(testString, 20, 20);
-      }
-
-      // Periodicaly check for the width of the testString, it will be
-      // different once the real font has loaded
-      var textWidth = ctx.measureText(testString).width;
-
-      var interval = window.setInterval(function canvasInterval(self) {
-        this.start = this.start || Date.now();
-        ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
-
-        // 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 + " for charset: " + charset + " loaded?");
-          this.start = 0;
-        } else if (textWidth != ctx.measureText(testString).width) {
-          window.clearInterval(interval);
-          Fonts[fontName].loading = false;
-          this.start = 0;
+      function readOpenTypeHeader(ttf) {
+        return {
+          version: ttf.getBytes(4),
+          numTables: FontsUtils.bytesToInteger(ttf.getBytes(2)),
+          searchRange: FontsUtils.bytesToInteger(ttf.getBytes(2)),
+          entrySelector: FontsUtils.bytesToInteger(ttf.getBytes(2)),
+          rangeShift: FontsUtils.bytesToInteger(ttf.getBytes(2))
         }
+      };
 
-        if (debug)
-          ctx.fillText(testString, 20, 50);
-      }, 30, this);
-
-      /** Hack end */
-
-      // Get the base64 encoding of the binary font data
-      var str = "";
-      var length = data.length;
-      for (var i = 0; i < length; ++i)
-        str += String.fromCharCode(data[i]);
-
-      var base64 = window.btoa(str);
+      // Check that required tables are present
+      var requiredTables = [ "OS/2", "cmap", "head", "hhea",
+                             "hmtx", "maxp", "name", "post" ];
 
-      // Add the @font-face rule to the document
-      var url = "url(data:" + this.mimetype + ";base64," + base64 + ");";
-      var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
-      var styleSheet = document.styleSheets[0];
-      styleSheet.insertRule(rule, styleSheet.length);
-    },
+      var header = readOpenTypeHeader(font);
+      var numTables = header.numTables;
 
-    cover: function font_cover(aName, aFont, aProperties) {
-      var otf = Uint8Array(kMaxFontFileSize);
+      // This keep a reference to the CMap and the post tables since they can
+      // be rewritted
+      var cmap, post;
 
-      function stringToArray(str) {
-        var array = [];
-        for (var i = 0; i < str.length; ++i)
-          array[i] = str.charCodeAt(i);
-        return array;
-      }
+      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")
+            post = table;
 
-      function string16(value) {
-        return String.fromCharCode((value >> 8) & 0xff) +
-               String.fromCharCode(value & 0xff);
+          requiredTables.splice(index, 1);
+        }
+        tables.push(table);
       }
 
-      function string32(value) {
-        return String.fromCharCode((value >> 24) & 0xff) +
-               String.fromCharCode((value >> 16) & 0xff) +
-               String.fromCharCode((value >> 8) & 0xff) +
-               String.fromCharCode(value & 0xff);
-      }
+      // If any tables are still in the array this means some required tables are
+      // missing, which means that we need to rebuild the font in order to pass
+      // the sanitizer.
+      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);
+
+        // The offsets object holds at the same time a representation of where
+        // to write the table entry information about a table and another offset
+        // representing the offset where to put the actual data of a particular
+        // table
+        var numTables = header.numTables + requiredTables.length;
+        var offsets = {
+          currentOffset: 0,
+          virtualOffset: numTables * (4 * 4)
+        };
 
-      function createOpenTypeHeader(aFile, aOffsets, numTables) {
-        var header = "";
+        // The new numbers of tables will be the last one plus the num of missing
+        // tables
+        createOpenTypeHeader("\x00\x01\x00\x00", ttf, offsets, numTables);
 
-        // sfnt version (4 bytes)
-        header += "\x4F\x54\x54\x4F";
+        // Insert the missing table
+        var OS2 = createOS2Table();
+        tables.push({
+          tag: "OS/2",
+          data: OS2
+        });
 
-        // numTables (2 bytes)
-        header += string16(numTables);
+        // 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]]
+          });
+        }
 
-        // searchRange (2 bytes)
-        var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
-        var searchRange = tablesMaxPower2 * 16;
-        header += string16(searchRange);
+        // Replace the old CMAP table with a shiny new one
+        cmap.data = createCMAPTable(glyphs);
+
+        // Rewrite the 'post' table if needed
+        if (!post) {
+          post =
+            "\x00\x03\x00\x00" + // Version number
+            "\x00\x00\x01\x00" + // 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
+
+          tables.unshift({
+            tag: "post",
+            data: stringToArray(post)
+          });
+        }
 
-        // entrySelector (2 bytes)
-        header += string16(Math.log(tablesMaxPower2) / Math.log(2));
+        // Tables needs to be written by ascendant alphabetic order
+        tables.sort(function tables_sort(a, b) {
+          return a.tag > b.tag;
+        });
 
-        // rangeShift (2 bytes)
-        header += string16(numTables * 16 - searchRange);
+        // rewrite the tables but tweak offsets
+        for (var i = 0; i < tables.length; i++) {
+          var table = tables[i];
+          var data = [];
 
-        aFile.set(stringToArray(header), aOffsets.currentOffset);
-        aOffsets.currentOffset += header.length;
-        aOffsets.virtualOffset += header.length;
-      }
+          var tableData = table.data;
+          for (var j = 0; j < tableData.length; j++)
+            data.push(tableData[j]);
+          createTableEntry(ttf, offsets, table.tag, data);
+        }
 
-      function createTableEntry(aFile, aOffsets, aTag, aData) {
-        // offset
-        var offset = aOffsets.virtualOffset;
+        // Add the table datas
+        for (var i = 0; i < tables.length; i++) {
+          var table = tables[i];
+          var tableData = table.data;
+          ttf.set(tableData, offsets.currentOffset);
+          offsets.currentOffset += tableData.length;
 
-        // Per spec tables must be 4-bytes align so add padding as needed
-        while (aData.length & 3)
-          aData.push(0x00);
+          // 4-byte aligned data
+          while (offsets.currentOffset & 3)
+            offsets.currentOffset++;
+        }
 
-        // length
-        var length = aData.length;
+        var fontData = [];
+        for (var i = 0; i < offsets.currentOffset; i++)
+          fontData.push(ttf[i]);
 
-        // checksum
-        var checksum = aTag.charCodeAt(0) +
-                       aTag.charCodeAt(1) +
-                       aTag.charCodeAt(2) +
-                       aTag.charCodeAt(3) +
-                       offset +
-                       length;
+        return fontData;
+      } else if (requiredTables.length) {
+        error("Table " + requiredTables[0] + " is missing from the TrueType font");
+      }
 
-        var tableEntry = aTag + string32(checksum) + string32(offset) + string32(length);
-        tableEntry = stringToArray(tableEntry);
-        aFile.set(tableEntry, aOffsets.currentOffset);
+      return font.getBytes();
+    },
 
-        aOffsets.currentOffset += tableEntry.length;
-        aOffsets.virtualOffset += aData.length;
-      }
+    convert: function font_convert(name, font, properties) {
+      var otf = Uint8Array(kMaxFontFileSize);
 
-      function createNameTable(aName) {
+      function createNameTable(name) {
         var names = [
           "See original licence",  // Copyright
-          aName,                   // Font family
+          name,                   // Font family
           "undefined",             // Font subfamily (font weight)
           "uniqueID",              // Unique ID
-          aName,                   // Full font name
+          name,                   // Full font name
           "0.1",                   // Version
           "undefined",             // Postscript name
           "undefined",             // Trademark
@@ -374,94 +563,17 @@ var Font = (function () {
         return name;
       }
 
-      function getRanges(glyphs) {
-        // Array.sort() sorts by characters, not numerically, so convert to an
-        // array of characters.
-        var codes = [];
-        var length = glyphs.length;
-        for (var n = 0; n < length; ++n)
-          codes.push(String.fromCharCode(glyphs[n].unicode))
-        codes.sort();
-
-        // Split the sorted codes into ranges.
-        var ranges = [];
-        for (var n = 0; n < length; ) {
-          var start = codes[n++].charCodeAt(0);
-          var end = start;
-          while (n < length && end + 1 == codes[n].charCodeAt(0)) {
-            ++end;
-            ++n;
-          }
-          ranges.push([start, end]);
-        }
-        return ranges;
-      }
-
-      function createCMAPTable(aGlyphs) {
-        var ranges = getRanges(aGlyphs);
-
-        var headerSize = (12 * 2 + (ranges.length * 4 * 2));
-        var segCount = ranges.length + 1;
-        var segCount2 = segCount * 2;
-        var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
-        var searchEntry = Math.log(segCount) / Math.log(2);
-        var rangeShift = 2 * segCount - searchRange;
-
-        var cmap = "\x00\x00" + // version
-                   "\x00\x01" + // numTables
-                   "\x00\x03" + // platformID
-                   "\x00\x01" + // encodingID
-                   "\x00\x00\x00\x0C" + // start of the table record
-                   "\x00\x04" + // format
-                   string16(headerSize) + // length
-                   "\x00\x00" + // languages
-                   string16(segCount2) +
-                   string16(searchRange) +
-                   string16(searchEntry) +
-                   string16(rangeShift);
-
-        // Fill up the 4 parallel arrays describing the segments.
-        var startCount = "";
-        var endCount = "";
-        var idDeltas = "";
-        var idRangeOffsets = "";
-        var glyphsIds = "";
-        var bias = 0;
-        for (var i = 0; i < segCount - 1; i++) {
-          var range = ranges[i];
-          var start = range[0];
-          var end = range[1];
-          var delta = (((start - 1) - bias) ^ 0xffff) + 1;
-          bias += (end - start + 1);
-
-          startCount += string16(start);
-          endCount += string16(end);
-          idDeltas += string16(delta);
-          idRangeOffsets += string16(0);
-
-          for (var j = start; j <= end; j++)
-            glyphsIds += String.fromCharCode(j);
-        }
-
-        startCount += "\xFF\xFF";
-        endCount += "\xFF\xFF";
-        idDeltas += "\x00\x01";
-        idRangeOffsets += "\x00\x00";
-
-        return stringToArray(cmap + endCount + "\x00\x00" + startCount +
-                   idDeltas + idRangeOffsets + glyphsIds);
-      }
-
       // Required Tables
-      var CFF = aFont.data, // PostScript Font Program
-        OS2 = [],         // OS/2 and Windows Specific metrics
-        cmap = [],        // Character to glyphs mapping
-        head = [],        // Font eader
-        hhea = [],        // Horizontal header
-        hmtx = [],        // Horizontal metrics
-        maxp = [],        // Maximum profile
-        name = [],        // Naming tables
-        post = [];        // PostScript informations
+      var CFF =
+        font.data,  // PostScript Font Program
+        OS2,         // OS/2 and Windows Specific metrics
+        cmap,        // Character to glyphs mapping
+        head,        // Font eader
+        hhea,        // Horizontal header
+        hmtx,        // Horizontal metrics
+        maxp,        // Maximum profile
+        name,        // Naming tables
+        post;        // PostScript informations
       var tables = [CFF, OS2, cmap, head, hhea, hmtx, maxp, name, post];
 
       // The offsets object holds at the same time a representation of where
@@ -473,59 +585,18 @@ var Font = (function () {
         virtualOffset: tables.length * (4 * 4)
       };
 
-      // For files with only one font the offset table is the first thing of the
-      // file
-      createOpenTypeHeader(otf, offsets, tables.length);
+      // It there is only one font, offset table is the first bytes of the file
+      createOpenTypeHeader("\x4F\x54\x54\x4F", otf, offsets, tables.length);
 
-      // TODO: It is probable that in a future we want to get rid of this glue
-      // between the CFF and the OTF format in order to be able to embed TrueType
-      // data.
+      /** CFF */
       createTableEntry(otf, offsets, "CFF ", CFF);
 
       /** OS/2 */
-      OS2 = stringToArray(
-            "\x00\x03" + // version
-            "\x02\x24" + // xAvgCharWidth
-            "\x01\xF4" + // usWeightClass
-            "\x00\x05" + // usWidthClass
-            "\x00\x00" + // fstype
-            "\x02\x8A" + // ySubscriptXSize
-            "\x02\xBB" + // ySubscriptYSize
-            "\x00\x00" + // ySubscriptXOffset
-            "\x00\x8C" + // ySubscriptYOffset
-            "\x02\x8A" + // ySuperScriptXSize
-            "\x02\xBB" + // ySuperScriptYSize
-            "\x00\x00" + // ySuperScriptXOffset
-            "\x01\xDF" + // ySuperScriptYOffset
-            "\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)
-            "\x2A\x32\x31\x2A" + // achVendID
-            "\x00\x20" + // fsSelection
-            "\x00\x2D" + // usFirstCharIndex
-            "\x00\x7A" + // usLastCharIndex
-            "\x00\x03" + // sTypoAscender
-            "\x00\x20" + // sTypeDescender
-            "\x00\x38" + // sTypoLineGap
-            "\x00\x5A" + // usWinAscent
-            "\x02\xB4" + // usWinDescent
-            "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31)
-            "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63)
-            "\x00\x00" + // sxHeight
-            "\x00\x00" + // sCapHeight
-            "\x00\x01" + // usDefaultChar
-            "\x00\xCD" + // usBreakChar
-            "\x00\x02"   // usMaxContext
-      );
+      OS2 = createOS2Table();
       createTableEntry(otf, offsets, "OS/2", OS2);
 
       //XXX Getting charstrings here seems wrong since this is another CFF glue
-      var charstrings = aFont.getOrderedCharStrings(aProperties.glyphs);
+      var charstrings = font.getOrderedCharStrings(properties.glyphs);
 
       /** CMAP */
       cmap = createCMAPTable(charstrings);
@@ -593,7 +664,7 @@ var Font = (function () {
       createTableEntry(otf, offsets, "maxp", maxp);
 
       /** NAME */
-      name = stringToArray(createNameTable(aName));
+      name = stringToArray(createNameTable(name));
       createTableEntry(otf, offsets, "name", name);
 
       /** POST */
@@ -622,6 +693,97 @@ var Font = (function () {
       for (var i = 0; i < offsets.currentOffset; i++)
         fontData.push(otf[i]);
       return fontData;
+    },
+
+    bind: function font_bind() {
+      var data = this.font;
+      var fontName = this.name;
+
+      /** 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
+      var canvas = document.createElement("canvas");
+      var style = "border: 1px solid black; position:absolute; top: " +
+                   (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px";
+      canvas.setAttribute("style", style);
+      canvas.setAttribute("width", 340);
+      canvas.setAttribute("heigth", 100);
+      document.body.appendChild(canvas);
+
+      // Get the font size canvas think it will be for 'spaces'
+      var ctx = canvas.getContext("2d");
+      ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
+      var testString = "   ";
+
+      // When debugging use the characters provided by the charsets to visually
+      // see what's happening instead of 'spaces'
+      var debug = false;
+      if (debug) {
+        var name = document.createElement("font");
+        name.setAttribute("style", "position: absolute; left: 20px; top: " +
+                          (100 * fontCount + 60) + "px");
+        name.innerHTML = fontName;
+        document.body.appendChild(name);
+
+        // Retrieve font charset
+        var charset = Fonts[fontName].properties.charset || [];
+
+        // if the charset is too small make it repeat a few times
+        var count = 30;
+        while (count-- && charset.length <= 30)
+          charset = charset.concat(charset.slice());
+
+        for (var i = 0; i < charset.length; i++) {
+          var unicode = GlyphsUnicode[charset[i]];
+          if (!unicode)
+            continue;
+          testString += String.fromCharCode(unicode);
+        }
+
+        ctx.fillText(testString, 20, 20);
+      }
+
+      // Periodicaly check for the width of the testString, it will be
+      // different once the real font has loaded
+      var textWidth = ctx.measureText(testString).width;
+
+      var interval = window.setInterval(function canvasInterval(self) {
+        this.start = this.start || Date.now();
+        ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
+
+        // 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 + " for charset: " + charset + " loaded?");
+          this.start = 0;
+        } else if (textWidth != ctx.measureText(testString).width) {
+          window.clearInterval(interval);
+          Fonts[fontName].loading = false;
+          this.start = 0;
+        }
+
+        if (debug)
+          ctx.fillText(testString, 20, 50);
+      }, 30, this);
+
+      /** Hack end */
+
+      // Get the base64 encoding of the binary font data
+      var str = "";
+      var length = data.length;
+      for (var i = 0; i < length; ++i)
+        str += String.fromCharCode(data[i]);
+
+      var base64 = window.btoa(str);
+
+      // Add the @font-face rule to the document
+      var url = "url(data:" + this.mimetype + ";base64," + base64 + ");";
+      var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
+      var styleSheet = document.styleSheets[0];
+      styleSheet.insertRule(rule, styleSheet.length);
     }
   };
 
@@ -635,31 +797,31 @@ var Font = (function () {
  */
 var FontsUtils = {
   _bytesArray: new Uint8Array(4),
-  integerToBytes: function fu_integerToBytes(aValue, aBytesCount) {
+  integerToBytes: function fu_integerToBytes(value, bytesCount) {
     var bytes = this._bytesArray;
 
-    if (aBytesCount == 1) {
-      bytes.set([aValue]);
+    if (bytesCount == 1) {
+      bytes.set([value]);
       return bytes[0];
-    } else if (aBytesCount == 2) {
-      bytes.set([aValue >> 8, aValue]);
+    } else if (bytesCount == 2) {
+      bytes.set([value >> 8, value]);
       return [bytes[0], bytes[1]];
-    } else if (aBytesCount == 4) {
-      bytes.set([aValue >> 24, aValue >> 16, aValue >> 8, aValue]);
+    } else if (bytesCount == 4) {
+      bytes.set([value >> 24, value >> 16, value >> 8, value]);
       return [bytes[0], bytes[1], bytes[2], bytes[3]];
     }
   },
 
-  bytesToInteger: function fu_bytesToInteger(aBytesArray) {
+  bytesToInteger: function fu_bytesToInteger(bytesArray) {
     var value = 0;
-    for (var i = 0; i < aBytesArray.length; i++)
-      value = (value << 8) + aBytesArray[i];
+    for (var i = 0; i < bytesArray.length; i++)
+      value = (value << 8) + bytesArray[i];
     return value;
   },
 
-  getMaxPower2: function fu_getMaxPower2(aNumber) {
+  getMaxPower2: function fu_getMaxPower2(number) {
     var maxPower = 0;
-    var value = aNumber;
+    var value = number;
     while (value >= 2) {
       value /= 2;
       maxPower++;
@@ -673,391 +835,6 @@ var FontsUtils = {
 };
 
 
-/**
- * The TrueType class verify that the ttf embedded inside the PDF is correct in
- * the point of view of the OTS sanitizer and rewrite it on the fly otherwise.
- *
- * At the moment the rewiting only support rewriting missing 'OS/2' table.
- * This class is unused at the moment since the 'cmap' table of the test 
- * document is not missing but use and old version of the 'cmap' table that
- * is deprecated and  not supported by the sanitizer...
- *
- */
-var TrueType = function(aName, aFile, aProperties) {
-  var header = this._readOpenTypeHeader(aFile);
-  var numTables = header.numTables;
-
-  // Check that required tables are present
-  var requiredTables = [
-    "OS/2",
-    "cmap",
-    "head",
-    "hhea",
-    "hmtx",
-    "maxp",
-    "name",
-    "post"
-  ];
-
-  var originalCMAP = null;
-
-  var tables = [];
-  for (var i = 0; i < numTables; i++) {
-    var table = this._readTableEntry(aFile);
-    var index = requiredTables.indexOf(table.tag);
-    if (index != -1) {
-      if (table.tag == "cmap")
-        originalCMAP = table;
-
-      tables.push(table);
-    }
-  }
-
-  // If any tables are still in the array this means some required tables are
-  // missing, which means that we need to rebuild the font in order to pass
-  // the sanitizer.
-  if (requiredTables.length && requiredTables[0] == "OS/2") {
-    var OS2 = [
-      0x00, 0x03, // version
-      0x02, 0x24, // xAvgCharWidth
-      0x01, 0xF4, // usWeightClass
-      0x00, 0x05, // usWidthClass
-      0x00, 0x00, // fstype
-      0x02, 0x8A, // ySubscriptXSize
-      0x02, 0xBB, // ySubscriptYSize
-      0x00, 0x00, // ySubscriptXOffset
-      0x00, 0x8C, // ySubscriptYOffset
-      0x02, 0x8A, // ySuperScriptXSize
-      0x02, 0xBB, // ySuperScriptYSize
-      0x00, 0x00, // ySuperScriptXOffset
-      0x01, 0xDF, // ySuperScriptYOffset
-      0x00, 0x31, // yStrikeOutSize
-      0x01, 0x02, // yStrikeOutPosition
-      0x00, 0x00, // sFamilyClass
-      0x02, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Panose
-      0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 0-31)
-      0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 32-63)
-      0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 64-95)
-      0xFF, 0xFF, 0xFF, 0xFF, // ulUnicodeRange1 (Bits 96-127)
-      0x2A, 0x32, 0x31, 0x2A, // achVendID
-      0x00, 0x20, // fsSelection
-      0x00, 0x2D, // usFirstCharIndex
-      0x00, 0x7A, // usLastCharIndex
-      0x00, 0x03, // sTypoAscender
-      0x00, 0x20, // sTypeDescender
-      0x00, 0x38, // sTypoLineGap
-      0x00, 0x5A, // usWinAscent
-      0x02, 0xB4, // usWinDescent
-      0x00, 0xCE, 0x00, 0x00, // ulCodePageRange1 (Bits 0-31)
-      0x00, 0x01, 0x00, 0x00, // ulCodePageRange2 (Bits 32-63)
-      0x00, 0x00, // sxHeight
-      0x00, 0x00, // sCapHeight
-      0x00, 0x01, // usDefaultChar
-      0x00, 0xCD, // usBreakChar
-      0x00, 0x02  // usMaxContext
-    ];
-
-    // 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 = aProperties.charset;
-    var glyphs = [];
-    for (var i = 0; i < charset.length; i++) {
-      glyphs.push({
-          unicode: GlyphsUnicode[charset[i]]
-      });
-    }
-
-
-    var offsetDelta = 0;
-
-    // Replace the old CMAP table
-    var rewrittedCMAP = this._createCMAPTable(glyphs);
-    offsetDelta = rewrittedCMAP.length - originalCMAP.data.length;
-    originalCMAP.data = rewrittedCMAP;
-
-    // Rewrite the 'post' table if needed
-    var postTable = null;
-    for (var i = 0; i < tables.length; i++) {
-      var table = tables[i];
-      if (table.tag == "post") {
-        postTable = table;
-        break;
-      }
-    }
-
-    if (!postTable) {
-      var post = [
-        0x00, 0x03, 0x00, 0x00, // Version number
-        0x00, 0x00, 0x01, 0x00, // italicAngle
-       0x00, 0x00, // underlinePosition
-        0x00, 0x00, // underlineThickness
-        0x00, 0x00, 0x00, 0x00, // isFixedPitch
-        0x00, 0x00, 0x00, 0x00, // minMemType42
-        0x00, 0x00, 0x00, 0x00, // maxMemType42
-        0x00, 0x00, 0x00, 0x00, // minMemType1
-        0x00, 0x00, 0x00, 0x00  // maxMemType1
-      ];
-
-      offsetDelta += post.length;
-      tables.unshift({
-        tag: "post",
-        data: post
-      });
-    }
-
-    // Create a new file to hold the new version of our truetype with a new
-    // header and new offsets
-    var stream = aFile.stream || aFile;
-    var ttf = new Uint8Array(stream.length + 1024);
-
-    // The new numbers of tables will be the last one plus the num of missing
-    // tables
-    var numTables = header.numTables + 1;
-
-    // The offsets object holds at the same time a representation of where
-    // to write the table entry information about a table and another offset
-    // representing the offset where to draw the actual data of a particular
-    // table
-    var offsets = {
-      currentOffset: 0,
-      virtualOffset: numTables * (4 * 4)
-    };
-
-    // Write the sfnt header with one more table
-    this._createOpenTypeHeader(ttf, offsets, numTables);
-
-    // Insert the missing table
-    tables.unshift({
-      tag: "OS/2",
-      data: OS2
-    });
-
-    // Tables needs to be written by ascendant alphabetic order
-    tables.sort(function tables_sort(a, b) {
-      return a.tag > b.tag;
-    });
-
-    // rewrite the tables but tweak offsets
-    for (var i = 0; i < tables.length; i++) {
-      var table = tables[i];
-      var data = [];
-
-      var tableData = table.data;
-      for (var j = 0; j < tableData.length; j++)
-        data.push(tableData[j]);
-      this._createTableEntry(ttf, offsets, table.tag, data);
-    }
-
-    // Add the table datas
-    for (var i = 0; i < tables.length; i++) {
-      var table = tables[i];
-      var tableData = table.data;
-      ttf.set(tableData, offsets.currentOffset);
-      offsets.currentOffset += tableData.length;
-
-      // 4-byte aligned data
-      while (offsets.currentOffset & 3)
-        offsets.currentOffset++;
-    }
-
-    var fontData = [];
-    for (var i = 0; i < offsets.currentOffset; i++)
-      fontData.push(ttf[i]);
-
-    this.data = fontData;
-    return;
-  } else if (requiredTables.length) {
-    error("Table " + requiredTables[0] + " is missing from the TruType font");
-  } else {
-    this.data = aFile.getBytes();
-  }
-};
-
-TrueType.prototype = {
-  _createOpenTypeHeader: function tt_createOpenTypeHeader(aFile, aOffsets, aNumTables) {
-    // sfnt version (4 bytes)
-    // XXX if we want to merge this function and the one from the Font class
-    // XXX this need to be adapted
-    var version = [0x00, 0x01, 0x00, 0X00];
-
-    // numTables (2 bytes)
-    var numTables = aNumTables;
-
-    // searchRange (2 bytes)
-    var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
-    var searchRange = tablesMaxPower2 * 16;
-
-    // entrySelector (2 bytes)
-    var entrySelector = Math.log(tablesMaxPower2) / Math.log(2);
-
-    // rangeShift (2 bytes)
-    var rangeShift = numTables * 16 - searchRange;
-
-    var header = [].concat(version,
-                           FontsUtils.integerToBytes(numTables, 2),
-                           FontsUtils.integerToBytes(searchRange, 2),
-                           FontsUtils.integerToBytes(entrySelector, 2),
-                           FontsUtils.integerToBytes(rangeShift, 2));
-    aFile.set(header, aOffsets.currentOffset);
-    aOffsets.currentOffset += header.length;
-    aOffsets.virtualOffset += header.length;
-  },
-
-  _createCMAPTable: function font_createCMAPTable(aGlyphs) {
-    var characters = Uint16Array(65535);
-    for (var i = 0; i < aGlyphs.length; i++)
-      characters[aGlyphs[i].unicode] = i + 1;
-
-    // Separate the glyphs into continuous range of codes, aka segment.
-    var ranges = [];
-    var range = [];
-    var count = characters.length;
-    for (var i = 0; i < count; i++) {
-      if (characters[i]) {
-        range.push(i);
-      } else if (range.length) {
-        ranges.push(range.slice());
-        range = [];
-      }
-    }
-
-    // The size in bytes of the header is equal to the size of the
-    // different fields * length of a short + (size of the 4 parallels arrays
-    // describing segments * length of a short).
-    var headerSize = (12 * 2 + (ranges.length * 4 * 2));
-
-    var segCount = ranges.length + 1;
-    var segCount2 = segCount * 2;
-    var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
-    var searchEntry = Math.log(segCount) / Math.log(2);
-    var rangeShift = 2 * segCount - searchRange;
-    var cmap = [].concat(
-      [
-        0x00, 0x00, // version
-        0x00, 0x01, // numTables
-        0x00, 0x03, // platformID
-        0x00, 0x01, // encodingID
-        0x00, 0x00, 0x00, 0x0C, // start of the table record
-        0x00, 0x04  // format
-      ],
-      FontsUtils.integerToBytes(headerSize, 2), // length
-      [0x00, 0x00], // language
-      FontsUtils.integerToBytes(segCount2, 2),
-      FontsUtils.integerToBytes(searchRange, 2),
-      FontsUtils.integerToBytes(searchEntry, 2),
-      FontsUtils.integerToBytes(rangeShift, 2)
-    );
-
-    // Fill up the 4 parallel arrays describing the segments.
-    var startCount = [];
-    var endCount = [];
-    var idDeltas = [];
-    var idRangeOffsets = [];
-    var glyphsIdsArray = [];
-    var bias = 0;
-    for (var i = 0; i < segCount - 1; i++) {
-      var range = ranges[i];
-      var start = FontsUtils.integerToBytes(range[0], 2);
-      var end = FontsUtils.integerToBytes(range[range.length - 1], 2);
-
-      var delta = FontsUtils.integerToBytes(((range[0] - 1) - bias) % 65536, 2);
-      bias += range.length;
-
-      // deltas are signed shorts
-      delta[0] ^= 0xFF;
-      delta[1] ^= 0xFF;
-      delta[1] += 1;
-
-      startCount.push(start[0], start[1]);
-      endCount.push(end[0], end[1]);
-      idDeltas.push(delta[0], delta[1]);
-      idRangeOffsets.push(0x00, 0x00);
-
-      for (var j = 0; j < range.length; j++)
-        glyphsIdsArray.push(range[j]);
-    }
-    startCount.push(0xFF, 0xFF);
-    endCount.push(0xFF, 0xFF);
-    idDeltas.push(0x00, 0x01);
-    idRangeOffsets.push(0x00, 0x00);
-
-    return cmap.concat(endCount, [0x00, 0x00], startCount,
-                       idDeltas, idRangeOffsets, glyphsIdsArray);
-  },
-
-  _createTableEntry: function font_createTableEntry(aFile, aOffsets, aTag, aData) {
-    // tag
-    var tag = [
-      aTag.charCodeAt(0),
-      aTag.charCodeAt(1),
-      aTag.charCodeAt(2),
-      aTag.charCodeAt(3)
-    ];
-
-    // Per spec tables must be 4-bytes align so add some 0x00 if needed
-    while (aData.length & 3)
-      aData.push(0x00);
-
-    while (aOffsets.virtualOffset & 3)
-      aOffsets.virtualOffset++;
-
-    // offset
-    var offset = aOffsets.virtualOffset;
-
-    // length
-    var length = aData.length;
-
-    // checksum
-    var checksum = FontsUtils.bytesToInteger(tag) + offset + length;
-
-    var tableEntry = [].concat(tag,
-                               FontsUtils.integerToBytes(checksum, 4),
-                               FontsUtils.integerToBytes(offset, 4),
-                               FontsUtils.integerToBytes(length, 4));
-    aFile.set(tableEntry, aOffsets.currentOffset);
-    aOffsets.currentOffset += tableEntry.length;
-    aOffsets.virtualOffset += aData.length;
-  },
-
-  _readOpenTypeHeader: function tt_readOpenTypeHeader(aFile) {
-    return {
-      version: aFile.getBytes(4),
-      numTables: FontsUtils.bytesToInteger(aFile.getBytes(2)),
-      searchRange: FontsUtils.bytesToInteger(aFile.getBytes(2)),
-      entrySelector: FontsUtils.bytesToInteger(aFile.getBytes(2)),
-      rangeShift: FontsUtils.bytesToInteger(aFile.getBytes(2))
-    }
-  },
-
-  _readTableEntry: function tt_readTableEntry(aFile) {
-    // tag
-    var tag = aFile.getBytes(4);
-    tag = String.fromCharCode(tag[0]) +
-          String.fromCharCode(tag[1]) +
-          String.fromCharCode(tag[2]) +
-          String.fromCharCode(tag[3]);
-
-    var checksum = FontsUtils.bytesToInteger(aFile.getBytes(4));
-    var offset = FontsUtils.bytesToInteger(aFile.getBytes(4));
-    var length = FontsUtils.bytesToInteger(aFile.getBytes(4));
-
-    // Read the table associated data
-    var currentPosition = aFile.pos;
-    aFile.pos = aFile.start + offset;
-    var data = aFile.getBytes(length);
-    aFile.pos = currentPosition;
-
-    return {
-      tag: tag,
-      checksum: checksum,
-      length: offset,
-      offset: length,
-      data: data
-    }
-  }
-};
-
-
 /**
  * Type1Parser encapsulate the needed code for parsing a Type1 font
  * program.
@@ -1072,18 +849,18 @@ var Type1Parser = function() {
   var kEexecEncryptionKey = 55665;
   var kCharStringsEncryptionKey = 4330;
 
-  function decrypt(aStream, aKey, aDiscardNumber) {
-    var r = aKey, c1 = 52845, c2 = 22719;
+  function decrypt(stream, key, discardNumber) {
+    var r = key, c1 = 52845, c2 = 22719;
     var decryptedString = [];
 
     var value = "";
-    var count = aStream.length;
+    var count = stream.length;
     for (var i = 0; i < count; i++) {
-      value = aStream[i];
+      value = stream[i];
       decryptedString[i] = value ^ (r >> 8);
       r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
     }
-    return decryptedString.slice(aDiscardNumber);
+    return decryptedString.slice(discardNumber);
   };
 
   /*
@@ -1193,18 +970,18 @@ var Type1Parser = function() {
     "31": "hvcurveto"
   };
 
-  function decodeCharString(aArray) {
+  function decodeCharString(array) {
     var charString = [];
 
     var value = "";
-    var count = aArray.length;
+    var count = array.length;
     for (var i = 0; i < count; i++) {
-      value = parseInt(aArray[i]);
+      value = parseInt(array[i]);
 
       if (value < 32) {
         var command = null;
         if (value == 12) {
-          var escape = aArray[++i];
+          var escape = array[++i];
           command = charStringDictionary["12"][escape];
         } else {
           command = charStringDictionary[value];
@@ -1224,14 +1001,14 @@ var Type1Parser = function() {
       } else if (value <= 246) {
         value = parseInt(value) - 139;
       } else if (value <= 250) {
-        value = ((value - 247) * 256) + parseInt(aArray[++i]) + 108;
+        value = ((value - 247) * 256) + parseInt(array[++i]) + 108;
       } else if (value <= 254) {
-        value = -((value - 251) * 256) - parseInt(aArray[++i]) - 108;
+        value = -((value - 251) * 256) - parseInt(array[++i]) - 108;
       } else {
-        var byte = aArray[++i];
+        var byte = array[++i];
         var high = (byte >> 1);
-        value = (byte - high) << 24 | aArray[++i] << 16 |
-                aArray[++i] << 8 | aArray[++i];
+        value = (byte - high) << 24 | array[++i] << 16 |
+                array[++i] << 8 | array[++i];
       }
 
       charString.push(value);
@@ -1244,8 +1021,8 @@ var Type1Parser = function() {
    * Returns an object containing a Subrs array and a CharStrings array
    * extracted from and eexec encrypted block of data
    */
-  this.extractFontProgram = function t1_extractFontProgram(aStream) {
-    var eexecString = decrypt(aStream, kEexecEncryptionKey, 4);
+  this.extractFontProgram = function t1_extractFontProgram(stream) {
+    var eexecString = decrypt(stream, kEexecEncryptionKey, 4);
     var subrs = [],  glyphs = [];
     var inGlyphs = false;
     var inSubrs = false;
@@ -1369,26 +1146,26 @@ const CFFStrings = [
   "001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"
 ];
 
-var CFF = function(aName, aFile, aProperties) {
+var CFF = function(name, file, properties) {
   // Get the data block containing glyphs and subrs informations
-  var length1 = aFile.dict.get("Length1");
-  var length2 = aFile.dict.get("Length2");
-  aFile.skip(length1);
-  var eexecBlock = aFile.getBytes(length2);
+  var length1 = file.dict.get("Length1");
+  var length2 = file.dict.get("Length2");
+  file.skip(length1);
+  var eexecBlock = file.getBytes(length2);
 
   // Decrypt the data blocks and retrieve the informations from it
   var parser = new Type1Parser();
   var fontInfo = parser.extractFontProgram(eexecBlock);
 
-  aProperties.subrs = fontInfo.subrs;
-  aProperties.glyphs = fontInfo.charstrings;
-  this.data = this.wrap(aName, aProperties);
+  properties.subrs = fontInfo.subrs;
+  properties.glyphs = fontInfo.charstrings;
+  this.data = this.wrap(name, properties);
 };
 
 CFF.prototype = {
-  createCFFIndexHeader: function(aObjects, aIsByte) {
+  createCFFIndexHeader: function(objects, isByte) {
     // First 2 bytes contains the number of objects contained into this index
-    var count = aObjects.length;
+    var count = objects.length;
 
     // If there is no object, just create an array saying that with another
     // offset byte.
@@ -1412,33 +1189,33 @@ CFF.prototype = {
       for (var j = 0; j < bytes.length; j++)
         data.push(bytes[j]);
 
-      if (aObjects[i])
-        relativeOffset += aObjects[i].length;
+      if (objects[i])
+        relativeOffset += objects[i].length;
     }
 
     for (var i =0; i < count; i++) {
-      for (var j = 0; j < aObjects[i].length; j++)
-        data.push(aIsByte ? aObjects[i][j] : aObjects[i].charCodeAt(j));
+      for (var j = 0; j < objects[i].length; j++)
+        data.push(isByte ? objects[i][j] : objects[i].charCodeAt(j));
     }
     return data;
   },
 
-  encodeNumber: function(aValue) {
+  encodeNumber: function(value) {
     var x = 0;
-    if (aValue >= -32768 && aValue <= 32767) {
-      return [ 28, aValue >> 8, aValue & 0xFF ];
-    } else if (aValue >= (-2147483647-1) && aValue <= 2147483647) {
-      return [ 0xFF, aValue >> 24, Value >> 16, aValue >> 8, aValue & 0xFF ];
+    if (value >= -32768 && value <= 32767) {
+      return [ 28, value >> 8, value & 0xFF ];
+    } else if (value >= (-2147483647-1) && value <= 2147483647) {
+      return [ 0xFF, value >> 24, Value >> 16, value >> 8, value & 0xFF ];
     } else {
-      error("Value: " + aValue + " is not allowed");
+      error("Value: " + value + " is not allowed");
     }
   },
 
-  getOrderedCharStrings: function(aGlyphs) {
+  getOrderedCharStrings: function(glyphs) {
     var charstrings = [];
 
-    for (var i = 0; i < aGlyphs.length; i++) {
-      var glyph = aGlyphs[i].glyph;
+    for (var i = 0; i < glyphs.length; i++) {
+      var glyph = glyphs[i].glyph;
       var unicode = GlyphsUnicode[glyph];
       if (!unicode) {
         if (glyph != ".notdef")
@@ -1447,7 +1224,7 @@ CFF.prototype = {
         charstrings.push({
           glyph: glyph,
           unicode: unicode,
-          charstring: aGlyphs[i].data.slice()
+          charstring: glyphs[i].data.slice()
         });
       }
     };
@@ -1480,100 +1257,100 @@ CFF.prototype = {
     "hvcurveto": 31,
   },
 
-  flattenCharstring: function flattenCharstring(aGlyph, aCharstring, aSubrs) {
+  flattenCharstring: function flattenCharstring(glyph, charstring, subrs) {
     var i = 0;
     while (true) {
-      var obj = aCharstring[i];
+      var obj = charstring[i];
       if (obj == null)
         return [];
 
       if (obj.charAt) {
         switch (obj) {
           case "callsubr":
-            var subr = aSubrs[aCharstring[i - 1]].slice();
+            var subr = subrs[charstring[i - 1]].slice();
             if (subr.length > 1) {
-              subr = this.flattenCharstring(aGlyph, subr, aSubrs);
+              subr = this.flattenCharstring(glyph, subr, subrs);
               subr.pop();
-              aCharstring.splice(i - 1, 2, subr);
+              charstring.splice(i - 1, 2, subr);
             } else {
-              aCharstring.splice(i - 1, 2);
+              charstring.splice(i - 1, 2);
             }
             i -= 1;
             break;
 
           case "callothersubr":
-            var index = aCharstring[i - 1];
-            var count = aCharstring[i - 2];
-            var data = aCharstring[i - 3];
+            var index = charstring[i - 1];
+            var count = charstring[i - 2];
+            var data = charstring[i - 3];
 
             // XXX The callothersubr needs to support at least the 3 defaults
             // otherSubrs of the spec
             if (index != 3)
-              error("callothersubr for index: " + index + " (" + aCharstring + ")");
+              error("callothersubr for index: " + index + " (" + charstring + ")");
 
             if (!data) {
-              aCharstring.splice(i - 2, 4, "pop", 3);
+              charstring.splice(i - 2, 4, "pop", 3);
               i -= 3;
             } else {
               // 5 to remove the arguments, the callothersubr call and the pop command
-              aCharstring.splice(i - 3, 5, 3);
+              charstring.splice(i - 3, 5, 3);
               i -= 3;
             }
             break;
 
           case "div":
-            var num2 = aCharstring[i - 1];
-            var num1 = aCharstring[i - 2];
-            aCharstring.splice(i - 2, 3, num1 / num2);
+            var num2 = charstring[i - 1];
+            var num1 = charstring[i - 2];
+            charstring.splice(i - 2, 3, num1 / num2);
             i -= 2;
             break;
 
           case "pop":
             if (i)
-              aCharstring.splice(i - 2, 2);
+              charstring.splice(i - 2, 2);
             else
-              aCharstring.splice(i - 1, 1);
+              charstring.splice(i - 1, 1);
             i -= 1;
             break;
 
 
           case "hsbw":
-            var charWidthVector = aCharstring[i - 1];
-            var leftSidebearing = aCharstring[i - 2];
+            var charWidthVector = charstring[i - 1];
+            var leftSidebearing = charstring[i - 2];
 
             if (leftSidebearing)
-              aCharstring.splice(i - 2, 3, charWidthVector, leftSidebearing, "hmoveto");
+              charstring.splice(i - 2, 3, charWidthVector, leftSidebearing, "hmoveto");
             else
-              aCharstring.splice(i - 2, 3, charWidthVector);
+              charstring.splice(i - 2, 3, charWidthVector);
             break;
 
           case "endchar":
           case "return":
             // CharString is ready to be re-encode to commands number at this point
-            for (var j = 0; j < aCharstring.length; j++) {
-              var command = aCharstring[j];
+            for (var j = 0; j < charstring.length; j++) {
+              var command = charstring[j];
               if (parseFloat(command) == command) {
-                aCharstring.splice(j, 1, 28, command >> 8, command);
+                charstring.splice(j, 1, 28, command >> 8, command);
                 j+= 2;
               } else if (command.charAt) {
                 var command = this.commandsMap[command];
                 if (IsArray(command)) {
-                  aCharstring.splice(j - 1, 1, command[0], command[1]);
+                  charstring.splice(j - 1, 1, command[0], command[1]);
                   j += 1;
                 } else {
-                  aCharstring[j] = command;
+                  charstring[j] = command;
                 }
               } else {
-                aCharstring.splice(j, 1);
+                charstring.splice(j, 1);
 
                 // command has already been translated, just add them to the
                 // charstring directly
                 for (var k = 0; k < command.length; k++)
-                  aCharstring.splice(j + k, 0, command[k]);
+                  charstring.splice(j + k, 0, command[k]);
                 j+= command.length - 1;
               }
             }
-            return aCharstring;
+            return charstring;
 
           default:
             break;
@@ -1581,11 +1358,11 @@ CFF.prototype = {
       }
       i++;
     }
-    error("failing with i = " + i + " in charstring:" + aCharstring + "(" + aCharstring.length + ")");
+    error("failing with i = " + i + " in charstring:" + charstring + "(" + charstring.length + ")");
   },
 
-  wrap: function wrap(aName, aProperties) {
-    var charstrings = this.getOrderedCharStrings(aProperties.glyphs);
+  wrap: function wrap(name, properties) {
+    var charstrings = this.getOrderedCharStrings(properties.glyphs);
 
     // Starts the conversion of the Type1 charstrings to Type2
     var charstringsCount = 0;
@@ -1595,7 +1372,7 @@ CFF.prototype = {
       var charstring = charstrings[i].charstring.slice();
       var glyph = charstrings[i].glyph;
 
-      var flattened = this.flattenCharstring(glyph, charstring, aProperties.subrs);
+      var flattened = this.flattenCharstring(glyph, charstring, properties.subrs);
       glyphs.push(flattened);
       charstringsCount++;
       charstringsDataLength += flattened.length;
@@ -1611,7 +1388,7 @@ CFF.prototype = {
     cff.set(header);
 
     // Names Index
-    var nameIndex = this.createCFFIndexHeader([aName]);
+    var nameIndex = this.createCFFIndexHeader([name]);
     cff.set(nameIndex, currentOffset);
     currentOffset += nameIndex.length;
 
@@ -1655,7 +1432,7 @@ CFF.prototype = {
       248, 31, 4  // Weight
     ];
 
-    var fontBBox = aProperties.bbox;
+    var fontBBox = properties.bbox;
     for (var i = 0; i < fontBBox.length; i++)
       topDictIndex = topDictIndex.concat(this.encodeNumber(fontBBox[i]));
     topDictIndex.push(5) // FontBBox;
diff --git a/pdf.js b/pdf.js
index 5c49f14dd54f58c7d31fee5c2bb8127517b9b572..4db4ef06f785953877f813ca08a744c4e5f94415 100644 (file)
--- a/pdf.js
+++ b/pdf.js
@@ -80,6 +80,7 @@ var Stream = (function() {
         getBytes: function(length) {
             var bytes = this.bytes;
             var pos = this.pos;
+
             var end = pos + length;
             var strEnd = this.end;
             if (!end || end > strEnd)
@@ -2198,7 +2199,7 @@ var CanvasGraphics = (function() {
                     var tokens = [];
                     var token = "";
 
-                    var buffer = cmapObj.ensureBuffer();
+                    var buffer = cmapObj.ensureBuffer ? cmapObj.ensureBuffer() : cmapObj;
                     var cmap = cmapObj.getBytes(buffer.byteLength);
                     for (var i =0; i < cmap.length; i++) {
                       var byte = cmap[i];