]> git.parisson.com Git - pdf.js.git/commitdiff
Starts to decode type2 charStrings
authorVivien Nicolas <21@vingtetun.org>
Wed, 8 Jun 2011 18:37:25 +0000 (20:37 +0200)
committerVivien Nicolas <21@vingtetun.org>
Wed, 8 Jun 2011 18:37:25 +0000 (20:37 +0200)
PDFFont.js
cffStandardStrings.js

index b8ce52e071bad536ef402271e81731d3a35d315e..755bea9b505971f231fe4a9e2e648032e0af77ad 100644 (file)
@@ -660,80 +660,19 @@ var Type1Font = function(aFontName, aFontFile) {
   }
 };
 
-var hack = false;
-Type1Font.prototype = {
-  convert: function() {
-    var fontName = "TACTGM+NimbusRomNo9L-Medi";
-    var fontData = null;
-    for (var font in Fonts.map) {
-      if (font == fontName) {
-        fontData = Fonts.get(font);
-        break;
-      }
-    }
 
-    if (!fontData || hack)
-      return;
-    hack = true;
-
-    var t1Only = [
-      "callothersubr",
-      "closepath",
-      "dotsection",
-      "hsbw",
-      "hstem3",
-      "pop",
-      "sbw",
-      "seac",
-      "setcurrentpoint",
-      "vstem3"
-    ];
-
-    /*
-     * The sequence and form of a Type 2 charstring program may be
-     * represented as:
-     * w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar
-     *
-     */
-    var t2CharStrings = new Dict();
-
-    var t1CharStrings = fontData.get("CharStrings");
-    for (var key in t1CharStrings.map) {
-      var font = t1CharStrings.get(key);
-      var t2font = [];
-
-      for (var i = 0; i < font.length; i++) {
-        var token = font[i];
-        switch (token) {
-          case "hsbw":
-            var width = t2font.pop();
-            var leftSidebearingPoint = t2font.pop();
-            font.push(width);
-            break;
-          default:
-            if (t1Only.indexOf(token) != -1) {
-              log(token + " need convert!\n");
-              throw new Error("Type1 Only token");
-            }
-            t2font.push(token);
-            break;
-        }
-      }
-      log(key + "::" + t1CharStrings.get(key));
-      log("type2::" + t2font);
-    }
-  }
-};
 
