]> git.parisson.com Git - pdf.js.git/commitdiff
Beginning of the separatation of the Type1/CFF/OTF code
authorVivien Nicolas <21@vingtetun.org>
Mon, 13 Jun 2011 00:30:16 +0000 (02:30 +0200)
committerVivien Nicolas <21@vingtetun.org>
Mon, 13 Jun 2011 00:30:16 +0000 (02:30 +0200)
PDFFont.js

index 15e5f2c32632501e86e4aca5763fd703af1b82b7..8cddb2360cebaf2a498ad26c4bc7a9c52fc73eb9 100644 (file)
@@ -1,6 +1,12 @@
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
 
+/**
+ * Maximum file size of the font.
+ */
+var kMaxFontFileSize = 40000;
+
+
 /**
  * Hold a map of decoded fonts and of the standard fourteen Type1 fonts and
  * their acronyms.
@@ -32,20 +38,32 @@ var Font = function(aFontName, aFontFile, aFontType) {
 
   switch (aFontType) {
     case "Type1":
+      // All Type1 font program should begin with the comment %!
+      if (aFontFile.getByte() != 0x25 || aFontFile.getByte() != 0x21)
+        error("Invalid file header");
+
+      var cff = new CFF(aFontName, aFontFile);
       this.mimetype = "font/otf";
-      this.font = new Type1(aFontName, aFontFile);
+
+      // Wrap the CFF data inside an OTF font file
+      this.font = this.cover(cff);
       break;
+
     case "TrueType":
       this.mimetype = "font/ttf";
-      this.font = new TrueType(aFontName, aFontFile);
+      var ttf = new TrueType(aFontName, aFontFile);
+      this.font = ttf.data;
       break;
+
     default:
-      error("Font " + aFontType + " is not supported");
+      warn("Font " + aFontType + " is not supported");
       break;
   }
 
-  Fonts[aFontName] = this.font;
+  // Attach the font to the document
   this.bind();
+
+  Fonts[aFontName] = this.font;
 };
 
 Font.prototype = {
@@ -53,8 +71,8 @@ Font.prototype = {
   font: null,
   mimetype: null,
 
-  bind: function() {
-    var data = this.font.data;
+  bind: function font_bind() {
+    var data = this.font;
 
     // Compute the binary data to base 64
     var str = [];
@@ -70,1443 +88,1423 @@ Font.prototype = {
     var rule = "@font-face { font-family:'" + this.name + "';src:" + url + "}";
     var styleSheet = document.styleSheets[0];
     styleSheet.insertRule(rule, styleSheet.length);
-  }
-};
-
-/** Implementation dirty logic starts here */
-
-var kMaxFontFileSize = 100000;
-
-/**
- * This dictionary holds decoded fonts data.
- */
-var PSFonts = new Dict();
+  },
 
+  _createOpenTypeHeader: function font_createOpenTypeHeader(aNumTables) {
+    // sfnt version (4 bytes)
+    var version = [0x4F, 0x54, 0x54, 0X4F];
 
-var Stack = function() {
-  var innerStack = [];
+    // numTables (2 bytes)
+    var numTables = aNumTables;
 
-  this.push = function(aOperand) {
-    innerStack.push(aOperand);
-  };
+    // searchRange (2 bytes)
+    var tablesMaxPower2 = FontsUtils.getMaxPower2(numTables);
+    var searchRange = tablesMaxPower2 * 16;
 
-  this.pop = function() {
-    if (!this.count())
-      throw new Error("stackunderflow");
-    return innerStack.pop();
-  };
+    // entrySelector (2 bytes)
+    var entrySelector = Math.log(tablesMaxPower2) / Math.log(2);
 
-  this.peek = function() {
-    if (!this.count())
-      return null;
-    return innerStack[innerStack.length - 1];
-  };
+    // rangeShift (2 bytes)
+    var rangeShift = numTables * 16 - searchRange;
 
-  this.get = function(aIndex) {
-    return innerStack[aIndex];
-  };
+    return [].concat(version,
+                     FontsUtils.integerToBytes(numTables, 2),
+                     FontsUtils.integerToBytes(searchRange, 2),
+                     FontsUtils.integerToBytes(entrySelector, 2),
+                     FontsUtils.integerToBytes(rangeShift, 2));
+  },
 
-  this.clear = function() {
-    innerStack = [];
-  };
+  _createTableEntry: function font_createTableEntry(aTag, aOffset, aData) {
+    // tag
+    var tag = [
+      aTag.charCodeAt(0),
+      aTag.charCodeAt(1),
+      aTag.charCodeAt(2),
+      aTag.charCodeAt(3)
+    ];
 
-  this.count = function() {
-    return innerStack.length;
-  };
+    // offset
+    var offset = aOffset;
 
-  this.dump = function() {
-    for (var i = 0; i < this.length; i++)
-      log(innerStack[i]);
-  };
+    // Per spec tables must be 4-bytes align so add some 0x00 if needed
+    while (aData.length & 3)
+      aData.push(0x00);
 
-  this.clone = function() {
-    return innerStack.slice();
-  };
-};
+    // length
+    var length = aData.length;
 
-var TrueType = function(aFontName, aFontFile) {
-  var debug = true;
-  function dump(aMsg) {
-    if (debug)
-      log(aMsg);
-  }
+    // checksum
+    var checksum = FontsUtils.bytesToInteger(tag) + offset + length;
 
-  dump("Loading a TrueType font: " + aFontName);
-  this.data = aFontFile;
-};
+    return [].concat(tag,
+                    FontsUtils.integerToBytes(checksum, 4),
+                    FontsUtils.integerToBytes(offset, 4),
+                    FontsUtils.integerToBytes(length, 4));
+  },
 
-var Type1Parser = function(aAsciiStream, aBinaryStream) {
-  var lexer = aAsciiStream ? new Lexer(aAsciiStream) : null;
+  _createCMAPTable: function font_createCMAPTable(aGlyphs) {
+    var data = new Array(1000);
+    for (var i = 0; i < aGlyphs.length; i++)
+      data[aGlyphs[i].unicode] = i + 1;
+
+    var ranges = [];
+    var range = [];
+    for (var i = 0; i < data.length; i++) {
+      var char = data[i];
+      if (char) {
+        range.push(i);
+      } else if (range.length) {
+        if (0) {
+          log("create a new range of " + range.length + " chars width min: " + range[0] + " to max: " + range[range.length - 1]);
+          log("range content is: " + range);
+        }
+        ranges.push(range.slice());
+        range = [];
+      }
+    }
 
-  // Turn on this flag for additional debugging logs
-  var debug = false;
 
-  var dump = function(aData) {
-    if (debug)
-      log(aData);
-  };
+    var cmap = [];
+    var segCount = ranges.length + 1;
 
-  // Hold the fontName as declared inside the /FontName postscript directive
-  // XXX This is a hack but at the moment I need it to map the name declared
-  // in the PDF and the name in the PS code.
-  var fontName = "";
+    var segCount2 = segCount * 2;
+    var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
+    var searchEntry = Math.log(segCount) / Math.log(2);
+    var rangeShift = 2 * segCount - searchRange;
+    cmap = cmap.concat(FontsUtils.integerToBytes(segCount2, 2));
+    cmap = cmap.concat(FontsUtils.integerToBytes(searchRange, 2));
+    cmap = cmap.concat(FontsUtils.integerToBytes(searchEntry, 2));
+    cmap = cmap.concat(FontsUtils.integerToBytes(rangeShift, 2));
 
-  /*
-   * Parse a whole Type1 font stream (from the first segment to the last)
-   * assuming the 'eexec' block is binary data and fill up the 'Fonts'
-   * dictionary with the font informations.
-   */
-  var self = this;
-  this.parse = function() {
-    if (!debug) {
-      while (!processNextToken()) {};
-      return fontName;
-    } else {
-      // debug mode is used to debug postcript processing
-      setTimeout(function() {
-        if (!processNextToken())
-          self.parse();
-      }, 0);
-    }
-  };
+    // End characters code with an additional 0xFFFF to finish the array
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      cmap = cmap.concat(FontsUtils.integerToBytes(range[range.length - 1], 2));
+    };
+    cmap = cmap.concat([0xFF, 0xFF]);
 
-  /*
-   * Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
-   * of Plaintext Bytes. The function took a key as a parameter which can be
-   * for decrypting the eexec block of for decoding charStrings.
-   */
-  var kEexecEncryptionKey = 55665;
-  var kCharStringsEncryptionKey = 4330;
+    // reserved pad
+    cmap = cmap.concat([0x00, 0x00]);
 
-  function decrypt(aStream, aKey, aDiscardNumber) {
-    var start = Date.now();
-    var r = aKey, c1 = 52845, c2 = 22719;
-    var decryptedString = [];
+    // Start characters code with an additional 0xFFFF to finish the array
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      cmap = cmap.concat(FontsUtils.integerToBytes(range[0], 2));
+    };
+    cmap = cmap.concat([0xFF, 0xFF]);
+
+    // Fill idDelta
+    var delta = 0;
+    var p = 0;
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      var start = range[0];
+      var delta = ((start - 1) - p) % 65536;
+
+      var value = FontsUtils.integerToBytes(delta, 2);
+      value[0] ^= 0xFF;
+      value[1] ^= 0xFF;
+      value[1] += 1;
+      cmap = cmap.concat([value[0], value[1]]);
+
+      p += range.length;
+    };
+    cmap = cmap.concat([0x00, 0x01]);
 
-    var value = "";
-    var count = aStream.length;
-    for (var i = 0; i < count; i++) {
-      value = aStream.getByte();
-      decryptedString[i] = String.fromCharCode(value ^ (r >> 8));
-      r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
-    }
-    var end = Date.now();
-    dump("Time to decrypt string of length " + count + " is " + (end - start));
-    return decryptedString.slice(aDiscardNumber);
-  };
 
