diff --git a/PDFFont.js b/PDFFont.js index b8ce52e07..755bea9b5 100644 --- a/PDFFont.js +++ b/PDFFont.js @@ -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": + 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; - 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; - } + } + 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) { - log(p + "::" + font.get(p)); + for (var p in font.map) + dump(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"; diff --git a/cffStandardStrings.js b/cffStandardStrings.js index 1604b5fdd..ab71947ec 100644 --- a/cffStandardStrings.js +++ b/cffStandardStrings.js @@ -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" + } +}; +