-function decodeType2DictData(aString, aDictionary) {
+/**************************************************************************/
+
+function decodeType2DictData(aString, aDictionary, aHack) {
   var data = [];
 
   var value = "";
   var count = aString.length;
   for (var i = 0; i < count; i) {
     value = aString[i++];
-
-    if (value < 0) {
+    if (value <= 0) {
+      data.push(value);
       continue;
     } else if (value == 28) {
       value = aString[i++] << 8 | aString[i++];
@@ -743,11 +682,15 @@ function decodeType2DictData(aString, aDictionary) {
               aString[i++] << 8  |
               aString[i++];
     } else if (value < 32) {
+      var oldValue = value;
       if (value == 12) {
         value = aDictionary["12"][aString[i++]];
       } else {
         value = aDictionary[value];
       }
+      if (!value)
+        throw new Error("This command number does not match anything : " + oldValue);
+      value = aHack ? value.name : value;
     } else if (value <= 246) {
       value = parseInt(value) - 139;
     } else if (value <= 250) {
@@ -776,15 +719,30 @@ var Type2Parser = function(aFilePath) {
   };
 
   function readIndex(aStream, aIsByte) {
-    var count = aStream.getByte() + aStream.getByte();
+    var count = aStream.getByte() << 8 | aStream.getByte();
     var offsize = aStream.getByte();
     var offsets = [];
     for (var i = 0; i < count + 1; i++) {
-      var offset = 0;
-      for (var j = 0; j < offsize; j++) {
-        // XXX need to do some better code here
-        var byte = aStream.getByte();
-        offset += byte;
+      switch (offsize) {
+        case 0:
+          offset = 0;
+          break;
+        case 1:
+          offset = aStream.getByte();
+          break;
+        case 2:
+          offset = aStream.getByte() << 8 | aStream.getByte();
+          break;
+        case 3:
+          offset = aStream.getByte() << 16 | aStream.getByte() << 8 |
+                   aStream.getByte();
+          break;
+        case 4:
+          offset = aStream.getByte() << 24 | aStream.getByte() << 16 |
+                   aStream.getByte() << 8 | aStream.getByte();
+          break;
+        default:
+          throw new Error("Unsupported offsize: " + offsize);
       }
       offsets.push(offset);
     }
@@ -800,58 +758,73 @@ var Type2Parser = function(aFilePath) {
       var length = offsets[i + 1] - 1;
       for (var j = offset - 1; j < length; j++)
         data.push(aIsByte ? aStream.getByte() : aStream.getChar());
-      dump("object at offset " + offset + " is: " + data);
+      //dump("object at offset " + offset + " is: " + data);
       objects.push(data);
     }
     return objects;
   };
 
-  function parseAsToken(aArray) {
-    var objects = [];
+  function parseAsToken(aString, aDict) {
+    var decoded = decodeType2DictData(aString, aDict);
 
-    var count = aArray.length;
+    var stack = [];
+    var count = decoded.length;
     for (var i = 0; i < count; i++) {
-      var decoded = decodeType2DictData(aArray[i], CFFDictOps);
-
-      var stack = [];
-      var count = decoded.length;
-      for (var i = 0; i < count; i++) {
-        var token = decoded[i];
-        if (IsNum(token)) {
-          stack.push(token);
-        } else {
-          switch (token.operand) {
-            case "SID":
-              font.set(token.name, CFFStrings[stack.pop()]);
-              break;
-            case "number number":
-              font.set(token.name, {
-                size: stack.pop(),
-                offset: stack.pop()
-              });
-              break;
-            case "boolean":
-              font.set(token.name, stack.pop());
-              break;
-            case "delta":
+      var token = decoded[i];
+      if (IsNum(token)) {
+        stack.push(token);
+      } else {
+        switch (token.operand) {
+          case "SID":
+            font.set(token.name, CFFStrings[stack.pop()]);
+            break;
+          case "number number":
+            font.set(token.name, {
+              size: stack.pop(),
+              offset: stack.pop()
+            });
+            break;
+          case "boolean":
+            font.set(token.name, stack.pop());
+            break;
+          case "delta":
+            font.set(token.name, stack.pop());
+            break;
+          default:
+            if (token.operand && token.operand.length) {
+              var array = [];
+              for (var j = 0; j < token.operand.length; j++)
+                array.push(stack.pop());
+              font.set(token.name, array);
+            } else {
               font.set(token.name, stack.pop());
-              break;
-            default:
-              if (token.operand && token.operand.length) {
-                var array = [];
-                for (var j = 0; j < token.operand.length; j++)
-                  array.push(stack.pop());
-                font.set(token.name, array);
-              } else {
-                font.set(token.name, stack.pop());
-              }
-              break;
-          }
+            }
+            break;
         }
       }
     }
+  };
 
-    return objects;
+
+  function readCharset(aStream, aCharStrings) {
+    var charset = {};
+
+    var format = aStream.getByte();
+    if (format == 0) {
+      var count = aCharStrings.length - 1;
+      charset[".notdef"] = decodeType2DictData(aCharStrings[0], CFFDictCommands, true);
+      for (var i = 1; i < count + 1; i++) {
+        var sid = aStream.getByte() << 8 | aStream.getByte();
+        var charString = decodeType2DictData(aCharStrings[i], CFFDictCommands, true);
+        charset[CFFStrings[sid]] = charString;
+        log(CFFStrings[sid] + "::" + charString);
+      }
+    } else if (format == 1) {
+      throw new Error("Format 1 charset are not supported");
+    } else {
+      throw new Error("Invalid charset format");
+    }
+    return charset;
   };
 
   this.parse = function(aStream) {
@@ -881,15 +854,44 @@ var Type2Parser = function(aFilePath) {
       CFFStrings.push(strings[i].join(""));
 
     // Parse the TopDict operator
-    parseAsToken(topDict);
+    var objects = [];
+    var count = topDict.length;
+    for (var i = 0; i < count; i++)
+      parseAsToken(topDict[i], CFFDictOps);
+
+    for (var p in font.map)
+      dump(p + "::" + font.get(p));
 
-    for (var p in font.map) {
-      log(p + "::" + font.get(p));
+    // Read the Subr Index
+    dump("Reading Subr Index");
+    var subrs = readIndex(aStream);
+
+    // Read CharStrings Index
+    dump("Read CharStrings Index");
+    var charStringsOffset = font.get("CharStrings");
+    aStream.pos = charStringsOffset;
+    var charStrings = readIndex(aStream, true);
+
+
+    var charsetEntry = font.get("charset");
+    if (charsetEntry == 0) {
+      throw new Error("Need to support CFFISOAdobeCharset");
+    } else if (charsetEntry == 1) {
+      throw new Error("Need to support CFFExpert");
+    } else if (charsetEntry == 2) {
+      throw new Error("Need to support CFFExpertSubsetCharset");
+    } else {
+      aStream.pos = charsetEntry;
+      var charset = readCharset(aStream, charStrings);
     }
+
+    // Read Encoding data
+    log("Reading encoding data");
   }
 };
 
-// 
+
+// XXX
 var xhr = new XMLHttpRequest();
 xhr.open("GET", "titi.cff", false);
 xhr.mozResponseType = xhr.responseType = "arraybuffer";
index 1604b5fdd69522ab85c65c2524d99d9242ff8abe..ab71947ec81aa94b224613044201b82dfccc7747 100644 (file)
@@ -65,7 +65,7 @@ var CFFStrings = [
   "asciicircum",
   "underscore",
   "quoteleft",
-  "95 asciitilde",
+  "a",
   "b",
   "c",
   "d",
@@ -550,3 +550,150 @@ var CFFDictOps = {
     name: "nominalWidthX"
   }
 };
+
+var CFFDictCommands = {
+  "1": {
+    name: "hstem"
+  },
+  "3": {
+    name: "vstem"
+  },
+  "4": {
+    name: "vmoveto"
+  },
+  "5": {
+    name: "rlineto"
+  },
+  "6": {
+    name: "hlineto"
+  },
+  "7": {
+    name: "vlineto"
+  },
+  "8": {
+    name: "rrcurveto"
+  },
+  "10": {
+    name: "callsubr"
+  },
+  "11": {
+    name: "return"
+  },
+  "12": {
+    "3": {
+      name: "and"
+    },
+    "4": {
+      name: "or"
+    },
+    "5": {
+      name: "not"
+    },
+    "9": {
+      name: "abs"
+    },
+    "10": {
+      name: "add"
+    },
+    "11": {
+      name: "div"
+    },
+    "12": {
+      name: "sub"
+    },
+    "14": {
+      name: "neg"
+    },
+    "15": {
+      name: "eq"
+    },
+    "18": {
+      name: "drop"
+    },
+    "20": {
+      name: "put"
+    },
+    "21": {
+      name: "get"
+    },
+    "22": {
+      name: "ifelse"
+    },
+    "23": {
+      name: "random"
+    },
+    "24": {
+      name: "mul"
+    },
+    "26": {
+      name: "sqrt"
+    },
+    "27": {
+      name: "dup"
+    },
+    "28": {
+      name: "exch"
+    },
+    "29": {
+      name: "index"
+    },
+    "30": {
+      name: "roll"
+    },
+    "34": {
+      name: "hflex"
+    },
+    "35": {
+      name: "flex"
+    },
+    "36": {
+      name: "hflex1"
+    },
+    "37": {
+      name: "flex1"
+    }
+  },
+  "14": {
+    name: "endchar"
+  },
+  "18": {
+    name: "hstemhm"
+  },
+  "19": {
+    name: "hintmask"
+  },
+  "20": {
+    name: "cntrmask"
+  },
+  "21": {
+    name: "rmoveto"
+  },
+  "22": {
+    name: "hmoveto"
+  },
+  "23": {
+    name: "vstemhm"
+  },
+  "24": {
+    name: "rcurveline"
+  },
+  "25": {
+    name: "rlivecurve"
+  },
+  "26": {
+    name: "vvcurveto"
+  },
+  "27": {
+    name: "hhcurveto"
+  },
+  "29": {
+    name: "callgsubr"
+  },
+  "30": {
+    name: "vhcurveto"
+  },
+  "31": {
+    name: "hvcurveto"
+  }
+};
+