-  /*
-   * CharStrings are encoded following the the CharString Encoding sequence
-   * describe in Chapter 6 of the "Adobe Type1 Font Format" specification.
-   * The value in a byte indicates a command, a number, or subsequent bytes
-   * that are to be interpreted in a special way.
-   *
-   * CharString Number Encoding:
-   *  A CharString byte containing the values from 32 through 255 inclusive
-   *  indicate an integer. These values are decoded in four ranges.
-   *
-   * 1. A CharString byte containing a value, v, between 32 and 246 inclusive,
-   * indicate the integer v - 139. Thus, the integer values from -107 through
-   * 107 inclusive may be encoded in single byte.
-   *
-   * 2. A CharString byte containing a value, v, between 247 and 250 inclusive,
-   * indicates an integer involving the next byte, w, according to the formula:
-   * [(v - 247) x 256] + w + 108
-   *
-   * 3. A CharString byte containing a value, v, between 251 and 254 inclusive,
-   * indicates an integer involving the next byte, w, according to the formula:
-   * -[(v - 251) * 256] - w - 108
-   *
-   * 4. A CharString containing the value 255 indicates that the next 4 bytes
-   * are a two complement signed integer. The first of these bytes contains the
-   * highest order bits, the second byte contains the next higher order bits
-   * and the fourth byte contain the lowest order bits.
-   *
-   *
-   * CharString Command Encoding:
-   *  CharStrings commands are encoded in 1 or 2 bytes.
-   *
-   *  Single byte commands are encoded in 1 byte that contains a value between
-   *  0 and 31 inclusive.
-   *  If a command byte contains the value 12, then the value in the next byte
-   *  indicates a command. This "escape" mechanism allows many extra commands
-   * to be encoded and this encoding technique helps to minimize the length of
-   * the charStrings.
-   */
-  var charStringDictionary = {
-    "1": "hstem",
-    "3": "vstem",
-    "4": "vmoveto",
-    "5": "rlineto",
-    "6": "hlineto",
-    "7": "vlineto",
-    "8": "rrcurveto",
-    "9": "closepath",
-    "10": "callsubr",
-    "11": "return",
-    "12": {
-      "0": "dotsection",
-      "1": "vstem3",
-      "3": "hstem3",
-      "6": "seac",
-      "7": "sbw",
-      "12": "div",
-      "16": "callothersubr",
-      "17": "pop",
-      "33": "setcurrentpoint"
-    },
-    "13": "hsbw",
-    "14": "endchar",
-    "21": "rmoveto",
-    "22": "hmoveto",
-    "30": "vhcurveto",
-    "31": "hvcurveto"
-  };
+    // Fill id Offsets with 0x00
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      cmap = cmap.concat([0x00, 0x00]);
+    };
+    cmap = cmap.concat([0x00, 0x00]);
+
+    var cmapHeader = [
+      0x00, 0x00, // version
+      0x00, 0x01, // numTables
+      0x00, 0x03, // platformID
+      0x00, 0x01, // encodingID
+      0x00, 0x00, 0x00, 0x0C, // start of the table record
+      0x00, 0x04  // format
+    ];
+    cmapHeader = cmapHeader.concat(FontsUtils.integerToBytes(cmap.length + 6, 2)); // length
+    cmapHeader = cmapHeader.concat(0x00, 0x00); // language
+
+    // Fill up data!
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      for (var j = 0; j < range.length; j++)
+        cmap = cmap.concat(range[j]);
+    };
+    cmap = cmapHeader.concat(cmap);
+    return cmap;
+  },
 
-  function decodeCharString(aStream) {
-    var start = Date.now();
-    var charString = [];
+  cover: function font_cover(aFont) {
+    var otf = new Uint8Array(kMaxFontFileSize);
+    var aFontData = aFont.data;
+    var currentOffset = 0;
 
-    var value = "";
-    var count = aStream.length;
-    for (var i = 0; i < count; i++) {
-      value = aStream.getByte();
+    var numTables = 9;
+    //var tables = [OS2, cmap, head, hhea, hmtx, maxp, name, post];
+    var header = this._createOpenTypeHeader(numTables);
+    otf.set(header, currentOffset);
+    currentOffset += header.length;
 
-      if (value < 32) {
-        if (value == 12) {
-          value = charStringDictionary["12"][aStream.getByte()];
-          i++;
-        } else {
-          value = charStringDictionary[value];
-        }
-      } else if (value <= 246) {
-        value = parseInt(value) - 139;
-      } else if (value <= 250) {
-        value = ((value - 247) * 256) + parseInt(aStream.getByte()) + 108;
-        i++;
-      } else if (value <= 254) {
-        value = -((value - 251) * 256) - parseInt(aStream.getByte()) - 108;
-        i++;
-      } else {
-        var byte = aStream.getByte();
-        var high = (byte >> 1);
-        value = (byte - high) << 24 | aStream.getByte() << 16 |
-                aStream.getByte() << 8 | aStream.getByte();
-        i += 4;
-      }
+    var baseOffset = numTables * (4 * 4) + currentOffset;
+    var virtualOffset = baseOffset;
+    var tableEntry = this._createTableEntry("CFF ", virtualOffset, aFontData);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += aFontData.length;
 
-      charString.push(value);
-    }
+    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
+      0x00, 0x00, 0x00, 0x01, // ulUnicodeRange1 (Bits 0-31)
+      0x00, 0x00, 0x00, 0x00, // ulUnicodeRange2 (Bits 32-63)
+      0x00, 0x00, 0x00, 0x00, // ulUnicodeRange3 (Bits 64-95)
+      0x00, 0x00, 0x00, 0x00, // ulUnicodeRange4 (Bits 96-127)
+      0x47, 0x49, 0x60, 0x20, // 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
+    ];
 
-    var end = Date.now();
-    dump("Time to decode charString of length " + count + " is " + (end - start));
-    return charString;
-  }
+    var tableEntry = this._createTableEntry("OS/2", virtualOffset, OS2);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += OS2.length;
 
-  /*
-   * The operand stack holds arbitrary PostScript objects that are the operands
-   * and results of PostScript operators being executed. The interpreter pushes
-   * objects on the operand stack when it encounters them as literal data in a
-   * program being executed. When an operator requires one or more operands, it
-   * obtains them by popping them off the top of the operand stack. When an
-   * operator returns one or more results, it does so by pushing them on the
-   * operand stack.
-   */
-   var operandStack = new Stack();
+    /** CMAP */
+    var charstrings = aFont.getOrderedCharStrings(aFont.font);
 
-   // Flag indicating if the topmost operand of the operandStack is an array
-   var operandIsArray = 0;
+    var cmap = this._createCMAPTable(charstrings);
+    var tableEntry = this._createTableEntry("cmap", virtualOffset, cmap);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += cmap.length;
 
-  /*
-   * The dictionary stack holds only dictionary objects. The current set of
-   * dictionaries on the dictionary stack defines the environment for all
-   * implicit name searches, such as those that occur when the interpreter
-   * encounters an executable name. The role of the dictionary stack is
-   * introduced in Section 3.3, “Data Types and Objects,” and is further
-   * explained in Section 3.5, “Execution.” of the PostScript Language
-   * Reference.
-   */
-  var systemDict = new Dict(),
-      globalDict = new Dict(),
-      userDict   = new Dict();
 
-  var dictionaryStack = new Stack();
-  dictionaryStack.push(systemDict);
-  dictionaryStack.push(globalDict);
-  dictionaryStack.push(userDict);
+    /** HEAD */
 
-  /*
-   * The execution stack holds executable objects (mainly procedures and files)
-   * that are in intermediate stages of execution. At any point in the
-   * execution of a PostScript program, this stack represents the program’s
-   * call stack. Whenever the interpreter suspends execution of an object to
-   * execute some other object, it pushes the new object on the execution
-   * stack. When the interpreter finishes executing an object, it pops that
-   * object off the execution stack and resumes executing the suspended object
-   * beneath it.
-   */
-  var executionStack = new Stack();
+    var head = [
+      0x00, 0x01, 0x00, 0x00, // Version number
+      0x00, 0x00, 0x50, 0x00, // fontRevision
+      0x00, 0x00, 0x00, 0x00, // checksumAdjustement
+      0x5F, 0x0F, 0x3C, 0xF5, // magicNumber
+      0x00, 0x00, // Flags
+      0x03, 0xE8, // unitsPerEM (>= 16 && <=16384)
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // created
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // modified
+      0x00, 0x00, // xMin
+      0x00, 0x00, // yMin
+      0x00, 0x00, // xMax
+      0x00, 0x00, // yMax
+      0x00, 0x00, // macStyle
+      0x00, 0x00, // lowestRecPPEM
+      0x00, 0x00, // fontDirectionHint
+      0x00, 0x00, // indexToLocFormat
+      0x00, 0x00 // glyphDataFormat
+    ];
+    var tableEntry = this._createTableEntry("head", virtualOffset, head);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += head.length;
 
-  /*
-   * Return the next token in the execution stack
-   */
-  function nextInStack() {
-    var currentProcedure = executionStack.peek();
-    if (currentProcedure) {
-      var command = currentProcedure.shift();
-      if (!currentProcedure.length)
-        executionStack.pop();
-      return command;
+
+    /** HHEA */
+
+    var hhea = [
+      0x00, 0x01, 0x00, 0x00, // Version number
+      0x00, 0x00, // Typographic Ascent
+      0x00, 0x00, // Typographic Descent
+      0x00, 0x00, // Line Gap
+      0xFF, 0xFF, // advanceWidthMax
+      0x00, 0x00, // minLeftSidebearing
+      0x00, 0x00, // minRightSidebearing
+      0x00, 0x00, // xMaxExtent
+      0x00, 0x00, // caretSlopeRise
+      0x00, 0x00, // caretSlopeRun
+      0x00, 0x00, // caretOffset
+      0x00, 0x00, // -reserved-
+      0x00, 0x00, // -reserved-
+      0x00, 0x00, // -reserved-
+      0x00, 0x00, // -reserved-
+      0x00, 0x00 // metricDataFormat
+    ];
+    hhea = hhea.concat(FontsUtils.integerToBytes(charstrings.length, 2)); // numberOfHMetrics
+
+    var tableEntry = this._createTableEntry("hhea", virtualOffset, hhea);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += hhea.length;
+
+    /** HMTX */
+
+    var hmtx = [0x01, 0xF4, 0x00, 0x00];
+    for (var i = 0; i < charstrings.length; i++) {
+      var charstring = charstrings[i].charstring;
+      var width = FontsUtils.integerToBytes(charstring[1], 2);
+      var lsb = FontsUtils.integerToBytes(charstring[0], 2);
+      hmtx = hmtx.concat(width, lsb);
     }
 
-    return lexer.getObj();
-  };
+    var tableEntry = this._createTableEntry("hmtx", virtualOffset, hmtx);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += hmtx.length;
 
-  /*
-   * Get the next token from the executionStack and process it.
-   * Actually the function does not process the third segment of a Type1 font
-   * and end on 'closefile'.
-   *
-   * The method thrown an error if it encounters an unknown token.
-   */
-  function processNextToken() {
-    var obj = nextInStack();
-    if (operandIsArray && !IsCmd(obj, "{") && !IsCmd(obj, "[") &&
-                          !IsCmd(obj, "]") && !IsCmd(obj, "}")) {
-      dump("Adding an object: " + obj +" to array " + operandIsArray);
-      var currentArray = operandStack.peek();
-      for (var i = 1; i < operandIsArray; i++)
-        currentArray = currentArray[currentArray.length - 1];
 
-      currentArray.push(obj);
-    } else if (IsBool(obj) || IsInt(obj) || IsNum(obj) || IsString(obj)) {
-      dump("Value: " + obj);
-      operandStack.push(obj);
-    } else if (IsName(obj)) {
-      dump("Name: " + obj.name);
-      operandStack.push(obj.name);
-    } else if (IsCmd(obj)) {
-      var command = obj.cmd;
-      dump(command);
+    /** MAXP */
 
-      switch (command) {
-        case "[":
-        case "{":
-          dump("Start" + (command == "{" ? " Executable " : " ") + "Array");
-          operandIsArray++;
-          var currentArray = operandStack;
-          for (var i = 1; i < operandIsArray; i++)
-            if (currentArray.peek)
-              currentArray = currentArray.peek();
-            else
-              currentArray = currentArray[currentArray.length - 1];
-          currentArray.push([]);
-          break;
+    var maxp = [
+      0x00, 0x00, 0x50, 0x00, // Version number
+    ].concat(FontsUtils.integerToBytes(charstrings.length + 1, 2)); // Num of glyphs (+1 to pass the sanitizer...)
 
-        case "]":
-        case "}":
-          var currentArray = operandStack.peek();
-          for (var i = 1; i < operandIsArray; i++)
-            currentArray = currentArray[currentArray.length - 1];
-          dump("End" + (command == "}" ? " Executable " : " ") + "Array: " + currentArray.join(" "));
-          operandIsArray--;
-          break;
+    var tableEntry = this._createTableEntry("maxp", virtualOffset, maxp);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += maxp.length;
 
-        case "if":
-          var procedure = operandStack.pop();
-          var bool = operandStack.pop();
-          if (!IsBool(bool)) {
-            dump("if: " + bool);
-            // we need to execute things, let be dirty
-            executionStack.push(bool);
-          } else {
-            dump("if ( " + bool + " ) { " + procedure + " }");
-            if (bool)
-              executionStack.push(procedure);
-          }
-          break;
 
-        case "ifelse":
-          var procedure1 = operandStack.pop();
-          var procedure2 = operandStack.pop();
-          var bool = !!operandStack.pop();
-          dump("if ( " + bool + " ) { " + procedure2 + " } else { " + procedure1 + " }");
-          executionStack.push(bool ? procedure2 : procedure1);
-          break;
+    /** NAME */
 
-        case "for":
-          var procedure = operandStack.pop();
-          var limit = operandStack.pop();
-          var increment = operandStack.pop();
-          var initial = operandStack.pop();
-          for (var i = 0; i < limit; i += increment) {
-            operandStack.push(i);
-            executionStack.push(procedure.slice());
-          }
-          break;
+    var name = [
+      0x00, 0x00, // format
+      0x00, 0x00, // Number of names Record
+      0x00, 0x00 // Storage
+    ];
+    var tableEntry = this._createTableEntry("name", virtualOffset, name);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += name.length;
 
-        case "dup":
-          dump("duplicate: " + operandStack.peek());
-          operandStack.push(operandStack.peek());
-          break;
 
-        case "mark":
-          operandStack.push("mark");
-          break;
+    /** POST */
 
-        case "cleartomark":
-          var command = "";
-          do {
-            command = operandStack.pop();
-          } while (command != "mark");
-          break;
+    // XXX get those info from the Font dict!
+    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
+    ];
+    var tableEntry = this._createTableEntry("post", virtualOffset, post);
+    otf.set(tableEntry, currentOffset);
+    currentOffset += tableEntry.length;
+    virtualOffset += post.length;
 
-        case "put":
-          var data = operandStack.pop();
-          var indexOrKey = operandStack.pop();
-          var object = operandStack.pop();
-          dump("put " + data + " in " + object + "[" + indexOrKey + "]");
-          object.set ? object.set(indexOrKey, data)
-                     : object[indexOrKey] = data;
-          break;
+    // Set the CFF data
+    otf.set(aFontData, currentOffset);
+    currentOffset += aFontData.length;
 
-        case "pop":
-          operandStack.pop();
-          break;
+    var tables = [OS2, cmap, head, hhea, hmtx, maxp, name, post];
+    for (var i = 0; i < tables.length; i++) {
+      var table = tables[i];
+      otf.set(table, currentOffset);
+      currentOffset += table.length;
+    }
 
-        case "exch":
-          var operand1 = operandStack.pop();
-          var operand2 = operandStack.pop();
-          operandStack.push(operand1);
-          operandStack.push(operand2);
-          break;
+    var fontData = [];
+    for (var i = 0; i < currentOffset; i++)
+      fontData.push(otf[i]);
 
-        case "get":
-          var indexOrKey = operandStack.pop();
-          var object = operandStack.pop();
-          var data = object.get ? object.get(indexOrKey) : object[indexOrKey];
-          dump("get " + object + "[" + indexOrKey + "]: " + data);
-          operandStack.push(data);
-          break;
+    //writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".otf");
+    return fontData;
+  }
+};
 
-        case "currentdict":
-          var dict = dictionaryStack.peek();
-          operandStack.push(dict);
-          break;
 
-        case "systemdict":
-          operandStack.push(systemDict);
-          break;
+var FontsUtils = {
+  integerToBytes: function fu_integerToBytes(aValue, aBytesCount) {
+    var bytes = [];
+    for (var i = 0; i < aBytesCount; i++)
+      bytes[i] = 0x00;
 
-        case "readonly":
-        case "executeonly":
-        case "noaccess":
-          // Do nothing for the moment
-          break;
+    do {
+      bytes[--aBytesCount] = (aValue & 0xFF);
+      aValue = aValue >> 8;
+    } while (aBytesCount && aValue > 0);
 
-        case "currentfile":
-          operandStack.push("currentfile");
-          break;
+    return bytes;
+  },
 
-        case "array":
-          var size = operandStack.pop();
-          var array = new Array(size);
-          operandStack.push(array);
-          break;
+  bytesToInteger: function(aBytesArray) {
+    var value = 0;
+    for (var i = 0; i < aBytesArray.length; i++)
+      value = (value << 8) + aBytesArray[i];
+    return value;
+  },
 
-        case "dict":
-          var size = operandStack.pop();
-          var dict = new Dict(size);
-          operandStack.push(dict);
-          break;
+  getMaxPower2: function fu_getMaxPower2(aNumber) {
+    var maxPower = 0;
+    var value = aNumber;
+    while (value >= 2) {
+      value /= 2;
+      maxPower++;
+    }
+
+    value = 2;
+    for (var i = 1; i < maxPower; i++)
+      value *= 2;
+    return value;
+  }
+};
 
-        case "begin":
-          dictionaryStack.push(operandStack.pop());
-          break;
 
-        case "end":
-          dictionaryStack.pop();
-          break;
+/** Implementation dirty logic starts here */
 
-        case "def":
-          var value = operandStack.pop();
-          var key = operandStack.pop();
-          dump("def: " + key + " = " + value);
-          dictionaryStack.peek().set(key, value);
-          break;
+/**
+ * At the moment TrueType is just a stub that does mostly nothing but in a
+ * (near?) future this class will rewrite the font to ensure it is well formed
+ * and valid in the point of view of the sanitizer.
+ */
+var TrueType = function(aFontName, aFontFile) {
+  this.data = aFontFile;
+};
 
-        case "definefont":
-          var font = operandStack.pop();
-          var key = operandStack.pop();
-          dump("definefont " + font + " with key: " + key);
+/**
+ * This dictionary holds decoded fonts data.
+ */
+var PSFonts = new Dict();
 
-          // The key will be the identifier to recognize this font
-          fontName = key;
-          PSFonts.set(key, font);
 
-          operandStack.push(font);
-          break;
+var Stack = function() {
+  var innerStack = [];
 
-        case "known":
-          var name = operandStack.pop();
-          var dict = operandStack.pop();
-          var data = !!dict.get(name);
-          dump("known: " + data + " :: " + name + " in dict: " + dict);
-          operandStack.push(data);
-          break;
+  this.push = function(aOperand) {
+    innerStack.push(aOperand);
+  };
 
-        case "exec":
-          executionStack.push(operandStack.pop());
-          break;
+  this.pop = function() {
+    if (!this.count())
+      throw new Error("stackunderflow");
+    return innerStack.pop();
+  };
 
-        case "eexec":
-          // All the first segment data has been read, decrypt the second segment
-          // and start interpreting it in order to decode it
-          var file = operandStack.pop();
-          var eexecString = decrypt(aBinaryStream, kEexecEncryptionKey, 4).join("");
-          dump(eexecString);
-          lexer = new Lexer(new StringStream(eexecString));
-          break;
+  this.peek = function() {
+    if (!this.count())
+      return null;
+    return innerStack[innerStack.length - 1];
+  };
 
-        case "LenIV":
-          error("LenIV: argh! we need to modify the length of discard characters for charStrings");
-          break;
+  this.get = function(aIndex) {
+    return innerStack[aIndex];
+  };
 
-        case "closefile":
-          var file = operandStack.pop();
-          return true;
-          break;
+  this.clear = function() {
+    innerStack = [];
+  };
 
-        case "index":
-          var operands = [];
-          var size = operandStack.pop();
-          for (var i = 0; i < size; i++)
-            operands.push(operandStack.pop());
+  this.count = function() {
+    return innerStack.length;
+  };
 
-          var newOperand = operandStack.peek();
+  this.dump = function() {
+    for (var i = 0; i < this.length; i++)
+      log(innerStack[i]);
+  };
 
-          while (operands.length)
-            operandStack.push(operands.pop());
+  this.clone = function() {
+    return innerStack.slice();
+  };
+};
 
-          operandStack.push(newOperand);
-          break;
+var Type1Parser = function(aAsciiStream, aBinaryStream) {
+  var lexer = aAsciiStream ? new Lexer(aAsciiStream) : null;
 
-        case "string":
-          var size = operandStack.pop();
-          var str = (new Array(size + 1)).join(" ");
-          operandStack.push(str);
-          break;
+  // Turn on this flag for additional debugging logs
+  var debug = false;
 
-        case "readstring":
-          var str = operandStack.pop();
-          var size = str.length;
+  var dump = function(aData) {
+    if (debug)
+      log(aData);
+  };
 
-          var file = operandStack.pop();
+  // Hold the fontName as declared inside the /FontName postscript directive
+  // XXX This is a hack but at the moment I need it to map the name declared
+  // in the PDF and the name in the PS code.
+  var fontName = "";
 
-          // Add '1' because of the space separator, this is dirty
-          var stream = lexer.stream.makeSubStream(lexer.stream.pos + 1, size);
-          lexer.stream.skip(size + 1);
+  /*
+   * Parse a whole Type1 font stream (from the first segment to the last)
+   * assuming the 'eexec' block is binary data and fill up the 'Fonts'
+   * dictionary with the font informations.
+   */
+  var self = this;
+  this.parse = function() {
+    if (!debug) {
+      while (!processNextToken()) {};
+      return fontName;
+    } else {
+      // debug mode is used to debug postcript processing
+      setTimeout(function() {
+        if (!processNextToken())
+          self.parse();
+      }, 0);
+    }
+  };
 
-          var charString = decrypt(stream, kCharStringsEncryptionKey, 4).join("");
-          var charStream = new StringStream(charString);
-          var decodedCharString = decodeCharString(charStream);
-          dump("decodedCharString: " + decodedCharString);
-          operandStack.push(decodedCharString);
+  /*
+   * Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
+   * of Plaintext Bytes. The function took a key as a parameter which can be
+   * for decrypting the eexec block of for decoding charStrings.
+   */
+  var kEexecEncryptionKey = 55665;
+  var kCharStringsEncryptionKey = 4330;
 
-          // boolean indicating if the operation is a success or not
-          operandStack.push(true);
-          break;
+  function decrypt(aStream, aKey, aDiscardNumber) {
+    var start = Date.now();
+    var r = aKey, c1 = 52845, c2 = 22719;
+    var decryptedString = [];
 
-        case "StandardEncoding":
-          // For some reason the value is considered as a command, maybe it is
-          // because of the uppercase 'S'
-          operandStack.push(obj.cmd);
-          break;
+    var value = "";
+    var count = aStream.length;
+    for (var i = 0; i < count; i++) {
+      value = aStream.getByte();
+      decryptedString[i] = String.fromCharCode(value ^ (r >> 8));
+      r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
+    }
+    var end = Date.now();
+    dump("Time to decrypt string of length " + count + " is " + (end - start));
+    return decryptedString.slice(aDiscardNumber);
+  };
 
-        default:
-          var command = null;
-          if (IsCmd(obj)) {
-            for (var i = 0; i < dictionaryStack.count(); i++) {
-              if (command = dictionaryStack.get(i).get(obj.cmd)) {
-                dump("found in dictionnary for " + obj.cmd + " command: " + command);
-                executionStack.push(command.slice());
-                break;
-              }
-            }
-          }
+  /*
+   * CharStrings are encoded following the the CharString Encoding sequence
+   * describe in Chapter 6 of the "Adobe Type1 Font Format" specification.
+   * The value in a byte indicates a command, a number, or subsequent bytes
+   * that are to be interpreted in a special way.
+   *
+   * CharString Number Encoding:
+   *  A CharString byte containing the values from 32 through 255 inclusive
+   *  indicate an integer. These values are decoded in four ranges.
+   *
+   * 1. A CharString byte containing a value, v, between 32 and 246 inclusive,
+   * indicate the integer v - 139. Thus, the integer values from -107 through
+   * 107 inclusive may be encoded in single byte.
+   *
+   * 2. A CharString byte containing a value, v, between 247 and 250 inclusive,
+   * indicates an integer involving the next byte, w, according to the formula:
+   * [(v - 247) x 256] + w + 108
+   *
+   * 3. A CharString byte containing a value, v, between 251 and 254 inclusive,
+   * indicates an integer involving the next byte, w, according to the formula:
+   * -[(v - 251) * 256] - w - 108
+   *
+   * 4. A CharString containing the value 255 indicates that the next 4 bytes
+   * are a two complement signed integer. The first of these bytes contains the
+   * highest order bits, the second byte contains the next higher order bits
+   * and the fourth byte contain the lowest order bits.
+   *
+   *
+   * CharString Command Encoding:
+   *  CharStrings commands are encoded in 1 or 2 bytes.
+   *
+   *  Single byte commands are encoded in 1 byte that contains a value between
+   *  0 and 31 inclusive.
+   *  If a command byte contains the value 12, then the value in the next byte
+   *  indicates a command. This "escape" mechanism allows many extra commands
+   * to be encoded and this encoding technique helps to minimize the length of
+   * the charStrings.
+   */
+  var charStringDictionary = {
+    "1": "hstem",
+    "3": "vstem",
+    "4": "vmoveto",
+    "5": "rlineto",
+    "6": "hlineto",
+    "7": "vlineto",
+    "8": "rrcurveto",
+    "9": "closepath",
+    "10": "callsubr",
+    "11": "return",
+    "12": {
+      "0": "dotsection",
+      "1": "vstem3",
+      "3": "hstem3",
+      "6": "seac",
+      "7": "sbw",
+      "12": "div",
+      "16": "callothersubr",
+      "17": "pop",
+      "33": "setcurrentpoint"
+    },
+    "13": "hsbw",
+    "14": "endchar",
+    "21": "rmoveto",
+    "22": "hmoveto",
+    "30": "vhcurveto",
+    "31": "hvcurveto"
+  };
+
+  function decodeCharString(aStream) {
+    var start = Date.now();
+    var charString = [];
 
-          if (!command) {
-            log("operandStack: " + operandStack);
-            log("dictionaryStack: " + dictionaryStack);
-            log(obj);
-            error("Unknow command while parsing font");
-          }
-          break;
+    var value = "";
+    var count = aStream.length;
+    for (var i = 0; i < count; i++) {
+      value = aStream.getByte();
+
+      if (value < 32) {
+        if (value == 12) {
+          value = charStringDictionary["12"][aStream.getByte()];
+          i++;
+        } else {
+          value = charStringDictionary[value];
+        }
+      } else if (value <= 246) {
+        value = parseInt(value) - 139;
+      } else if (value <= 250) {
+        value = ((value - 247) * 256) + parseInt(aStream.getByte()) + 108;
+        i++;
+      } else if (value <= 254) {
+        value = -((value - 251) * 256) - parseInt(aStream.getByte()) - 108;
+        i++;
+      } else {
+        var byte = aStream.getByte();
+        var high = (byte >> 1);
+        value = (byte - high) << 24 | aStream.getByte() << 16 |
+                aStream.getByte() << 8 | aStream.getByte();
+        i += 4;
       }
-    } else if (obj) {
-      dump("unknow: " + obj);
-      operandStack.push(obj);
-    } else { // The End!
-      operandStack.dump();
-      return true;
+
+      charString.push(value);
     }
 
-    return false;
+    var end = Date.now();
+    dump("Time to decode charString of length " + count + " is " + (end - start));
+    return charString;
   }
 
   /*
-   * Flatten the commands by interpreting the postscript code and replacing
-   * every 'callsubr', 'callothersubr' by the real commands.
-   * At the moment OtherSubrs are not fully supported and only otherSubrs 0-4
-   * as descrived in 'Using Subroutines' of 'Adobe Type 1 Font Format',
-   * chapter 8.
+   * The operand stack holds arbitrary PostScript objects that are the operands
+   * and results of PostScript operators being executed. The interpreter pushes
+   * objects on the operand stack when it encounters them as literal data in a
+   * program being executed. When an operator requires one or more operands, it
+   * obtains them by popping them off the top of the operand stack. When an
+   * operator returns one or more results, it does so by pushing them on the
+   * operand stack.
    */
-  this.flattenCharstring = function(aCharstring, aDefaultWidth, aSubrs) {
-    operandStack.clear();
-    executionStack.clear();
-    executionStack.push(aCharstring);
-
-    var leftSidebearing = 0;
-    var lastPoint = 0;
-    while (true) {
-      var obj = nextInStack();
-      if (IsBool(obj) || IsInt(obj) || IsNum(obj)) {
-        dump("Value: " + obj);
-        operandStack.push(obj);
-      } else if (IsString(obj)) {
-        dump("String: " + obj);
-        switch (obj) {
-          case "hsbw":
-            var charWidthVector = operandStack.pop();
-            leftSidebearing = operandStack.pop();
-
-            if (charWidthVector != aDefaultWidth)
-              operandStack.push(charWidthVector - aDefaultWidth);
-            break;
-
-          case "rmoveto":
-            var dy = operandStack.pop();
-            var dx = operandStack.pop();
-
-            if (leftSidebearing) {
-              dx += leftSidebearing;
-              leftSidebearing = 0;
-            }
-
-            operandStack.push(dx);
-            operandStack.push(dy);
-            operandStack.push("rmoveto");
-            break;
-
-          case "div":
-            var num2 = operandStack.pop();
-            var num1 = operandStack.pop();
-            operandStack.push(num2 / num1);
-            break;
-
-          case "setcurrentpoint":
-          case "dotsection":
-          case "seac":
-          case "sbw":
-            error(obj + " parsing is not implemented (yet)");
-            break;
-
-          case "closepath":
-          case "return":
-            break;
-
-          case "vstem3":
-          case "vstem":
-            operandStack.push("vstem");
-            break;
-
-          case "hstem":
-          case "hstem3":
-            operandStack.push("hstem");
-            break;
+   var operandStack = new Stack();
 
-          case "callsubr":
-            var index = operandStack.pop();
-            executionStack.push(aSubrs[index].slice());
-            break;
+   // Flag indicating if the topmost operand of the operandStack is an array
+   var operandIsArray = 0;
 
-          case "callothersubr":
-            // XXX need to be improved
-            var index = operandStack.pop();
-            var count = operandStack.pop();
-            var data = operandStack.pop();
-            if (index != 3)
-              dump("callothersubr for index: " + index);
-            operandStack.push(3);
-            operandStack.push("callothersubr");
-            break;
+  /*
+   * The dictionary stack holds only dictionary objects. The current set of
+   * dictionaries on the dictionary stack defines the environment for all
+   * implicit name searches, such as those that occur when the interpreter
+   * encounters an executable name. The role of the dictionary stack is
+   * introduced in Section 3.3, “Data Types and Objects,” and is further
+   * explained in Section 3.5, “Execution.” of the PostScript Language
+   * Reference.
+   */
+  var systemDict = new Dict(),
+      globalDict = new Dict(),
+      userDict   = new Dict();
 
-          case "endchar":
-            operandStack.push("endchar");
-            return operandStack.clone();
+  var dictionaryStack = new Stack();
+  dictionaryStack.push(systemDict);
+  dictionaryStack.push(globalDict);
+  dictionaryStack.push(userDict);
 
-          case "pop":
-            operandStack.pop();
-            break;
+  /*
+   * The execution stack holds executable objects (mainly procedures and files)
+   * that are in intermediate stages of execution. At any point in the
+   * execution of a PostScript program, this stack represents the program’s
+   * call stack. Whenever the interpreter suspends execution of an object to
+   * execute some other object, it pushes the new object on the execution
+   * stack. When the interpreter finishes executing an object, it pops that
+   * object off the execution stack and resumes executing the suspended object
+   * beneath it.
+   */
+  var executionStack = new Stack();
 
-          default:
-            operandStack.push(obj);
-            break;
-        }
-      }
+  /*
+   * Return the next token in the execution stack
+   */
+  function nextInStack() {
+    var currentProcedure = executionStack.peek();
+    if (currentProcedure) {
+      var command = currentProcedure.shift();
+      if (!currentProcedure.length)
+        executionStack.pop();
+      return command;
     }
-  }
-};
 
+    return lexer.getObj();
+  };
+
+  /*
+   * Get the next token from the executionStack and process it.
+   * Actually the function does not process the third segment of a Type1 font
+   * and end on 'closefile'.
+   *
+   * The method thrown an error if it encounters an unknown token.
+   */
+  function processNextToken() {
+    var obj = nextInStack();
+    if (operandIsArray && !IsCmd(obj, "{") && !IsCmd(obj, "[") &&
+                          !IsCmd(obj, "]") && !IsCmd(obj, "}")) {
+      dump("Adding an object: " + obj +" to array " + operandIsArray);
+      var currentArray = operandStack.peek();
+      for (var i = 1; i < operandIsArray; i++)
+        currentArray = currentArray[currentArray.length - 1];
 
-var fontCount = 0;
-var Type1 = function(aFontName, aFontFile) {
-  // All Type1 font program should begin with the comment %!
-  if (aFontFile.getByte() != 0x25 || aFontFile.getByte() != 0x21)
-    error("Invalid file header");
+      currentArray.push(obj);
+    } else if (IsBool(obj) || IsInt(obj) || IsNum(obj) || IsString(obj)) {
+      dump("Value: " + obj);
+      operandStack.push(obj);
+    } else if (IsName(obj)) {
+      dump("Name: " + obj.name);
+      operandStack.push(obj.name);
+    } else if (IsCmd(obj)) {
+      var command = obj.cmd;
+      dump(command);
 
-  if (!fontCount || true) {
-    fontCount++;
-    var start = Date.now();
+      switch (command) {
+        case "[":
+        case "{":
+          dump("Start" + (command == "{" ? " Executable " : " ") + "Array");
+          operandIsArray++;
+          var currentArray = operandStack;
+          for (var i = 1; i < operandIsArray; i++)
+            if (currentArray.peek)
+              currentArray = currentArray.peek();
+            else
+              currentArray = currentArray[currentArray.length - 1];
+          currentArray.push([]);
+          break;
 
-    var ASCIIStream = aFontFile.makeSubStream(0, aFontFile.dict.get("Length1"), aFontFile.dict);
-    var binaryStream = aFontFile.makeSubStream(aFontFile.dict.get("Length1"), aFontFile.dict.get("Length2"), aFontFile.dict);
+        case "]":
+        case "}":
+          var currentArray = operandStack.peek();
+          for (var i = 1; i < operandIsArray; i++)
+            currentArray = currentArray[currentArray.length - 1];
+          dump("End" + (command == "}" ? " Executable " : " ") + "Array: " + currentArray.join(" "));
+          operandIsArray--;
+          break;
 
-    this.parser = new Type1Parser(ASCIIStream, binaryStream);
-    var fontName = this.parser.parse();
-    var font = PSFonts.get(fontName);
-    this.data = this.convertToOTF(this.convertToCFF(font), font);
-    var end = Date.now();
-    log("Time to parse font is:" + (end - start));
-  }
-};
+        case "if":
+          var procedure = operandStack.pop();
+          var bool = operandStack.pop();
+          if (!IsBool(bool)) {
+            dump("if: " + bool);
+            // we need to execute things, let be dirty
+            executionStack.push(bool);
+          } else {
+            dump("if ( " + bool + " ) { " + procedure + " }");
+            if (bool)
+              executionStack.push(procedure);
+          }
+          break;
 
-Type1.prototype = {
-  getDefaultWidth: function(aCharstrings) {
-    var defaultWidth = 0;
-    var defaultUsedCount = 0;
+        case "ifelse":
+          var procedure1 = operandStack.pop();
+          var procedure2 = operandStack.pop();
+          var bool = !!operandStack.pop();
+          dump("if ( " + bool + " ) { " + procedure2 + " } else { " + procedure1 + " }");
+          executionStack.push(bool ? procedure2 : procedure1);
+          break;
 
-    var widths = {};
-    for (var i = 0; i < aCharstrings.length; i++) {
-      var width = aCharstrings[i].charstring[1];
-      var usedCount = (widths[width] || 0) + 1;
+        case "for":
+          var procedure = operandStack.pop();
+          var limit = operandStack.pop();
+          var increment = operandStack.pop();
+          var initial = operandStack.pop();
+          for (var i = 0; i < limit; i += increment) {
+            operandStack.push(i);
+            executionStack.push(procedure.slice());
+          }
+          break;
 
-      if (usedCount > defaultUsedCount) {
-        defaultUsedCount = usedCount;
-        defaultWidth = width;
-      }
+        case "dup":
+          dump("duplicate: " + operandStack.peek());
+          operandStack.push(operandStack.peek());
+          break;
 
-      widths[width] = usedCount;
-    }
-    return parseInt(defaultWidth);
-  },
+        case "mark":
+          operandStack.push("mark");
+          break;
 
-  createCFFIndexHeader: function(aObjects, aIsByte) {
-    var data = [];
+        case "cleartomark":
+          var command = "";
+          do {
+            command = operandStack.pop();
+          } while (command != "mark");
+          break;
 
-    // First 2 bytes contains the number of objects contained into this index
-    var count = aObjects.length;
-    if (count ==0)
-      return [0x00, 0x00, 0x00];
+        case "put":
+          var data = operandStack.pop();
+          var indexOrKey = operandStack.pop();
+          var object = operandStack.pop();
+          dump("put " + data + " in " + object + "[" + indexOrKey + "]");
+          object.set ? object.set(indexOrKey, data)
+                     : object[indexOrKey] = data;
+          break;
 
-    var bytes = this.integerToBytes(count, 2);
-    for (var i = 0; i < bytes.length; i++)
-      data.push(bytes[i]);
+        case "pop":
+          operandStack.pop();
+          break;
 
-    // Next byte contains the offset size use to reference object in the file
-    // Actually we're using 0x04 to be sure to be able to store everything
-    // without thinking of it while coding.
-    data.push(0x04);
+        case "exch":
+          var operand1 = operandStack.pop();
+          var operand2 = operandStack.pop();
+          operandStack.push(operand1);
+          operandStack.push(operand2);
+          break;
 
-    // Add another offset after this one because we need a new offset
-    var relativeOffset = 1;
-    for (var i = 0; i < count + 1; i++) {
-      var bytes = this.integerToBytes(relativeOffset, 4);
-      for (var j = 0; j < bytes.length; j++)
-        data.push(bytes[j]);
+        case "get":
+          var indexOrKey = operandStack.pop();
+          var object = operandStack.pop();
+          var data = object.get ? object.get(indexOrKey) : object[indexOrKey];
+          dump("get " + object + "[" + indexOrKey + "]: " + data);
+          operandStack.push(data);
+          break;
 
-      if (aObjects[i])
-        relativeOffset += aObjects[i].length;
-    }
+        case "currentdict":
+          var dict = dictionaryStack.peek();
+          operandStack.push(dict);
+          break;
 
-    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));
-    }
-    return data;
-  },
+        case "systemdict":
+          operandStack.push(systemDict);
+          break;
 
-  integerToBytes: function(aValue, aBytesCount) {
-    var bytes = [];
-    for (var i = 0; i < aBytesCount; i++)
-      bytes[i] = 0x00;
+        case "readonly":
+        case "executeonly":
+        case "noaccess":
+          // Do nothing for the moment
+          break;
 
-    do {
-      bytes[--aBytesCount] = (aValue & 0xFF);
-      aValue = aValue >> 8;
-    } while (aBytesCount && aValue > 0);
+        case "currentfile":
+          operandStack.push("currentfile");
+          break;
 
-    return bytes;
-  },
+        case "array":
+          var size = operandStack.pop();
+          var array = new Array(size);
+          operandStack.push(array);
+          break;
 
-  bytesToInteger: function(aBytesArray) {
-    var value = 0;
-    for (var i = 0; i < aBytesArray.length; i++)
-      value = (value << 8) + aBytesArray[i];
-    return value;
-  },
+        case "dict":
+          var size = operandStack.pop();
+          var dict = new Dict(size);
+          operandStack.push(dict);
+          break;
 
-  encodeNumber: function(aValue) {
-    var x = 0;
-    // XXX we don't really care about Type2 optimization here...
-    if (aValue >= -32768 && aValue <= 32767) {
-      return [
-        28,
-        this.integerToBytes(aValue >> 8, 1),
-        this.integerToBytes(aValue, 1)
-      ];
-    } else if (aValue >= (-2147483647-1) && aValue <= 2147483647) {
-      return [
-        0xFF,
-        this.integerToBytes(aValue >> 24, 1),
-        this.integerToBytes(aValue >> 16, 1),
-        this.integerToBytes(aValue >> 8, 1),
-        this.integerToBytes(aValue, 1)
-      ];
-    } else {
-      error("Value: " + aValue + " is not allowed");
-    }
-  },
+        case "begin":
+          dictionaryStack.push(operandStack.pop());
+          break;
 
-  getOrderedCharStrings: function(aFont) {
-    var dict = aFont.get("CharStrings")
-    var charstrings = [];
-    for (var glyph in dict.map) {
-      var unicode = GlyphsUnicode[glyph];
-      if (!unicode) {
-        if (glyph != ".notdef")
-          warn(glyph + " does not have an entry in the glyphs unicode dictionary");
-        continue;
-      }
+        case "end":
+          dictionaryStack.pop();
+          break;
 
-      var b1 = parseInt("0x" + unicode[0] + unicode[1]);
-      var b2 = parseInt("0x" + unicode[2] + unicode[3]);
-      unicode = this.bytesToInteger([b1, b2]);
+        case "def":
+          var value = operandStack.pop();
+          var key = operandStack.pop();
+          dump("def: " + key + " = " + value);
+          dictionaryStack.peek().set(key, value);
+          break;
 
-      charstrings.push({
-        glyph: glyph,
-        unicode: unicode,
-        charstring: dict.map[glyph].slice()
-      });
-    }
+        case "definefont":
+          var font = operandStack.pop();
+          var key = operandStack.pop();
+          dump("definefont " + font + " with key: " + key);
 
-    charstrings.sort(function(a, b) {
-      return a.unicode > b.unicode;
-    });
-    return charstrings;
-  },
+          // The key will be the identifier to recognize this font
+          fontName = key;
+          PSFonts.set(key, font);
 
-  convertToCFF: function(aFont) {
-    var debug = false;
-    function dump(aMsg) {
-      if (debug)
-        log(aMsg);
-    };
+          operandStack.push(font);
+          break;
 
-    var charstrings = this.getOrderedCharStrings(aFont);
-    var defaultWidth = this.getDefaultWidth(charstrings);
+        case "known":
+          var name = operandStack.pop();
+          var dict = operandStack.pop();
+          var data = !!dict.get(name);
+          dump("known: " + data + " :: " + name + " in dict: " + dict);
+          operandStack.push(data);
+          break;
 
-    var charstringsCount = 0;
-    var charstringsDataLength = 0;
-    var glyphs = [];
-    var glyphsChecker = {};
-    var subrs = aFont.get("Private").get("Subrs");
-    var parser = new Type1Parser();
-    for (var i = 0; i < charstrings.length; i++) {
-      var charstring = charstrings[i].charstring.slice();
-      var glyph = charstrings[i].glyph;
-      if (glyphsChecker[glyph])
-        error("glyphs already exists!");
-      glyphsChecker[glyph] = true;
+        case "exec":
+          executionStack.push(operandStack.pop());
+          break;
 
-      var flattened = parser.flattenCharstring(charstring, defaultWidth, subrs);
-      glyphs.push(flattened);
-      charstringsCount++;
-      charstringsDataLength += flattened.length;
-    }
-    dump("There is " + charstringsCount + " glyphs (size: " + charstringsDataLength + ")");
+        case "eexec":
+          // All the first segment data has been read, decrypt the second segment
+          // and start interpreting it in order to decode it
+          var file = operandStack.pop();
+          var eexecString = decrypt(aBinaryStream, kEexecEncryptionKey, 4).join("");
+          dump(eexecString);
+          lexer = new Lexer(new StringStream(eexecString));
+          break;
 
-    // Create a CFF font data
-    var cff = new Uint8Array(kMaxFontFileSize);
-    var currentOffset = 0;
+        case "LenIV":
+          error("LenIV: argh! we need to modify the length of discard characters for charStrings");
+          break;
 
-    // Font header (major version, minor version, header size, offset size)
-    var header = [0x01, 0x00, 0x04, 0x04];
-    currentOffset += header.length;
-    cff.set(header);
+        case "closefile":
+          var file = operandStack.pop();
+          return true;
+          break;
 
-    // Names Index
-    var nameIndex = this.createCFFIndexHeader([aFont.get("FontName")]);
-    cff.set(nameIndex, currentOffset);
-    currentOffset += nameIndex.length;
+        case "index":
+          var operands = [];
+          var size = operandStack.pop();
+          for (var i = 0; i < size; i++)
+            operands.push(operandStack.pop());
 
-    // Calculate strings before writing the TopDICT index in order
-    // to calculate correct relative offsets for storing 'charset'
-    // and 'charstrings' data
-    var fontInfo = aFont.get("FontInfo");
-    var version = fontInfo.get("version");
-    var notice = fontInfo.get("Notice");
-    var fullName = fontInfo.get("FullName");
-    var familyName = fontInfo.get("FamilyName");
-    var weight = fontInfo.get("Weight");
-    var strings = [version, notice, fullName,
-                   familyName, weight, "asteriskmath"];
-    var stringsIndex = this.createCFFIndexHeader(strings);
-    var stringsDataLength = stringsIndex.length;
+          var newOperand = operandStack.peek();
 
-    // Create the global subroutines index
-    var globalSubrsIndex = this.createCFFIndexHeader([]);
+          while (operands.length)
+            operandStack.push(operands.pop());
 
-    // Fill the charset header (first byte is the encoding)
-    var charset = [0x00];
-    for (var i = 0; i < glyphs.length; i++) {
-      var index = CFFStrings.indexOf(charstrings[i].glyph);
-      if (index == -1)
-        index = CFFStrings.length + strings.indexOf(glyph);
-      var bytes = this.integerToBytes(index, 2);
-      charset.push(bytes[0]);
-      charset.push(bytes[1]);
-    }
+          operandStack.push(newOperand);
+          break;
 
-    // Convert charstrings
-    var getNumFor = {
-      "hstem": 1,
-      "vstem": 3,
-      "vmoveto": 4,
-      "rlineto": 5,
-      "hlineto": 6,
-      "vlineto": 7,
-      "rrcurveto": 8,
-      "endchar": 14,
-      "rmoveto": 21,
-      "hmoveto": 22,
-      "vhcurveto": 30,
-      "hvcurveto": 31,
-    };
+        case "string":
+          var size = operandStack.pop();
+          var str = (new Array(size + 1)).join(" ");
+          operandStack.push(str);
+          break;
 
-    // Encode the glyph and add it to the FUX
-    var r = [[0x40, 0x0E]];
-    for (var i = 0; i < glyphs.length; i++) {
-      var data = glyphs[i].slice();
-      var charstring = [];
-      for (var j = 0; j < data.length; j++) {
-        var c = data[j];
-        if (!IsNum(c)) {
-          var token = getNumFor[c];
-          if (!token)
-            error(c);
-          charstring.push(token);
-        } else {
-          try {
-            var bytes = this.encodeNumber(c);
-          } catch(e) {
-            log("Glyph " + i + " has a wrong value: " + c + " in charstring: " + data);
-            log("the default value is glyph " + charstrings[i].glyph + " and is supposed to be: " + charstrings[i].charstring);
-          }
-          for (var k = 0; k < bytes.length; k++)
-            charstring.push(bytes[k]);
-        }
-      }
-      r.push(charstring);
-    }
+        case "readstring":
+          var str = operandStack.pop();
+          var size = str.length;
 
-    var charstringsIndex = this.createCFFIndexHeader(r, true);
-    charstringsIndex = charstringsIndex.join(" ").split(" "); // XXX why?
+          var file = operandStack.pop();
 
+          // Add '1' because of the space separator, this is dirty
+          var stream = lexer.stream.makeSubStream(lexer.stream.pos + 1, size);
+          lexer.stream.skip(size + 1);
 
-    var fontBBox = aFont.get("FontBBox");
+          var charString = decrypt(stream, kCharStringsEncryptionKey, 4).join("");
+          var charStream = new StringStream(charString);
+          var decodedCharString = decodeCharString(charStream);
+          dump("decodedCharString: " + decodedCharString);
+          operandStack.push(decodedCharString);
 
-    //Top Dict Index
-    var topDictIndex = [
-      0x00, 0x01, 0x01, 0x01, 0x30,
-      248, 27, 0, // version
-      248, 28, 1, // Notice
-      248, 29, 2, // FullName
-      248, 30, 3, // FamilyName
-      248, 31, 4, // Weight
-    ];
+          // boolean indicating if the operation is a success or not
+          operandStack.push(true);
+          break;
 
-    for (var i = 0; i < fontBBox.length; i++)
-      topDictIndex = topDictIndex.concat(this.encodeNumber(fontBBox[i]));
-    topDictIndex.push(5) // FontBBox;
+        case "StandardEncoding":
+          // For some reason the value is considered as a command, maybe it is
+          // because of the uppercase 'S'
+          operandStack.push(obj.cmd);
+          break;
 
-    var charsetOffset = currentOffset +
-                        (topDictIndex.length + (4 + 4 + 4 + 7)) +
-                        stringsIndex.length +
-                        globalSubrsIndex.length;
-    topDictIndex = topDictIndex.concat(this.encodeNumber(charsetOffset));
-    topDictIndex.push(15); // charset
+        default:
+          var command = null;
+          if (IsCmd(obj)) {
+            for (var i = 0; i < dictionaryStack.count(); i++) {
+              if (command = dictionaryStack.get(i).get(obj.cmd)) {
+                dump("found in dictionnary for " + obj.cmd + " command: " + command);
+                executionStack.push(command.slice());
+                break;
+              }
+            }
+          }
 
-    topDictIndex = topDictIndex.concat([28, 0, 0, 16]) // Encoding
+          if (!command) {
+            log("operandStack: " + operandStack);
+            log("dictionaryStack: " + dictionaryStack);
+            log(obj);
+            error("Unknow command while parsing font");
+          }
+          break;
+      }
+    } else if (obj) {
+      dump("unknow: " + obj);
+      operandStack.push(obj);
+    } else { // The End!
+      operandStack.dump();
+      return true;
+    }
 
-    var charstringsOffset = charsetOffset + (charstringsCount * 2) + 1;
-    topDictIndex = topDictIndex.concat(this.encodeNumber(charstringsOffset));
-    topDictIndex.push(17); // charstrings
+    return false;
+  }
 
-    topDictIndex = topDictIndex.concat([28, 0, 55])
-    var privateOffset = charstringsOffset + charstringsIndex.length;
-    topDictIndex = topDictIndex.concat(this.encodeNumber(privateOffset));
-    topDictIndex.push(18); // Private
-    topDictIndex = topDictIndex.join(" ").split(" ");
+  /*
+   * Flatten the commands by interpreting the postscript code and replacing
+   * every 'callsubr', 'callothersubr' by the real commands.
+   * At the moment OtherSubrs are not fully supported and only otherSubrs 0-4
+   * as descrived in 'Using Subroutines' of 'Adobe Type 1 Font Format',
+   * chapter 8.
+   */
+  this.flattenCharstring = function(aCharstring, aDefaultWidth, aSubrs) {
+    operandStack.clear();
+    executionStack.clear();
+    executionStack.push(aCharstring);
 
-    // Top Dict Index
-    cff.set(topDictIndex, currentOffset);
-    currentOffset += topDictIndex.length;
+    var leftSidebearing = 0;
+    var lastPoint = 0;
+    while (true) {
+      var obj = nextInStack();
+      if (IsBool(obj) || IsInt(obj) || IsNum(obj)) {
+        dump("Value: " + obj);
+        operandStack.push(obj);
+      } else if (IsString(obj)) {
+        dump("String: " + obj);
+        switch (obj) {
+          case "hsbw":
+            var charWidthVector = operandStack.pop();
+            leftSidebearing = operandStack.pop();
 
-    // Strings Index
-    cff.set(stringsIndex, currentOffset);
-    currentOffset += stringsIndex.length;
+            if (charWidthVector != aDefaultWidth)
+              operandStack.push(charWidthVector - aDefaultWidth);
+            break;
 
-    // Global Subrs Index
-    cff.set(globalSubrsIndex, currentOffset);
-    currentOffset += globalSubrsIndex.length;
+          case "rmoveto":
+            var dy = operandStack.pop();
+            var dx = operandStack.pop();
 
-    // Charset Index
-    cff.set(charset, currentOffset);
-    currentOffset += charset.length;
+            if (leftSidebearing) {
+              dx += leftSidebearing;
+              leftSidebearing = 0;
+            }
 
-    // Fill charstrings data
-    cff.set(charstringsIndex, currentOffset);
-    currentOffset += charstringsIndex.length;
+            operandStack.push(dx);
+            operandStack.push(dy);
+            operandStack.push("rmoveto");
+            break;
 
-    // Private Data
-    var privateData = [
-      248, 136, 20,
-      248, 136, 21,
-      119, 159, 248, 97, 159, 247, 87, 159, 6,
-      30, 10, 3, 150, 37, 255, 12, 9,
-      139, 12, 10,
-      172, 10,
-      172, 150, 143, 146, 150, 146, 12, 12,
-      247, 32, 11,
-      247, 10, 161, 147, 154, 150, 143, 12, 13,
-      139, 12, 14,
-      28, 0, 55, 19
-    ];
-    cff.set(privateData, currentOffset);
-    currentOffset += privateData.length;
+          case "div":
+            var num2 = operandStack.pop();
+            var num1 = operandStack.pop();
+            operandStack.push(num2 / num1);
+            break;
 
-    // Dump shit at the end of the file
-    var shit = [
-      0x00, 0x01, 0x01, 0x01,
-      0x13, 0x5D, 0x65, 0x64,
-      0x5E, 0x5B, 0xAF, 0x66,
-      0xBA, 0xBB, 0xB1, 0xB0,
-      0xB9, 0xBA, 0x65, 0xB2,
-      0x5C, 0x1F, 0x0B
-    ];
-    cff.set(shit, currentOffset);
-    currentOffset += shit.length;
+          case "setcurrentpoint":
+          case "dotsection":
+          case "seac":
+          case "sbw":
+            error(obj + " parsing is not implemented (yet)");
+            break;
 
+          case "closepath":
+          case "return":
+            break;
 
-    dump("==================== debug ====================");
-    //var file = new Uint8Array(cff, 0, currentOffset);
-    //var parser = new Type2Parser();
-    //parser.parse(new Stream(file));
+          case "vstem3":
+          case "vstem":
+            operandStack.push("vstem");
+            break;
 
-    var fontData = [];
-    for (var i = 0; i < currentOffset; i++)
-      fontData.push(cff[i]);
+          case "hstem":
+          case "hstem3":
+            operandStack.push("hstem");
+            break;
 
-    //log("== write to file");
-    //writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
+          case "callsubr":
+            var index = operandStack.pop();
+            executionStack.push(aSubrs[index].slice());
+            break;
 
-    return fontData;
-  },
+          case "callothersubr":
+            // XXX need to be improved
+            var index = operandStack.pop();
+            var count = operandStack.pop();
+            var data = operandStack.pop();
+            if (index != 3)
+              dump("callothersubr for index: " + index);
+            operandStack.push(3);
+            operandStack.push("callothersubr");
+            break;
 
-  getMaxPower2: function(aNumber) {
-    var maxPower = 0;
-    var value = aNumber;
-    while (value >= 2) {
-      value /= 2;
-      maxPower++;
+          case "endchar":
+            operandStack.push("endchar");
+            return operandStack.clone();
+
+          case "pop":
+            operandStack.pop();
+            break;
+
+          default:
+            operandStack.push(obj);
+            break;
+        }
+      }
     }
+  }
+};
 
-    value = 2;
-    for (var i = 1; i < maxPower; i++)
-      value *= 2;
-    return value;
-  },
 
-  createOpenTypeHeader: function(aNumTables) {
-    // sfnt version (4 bytes)
-    var version = [0x4F, 0x54, 0x54, 0X4F];
+var fontCount = 0;
+var CFF = function(aFontName, aFontFile) {
+  if (!fontCount || true) {
+    fontCount++;
+    var start = Date.now();
 
-    // numTables (2 bytes)
-    var numTables = aNumTables;
+    var ASCIIStream = aFontFile.makeSubStream(0, aFontFile.dict.get("Length1"), aFontFile.dict);
+    var binaryStream = aFontFile.makeSubStream(aFontFile.dict.get("Length1"), aFontFile.dict.get("Length2"), aFontFile.dict);
 
-    // searchRange (2 bytes)
-    var searchRange = this.getMaxPower2(numTables) * 16;
+    this.parser = new Type1Parser(ASCIIStream, binaryStream);
+    var fontName = this.parser.parse();
+    this.font = PSFonts.get(fontName);
+    this.data = this.convertToCFF(this.font);
+    var end = Date.now();
+    log("Time to parse font is:" + (end - start));
+  }
+};
 
-    // entrySelector (2 bytes)
-    var entrySelector = Math.log(this.getMaxPower2(numTables)) / Math.log(2);
+CFF.prototype = {
+  getDefaultWidth: function(aCharstrings) {
+    var defaultWidth = 0;
+    var defaultUsedCount = 0;
 
-    // rangeShift (2 bytes)
-    var rangeShift = numTables * 16 - searchRange;
+    var widths = {};
+    for (var i = 0; i < aCharstrings.length; i++) {
+      var width = aCharstrings[i].charstring[1];
+      var usedCount = (widths[width] || 0) + 1;
 
-    return [].concat(version,
-                     this.integerToBytes(numTables, 2),
-                     this.integerToBytes(searchRange, 2),
-                     this.integerToBytes(entrySelector, 2),
-                     this.integerToBytes(rangeShift, 2));
+      if (usedCount > defaultUsedCount) {
+        defaultUsedCount = usedCount;
+        defaultWidth = width;
+      }
+
+      widths[width] = usedCount;
+    }
+    return parseInt(defaultWidth);
   },
 
-  createTableEntry: function(aTag, aOffset, aData) {
-    // tag
-    var tag = [
-      aTag.charCodeAt(0),
-      aTag.charCodeAt(1),
-      aTag.charCodeAt(2),
-      aTag.charCodeAt(3)
-    ];
+  createCFFIndexHeader: function(aObjects, aIsByte) {
+    var data = [];
 
-    // offset
-    var offset = aOffset;
+    // First 2 bytes contains the number of objects contained into this index
+    var count = aObjects.length;
+    if (count ==0)
+      return [0x00, 0x00, 0x00];
 
-    // length
-    // Per spec tables must be 4-bytes align so add some 0x00 if needed
-    while (aData.length & 3)
-      aData.push(0x00);
+    var bytes = FontsUtils.integerToBytes(count, 2);
+    for (var i = 0; i < bytes.length; i++)
+      data.push(bytes[i]);
 
-    var length = aData.length;
+    // Next byte contains the offset size use to reference object in the file
+    // Actually we're using 0x04 to be sure to be able to store everything
+    // without thinking of it while coding.
+    data.push(0x04);
 
-    // checksum
-    var checksum = this.bytesToInteger(tag) + offset + length;
+    // Add another offset after this one because we need a new offset
+    var relativeOffset = 1;
+    for (var i = 0; i < count + 1; i++) {
+      var bytes = FontsUtils.integerToBytes(relativeOffset, 4);
+      for (var j = 0; j < bytes.length; j++)
+        data.push(bytes[j]);
 
-    return [].concat(tag,
-                    this.integerToBytes(checksum, 4),
-                    this.integerToBytes(offset, 4),
-                    this.integerToBytes(length, 4));
+      if (aObjects[i])
+        relativeOffset += aObjects[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));
+    }
+    return data;
   },
 
-  convertToOTF: function(aData, aFont) {
-    var otf = new Uint8Array(kMaxFontFileSize);
-    var currentOffset = 0;
+  encodeNumber: function(aValue) {
+    var x = 0;
+    // XXX we don't really care about Type2 optimization here...
+    if (aValue >= -32768 && aValue <= 32767) {
+      return [
+        28,
+        FontsUtils.integerToBytes(aValue >> 8, 1),
+        FontsUtils.integerToBytes(aValue, 1)
+      ];
+    } else if (aValue >= (-2147483647-1) && aValue <= 2147483647) {
+      return [
+        0xFF,
+        FontsUtils.integerToBytes(aValue >> 24, 1),
+        FontsUtils.integerToBytes(aValue >> 16, 1),
+        FontsUtils.integerToBytes(aValue >> 8, 1),
+        FontsUtils.integerToBytes(aValue, 1)
+      ];
+    } else {
+      error("Value: " + aValue + " is not allowed");
+    }
+  },
 
-    var numTables = 9;
-    var header = this.createOpenTypeHeader(numTables);
-    otf.set(header, currentOffset);
-    currentOffset += header.length;
+  getOrderedCharStrings: function(aFont) {
+    var dict = aFont.get("CharStrings")
+    var charstrings = [];
+    for (var glyph in dict.map) {
+      var unicode = GlyphsUnicode[glyph];
+      if (!unicode) {
+        if (glyph != ".notdef")
+          warn(glyph + " does not have an entry in the glyphs unicode dictionary");
+        continue;
+      }
 
-    var baseOffset = numTables * (4 * 4) + currentOffset;
-    var virtualOffset = baseOffset;
-    var tableEntry = this.createTableEntry("CFF ", virtualOffset, aData);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += aData.length;
+      var b1 = parseInt("0x" + unicode[0] + unicode[1]);
+      var b2 = parseInt("0x" + unicode[2] + unicode[3]);
+      unicode = FontsUtils.bytesToInteger([b1, b2]);
 
-    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
-      0x00, 0x00, 0x00, 0x01, // ulUnicodeRange1 (Bits 0-31)
-      0x00, 0x00, 0x00, 0x00, // ulUnicodeRange2 (Bits 32-63)
-      0x00, 0x00, 0x00, 0x00, // ulUnicodeRange3 (Bits 64-95)
-      0x00, 0x00, 0x00, 0x00, // ulUnicodeRange4 (Bits 96-127)
-      0x47, 0x49, 0x60, 0x20, // 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
-    ];
+      charstrings.push({
+        glyph: glyph,
+        unicode: unicode,
+        charstring: dict.map[glyph].slice()
+      });
+    }
 
-    var tableEntry = this.createTableEntry("OS/2", virtualOffset, OS2);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += OS2.length;
+    charstrings.sort(function(a, b) {
+      return a.unicode > b.unicode;
+    });
+    return charstrings;
+  },
 
-    /** CMAP */
-    var charstrings = this.getOrderedCharStrings(aFont);
+  convertToCFF: function(aFont) {
+    var debug = false;
+    function dump(aMsg) {
+      if (debug)
+        log(aMsg);
+    };
 
-    if (false) {
-      var cmap = [
-        0x00, 0x00, // version
-        0x00, 0x01, // numTables
-        0x00, 0x01, // platformID
-        0x00, 0x00, // encodingID
-        0x00, 0x00, 0x00, 0x0C, //offset
-        0x00, 0x00,
-        0x01, 0x06,
-        0x00, 0x00
-      ];
+    var charstrings = this.getOrderedCharStrings(aFont);
+    var defaultWidth = this.getDefaultWidth(charstrings);
 
-      var data = [];
-      for (var i = 0; i < 262; i++) {
-        data.push(0x00);
-      }
+    var charstringsCount = 0;
+    var charstringsDataLength = 0;
+    var glyphs = [];
+    var glyphsChecker = {};
+    var subrs = aFont.get("Private").get("Subrs");
+    var parser = new Type1Parser();
+    for (var i = 0; i < charstrings.length; i++) {
+      var charstring = charstrings[i].charstring.slice();
+      var glyph = charstrings[i].glyph;
+      if (glyphsChecker[glyph])
+        error("glyphs already exists!");
+      glyphsChecker[glyph] = true;
 
-      for (var i = 0; i < charstrings.length; i++)
-        data[charstrings[i].unicode] = i + 1;
-      cmap = cmap.concat(data);
+      var flattened = parser.flattenCharstring(charstring, defaultWidth, subrs);
+      glyphs.push(flattened);
+      charstringsCount++;
+      charstringsDataLength += flattened.length;
     }
-    else {
-      var data = new Array(1000);
-      for (var i = 0; i < charstrings.length; i++)
-        data[charstrings[i].unicode] = i + 1;
-
-      var ranges = [];
-      var range = [];
-      for (var i = 0; i < data.length; i++) {
-        var char = data[i];
-        if (char) {
-          range.push(i);
-        } else if (range.length) {
-          if (0) {
-            log("create a new range of " + range.length + " chars width min: " + range[0] + " to max: " + range[range.length - 1]);
-            log("range content is: " + range);
-          }
-          ranges.push(range.slice());
-          range = [];
-        }
-      }
-
+    dump("There is " + charstringsCount + " glyphs (size: " + charstringsDataLength + ")");
 
-      var cmap = [];
-      var segCount = ranges.length + 1;
+    // Create a CFF font data
+    var cff = new Uint8Array(kMaxFontFileSize);
+    var currentOffset = 0;
 
-      var segCount2 = segCount * 2;
-      var searchRange = this.getMaxPower2(segCount) * 2;
-      var searchEntry = Math.log(segCount) / Math.log(2);
-      var rangeShift = 2 * segCount - searchRange;
-      cmap = cmap.concat(this.integerToBytes(segCount2, 2));
-      cmap = cmap.concat(this.integerToBytes(searchRange, 2));
-      cmap = cmap.concat(this.integerToBytes(searchEntry, 2));
-      cmap = cmap.concat(this.integerToBytes(rangeShift, 2));
+    // Font header (major version, minor version, header size, offset size)
+    var header = [0x01, 0x00, 0x04, 0x04];
+    currentOffset += header.length;
+    cff.set(header);
 
-      // End characters code with an additional 0xFFFF to finish the array
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        cmap = cmap.concat(this.integerToBytes(range[range.length - 1], 2));
-      };
-      cmap = cmap.concat([0xFF, 0xFF]);
+    // Names Index
+    var nameIndex = this.createCFFIndexHeader([aFont.get("FontName")]);
+    cff.set(nameIndex, currentOffset);
+    currentOffset += nameIndex.length;
 
-      // reserved pad
-      cmap = cmap.concat([0x00, 0x00]);
+    // Calculate strings before writing the TopDICT index in order
+    // to calculate correct relative offsets for storing 'charset'
+    // and 'charstrings' data
+    var fontInfo = aFont.get("FontInfo");
+    var version = fontInfo.get("version");
+    var notice = fontInfo.get("Notice");
+    var fullName = fontInfo.get("FullName");
+    var familyName = fontInfo.get("FamilyName");
+    var weight = fontInfo.get("Weight");
+    var strings = [version, notice, fullName,
+                   familyName, weight, "asteriskmath"];
+    var stringsIndex = this.createCFFIndexHeader(strings);
+    var stringsDataLength = stringsIndex.length;
 
-      // Start characters code with an additional 0xFFFF to finish the array
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        cmap = cmap.concat(this.integerToBytes(range[0], 2));
-      };
-      cmap = cmap.concat([0xFF, 0xFF]);
-
-      // Fill idDelta
-      var delta = 0;
-      var p = 0;
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        var start = range[0];
-        var delta = ((start - 1) - p) % 65536;
-
-        var value = this.integerToBytes(delta, 2);
-        value[0] ^= 0xFF;
-        value[1] ^= 0xFF;
-        value[1] += 1;
-        cmap = cmap.concat([value[0], value[1]]);
-
-        p += range.length;
-      };
-      cmap = cmap.concat([0x00, 0x01]);
-
-
-      // Fill id Offsets with 0x00
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        cmap = cmap.concat([0x00, 0x00]);
-      };
-      cmap = cmap.concat([0x00, 0x00]);
+    // Create the global subroutines index
+    var globalSubrsIndex = this.createCFFIndexHeader([]);
 
-      var cmapHeader = [
-        0x00, 0x00, // version
-        0x00, 0x01, // numTables
-        0x00, 0x03, // platformID
-        0x00, 0x01, // encodingID
-        0x00, 0x00, 0x00, 0x0C, // start of the table record
-        0x00, 0x04  // format
-      ];
-      cmapHeader = cmapHeader.concat(this.integerToBytes(cmap.length + 6, 2)); // length
-      cmapHeader = cmapHeader.concat(0x00, 0x00); // language
-
-      // Fill up data!
-      for (var i = 0; i < ranges.length; i++) {
-        var range = ranges[i];
-        for (var j = 0; j < range.length; j++)
-          cmap = cmap.concat(range[j]);
-      };
-      cmap = cmapHeader.concat(cmap);
+    // Fill the charset header (first byte is the encoding)
+    var charset = [0x00];
+    for (var i = 0; i < glyphs.length; i++) {
+      var index = CFFStrings.indexOf(charstrings[i].glyph);
+      if (index == -1)
+        index = CFFStrings.length + strings.indexOf(glyph);
+      var bytes = FontsUtils.integerToBytes(index, 2);
+      charset.push(bytes[0]);
+      charset.push(bytes[1]);
     }
 
-    var tableEntry = this.createTableEntry("cmap", virtualOffset, cmap);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += cmap.length;
-
+    // Convert charstrings
+    var getNumFor = {
+      "hstem": 1,
+      "vstem": 3,
+      "vmoveto": 4,
+      "rlineto": 5,
+      "hlineto": 6,
+      "vlineto": 7,
+      "rrcurveto": 8,
+      "endchar": 14,
+      "rmoveto": 21,
+      "hmoveto": 22,
+      "vhcurveto": 30,
+      "hvcurveto": 31,
+    };
 
-    /** HEAD */
+    // Encode the glyph and add it to the FUX
+    var r = [[0x40, 0x0E]];
+    for (var i = 0; i < glyphs.length; i++) {
+      var data = glyphs[i].slice();
+      var charstring = [];
+      for (var j = 0; j < data.length; j++) {
+        var c = data[j];
+        if (!IsNum(c)) {
+          var token = getNumFor[c];
+          if (!token)
+            error(c);
+          charstring.push(token);
+        } else {
+          try {
+            var bytes = this.encodeNumber(c);
+          } catch(e) {
+            log("Glyph " + i + " has a wrong value: " + c + " in charstring: " + data);
+            log("the default value is glyph " + charstrings[i].glyph + " and is supposed to be: " + charstrings[i].charstring);
+          }
+          for (var k = 0; k < bytes.length; k++)
+            charstring.push(bytes[k]);
+        }
+      }
+      r.push(charstring);
+    }
 
-    var head = [
-      0x00, 0x01, 0x00, 0x00, // Version number
-      0x00, 0x00, 0x50, 0x00, // fontRevision
-      0x00, 0x00, 0x00, 0x00, // checksumAdjustement
-      0x5F, 0x0F, 0x3C, 0xF5, // magicNumber
-      0x00, 0x00, // Flags
-      0x03, 0xE8, // unitsPerEM (>= 16 && <=16384)
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // created
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // modified
-      0x00, 0x00, // xMin
-      0x00, 0x00, // yMin
-      0x00, 0x00, // xMax
-      0x00, 0x00, // yMax
-      0x00, 0x00, // macStyle
-      0x00, 0x00, // lowestRecPPEM
-      0x00, 0x00, // fontDirectionHint
-      0x00, 0x00, // indexToLocFormat
-      0x00, 0x00 // glyphDataFormat
-    ];
-    var tableEntry = this.createTableEntry("head", virtualOffset, head);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += head.length;
+    var charstringsIndex = this.createCFFIndexHeader(r, true);
+    charstringsIndex = charstringsIndex.join(" ").split(" "); // XXX why?
 
 
-    /** HHEA */
+    var fontBBox = aFont.get("FontBBox");
 
-    var hhea = [
-      0x00, 0x01, 0x00, 0x00, // Version number
-      0x00, 0x00, // Typographic Ascent
-      0x00, 0x00, // Typographic Descent
-      0x00, 0x00, // Line Gap
-      0xFF, 0xFF, // advanceWidthMax
-      0x00, 0x00, // minLeftSidebearing
-      0x00, 0x00, // minRightSidebearing
-      0x00, 0x00, // xMaxExtent
-      0x00, 0x00, // caretSlopeRise
-      0x00, 0x00, // caretSlopeRun
-      0x00, 0x00, // caretOffset
-      0x00, 0x00, // -reserved-
-      0x00, 0x00, // -reserved-
-      0x00, 0x00, // -reserved-
-      0x00, 0x00, // -reserved-
-      0x00, 0x00 // metricDataFormat
+    //Top Dict Index
+    var topDictIndex = [
+      0x00, 0x01, 0x01, 0x01, 0x30,
+      248, 27, 0, // version
+      248, 28, 1, // Notice
+      248, 29, 2, // FullName
+      248, 30, 3, // FamilyName
+      248, 31, 4, // Weight
     ];
-    hhea = hhea.concat(this.integerToBytes(charstrings.length, 2)); // numberOfHMetrics
 
-    var tableEntry = this.createTableEntry("hhea", virtualOffset, hhea);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += hhea.length;
+    for (var i = 0; i < fontBBox.length; i++)
+      topDictIndex = topDictIndex.concat(this.encodeNumber(fontBBox[i]));
+    topDictIndex.push(5) // FontBBox;
 
-    /** HMTX */
+    var charsetOffset = currentOffset +
+                        (topDictIndex.length + (4 + 4 + 4 + 7)) +
+                        stringsIndex.length +
+                        globalSubrsIndex.length;
+    topDictIndex = topDictIndex.concat(this.encodeNumber(charsetOffset));
+    topDictIndex.push(15); // charset
 
-    var hmtx = [0x01, 0xF4, 0x00, 0x00];
-    for (var i = 0; i < charstrings.length; i++) {
-      var charstring = charstrings[i].charstring;
-      var width = this.integerToBytes(charstring[1], 2);
-      var lsb = this.integerToBytes(charstring[0], 2);
-      hmtx = hmtx.concat(width, lsb);
-    }
+    topDictIndex = topDictIndex.concat([28, 0, 0, 16]) // Encoding
 
-    var tableEntry = this.createTableEntry("hmtx", virtualOffset, hmtx);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += hmtx.length;
+    var charstringsOffset = charsetOffset + (charstringsCount * 2) + 1;
+    topDictIndex = topDictIndex.concat(this.encodeNumber(charstringsOffset));
+    topDictIndex.push(17); // charstrings
 
+    topDictIndex = topDictIndex.concat([28, 0, 55])
+    var privateOffset = charstringsOffset + charstringsIndex.length;
+    topDictIndex = topDictIndex.concat(this.encodeNumber(privateOffset));
+    topDictIndex.push(18); // Private
+    topDictIndex = topDictIndex.join(" ").split(" ");
 
-    /** MAXP */
+    // Top Dict Index
+    cff.set(topDictIndex, currentOffset);
+    currentOffset += topDictIndex.length;
 
-    var maxp = [
-      0x00, 0x00, 0x50, 0x00, // Version number
-    ].concat(this.integerToBytes(charstrings.length + 1, 2)); // Num of glyphs (+1 to pass the sanitizer...)
+    // Strings Index
+    cff.set(stringsIndex, currentOffset);
+    currentOffset += stringsIndex.length;
 
-    var tableEntry = this.createTableEntry("maxp", virtualOffset, maxp);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += maxp.length;
+    // Global Subrs Index
+    cff.set(globalSubrsIndex, currentOffset);
+    currentOffset += globalSubrsIndex.length;
 
+    // Charset Index
+    cff.set(charset, currentOffset);
+    currentOffset += charset.length;
 
-    /** NAME */
+    // Fill charstrings data
+    cff.set(charstringsIndex, currentOffset);
+    currentOffset += charstringsIndex.length;
 
-    var name = [
-      0x00, 0x00, // format
-      0x00, 0x00, // Number of names Record
-      0x00, 0x00 // Storage
+    // Private Data
+    var privateData = [
+      248, 136, 20,
+      248, 136, 21,
+      119, 159, 248, 97, 159, 247, 87, 159, 6,
+      30, 10, 3, 150, 37, 255, 12, 9,
+      139, 12, 10,
+      172, 10,
+      172, 150, 143, 146, 150, 146, 12, 12,
+      247, 32, 11,
+      247, 10, 161, 147, 154, 150, 143, 12, 13,
+      139, 12, 14,
+      28, 0, 55, 19
     ];
-    var tableEntry = this.createTableEntry("name", virtualOffset, name);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += name.length;
-
-
-    /** POST */
+    cff.set(privateData, currentOffset);
+    currentOffset += privateData.length;
 
-    // XXX get those info from the Font dict!
-    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
+    // Dump shit at the end of the file
+    var shit = [
+      0x00, 0x01, 0x01, 0x01,
+      0x13, 0x5D, 0x65, 0x64,
+      0x5E, 0x5B, 0xAF, 0x66,
+      0xBA, 0xBB, 0xB1, 0xB0,
+      0xB9, 0xBA, 0x65, 0xB2,
+      0x5C, 0x1F, 0x0B
     ];
-    var tableEntry = this.createTableEntry("post", virtualOffset, post);
-    otf.set(tableEntry, currentOffset);
-    currentOffset += tableEntry.length;
-    virtualOffset += post.length;
+    cff.set(shit, currentOffset);
+    currentOffset += shit.length;
 
-    // Set the CFF data
-    otf.set(aData, currentOffset);
-    currentOffset += aData.length;
 
-    var tables = [OS2, cmap, head, hhea, hmtx, maxp, name, post];
-    for (var i = 0; i < tables.length; i++) {
-      var table = tables[i];
-      otf.set(table, currentOffset);
-      currentOffset += table.length;
-    }
+    dump("==================== debug ====================");
+    //var file = new Uint8Array(cff, 0, currentOffset);
+    //var parser = new Type2Parser();
+    //parser.parse(new Stream(file));
 
     var fontData = [];
     for (var i = 0; i < currentOffset; i++)
-      fontData.push(otf[i]);
+      fontData.push(cff[i]);
+
+    //log("== write to file");
+    //writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
 
-    //writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".otf");
     return fontData;
   }
 };