+++ /dev/null
-var CFFStrings = [
- ".notdef",
- "space",
- "exclam",
- "quotedbl",
- "numbersign",
- "dollar",
- "percent",
- "ampersand",
- "quoteright",
- "parenleft",
- "parenright",
- "asterisk",
- "plus",
- "comma",
- "hyphen",
- "period",
- "slash",
- "zero",
- "one",
- "two",
- "three",
- "four",
- "five",
- "six",
- "seven",
- "eight",
- "nine",
- "colon",
- "semicolon",
- "less",
- "equal",
- "greater",
- "question",
- "at",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "bracketleft",
- "backslash",
- "bracketright",
- "asciicircum",
- "underscore",
- "quoteleft",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
- "braceleft",
- "bar",
- "braceright",
- "asciitilde",
- "exclamdown",
- "cent",
- "sterling",
- "fraction",
- "yen",
- "florin",
- "section",
- "currency",
- "quotesingle",
- "quotedblleft",
- "guillemotleft",
- "guilsinglleft",
- "guilsinglright",
- "fi",
- "fl",
- "endash",
- "dagger",
- "daggerdbl",
- "periodcentered",
- "paragraph",
- "bullet",
- "quotesinglbase",
- "quotedblbase",
- "quotedblright",
- "guillemotright",
- "ellipsis",
- "perthousand",
- "questiondown",
- "grave",
- "acute",
- "circumflex",
- "tilde",
- "macron",
- "breve",
- "dotaccent",
- "dieresis",
- "ring",
- "cedilla",
- "hungarumlaut",
- "ogonek",
- "caron",
- "emdash",
- "AE",
- "ordfeminine",
- "Lslash",
- "Oslash",
- "OE",
- "ordmasculine",
- "ae",
- "dotlessi",
- "lslash",
- "oslash",
- "oe",
- "germandbls",
- "onesuperior",
- "logicalnot",
- "mu",
- "trademark",
- "Eth",
- "onehalf",
- "plusminus",
- "Thorn",
- "onequarter",
- "divide",
- "brokenbar",
- "degree",
- "thorn",
- "threequarters",
- "twosuperior",
- "registered",
- "minus",
- "eth",
- "multiply",
- "threesuperior",
- "copyright",
- "Aacute",
- "Acircumflex",
- "Adieresis",
- "Agrave",
- "Aring",
- "Atilde",
- "Ccedilla",
- "Eacute",
- "Ecircumflex",
- "Edieresis",
- "Egrave",
- "Iacute",
- "Icircumflex",
- "Idieresis",
- "Igrave",
- "Ntilde",
- "Oacute",
- "Ocircumflex",
- "Odieresis",
- "Ograve",
- "Otilde",
- "Scaron",
- "Uacute",
- "Ucircumflex",
- "Udieresis",
- "Ugrave",
- "Yacute",
- "Ydieresis",
- "Zcaron",
- "aacute",
- "acircumflex",
- "adieresis",
- "agrave",
- "aring",
- "atilde",
- "ccedilla",
- "eacute",
- "ecircumflex",
- "edieresis",
- "egrave",
- "iacute",
- "icircumflex",
- "idieresis",
- "igrave",
- "ntilde",
- "oacute",
- "ocircumflex",
- "odieresis",
- "ograve",
- "otilde",
- "scaron",
- "uacute",
- "ucircumflex",
- "udieresis",
- "ugrave",
- "yacute",
- "ydieresis",
- "zcaron",
- "exclamsmall",
- "Hungarumlautsmall",
- "dollaroldstyle",
- "dollarsuperior",
- "ampersandsmall",
- "Acutesmall",
- "parenleftsuperior",
- "parenrightsuperior",
- "266 ff",
- "onedotenleader",
- "zerooldstyle",
- "oneoldstyle",
- "twooldstyle",
- "threeoldstyle",
- "fouroldstyle",
- "fiveoldstyle",
- "sixoldstyle",
- "sevenoldstyle",
- "eightoldstyle",
- "nineoldstyle",
- "commasuperior",
- "threequartersemdash",
- "periodsuperior",
- "questionsmall",
- "asuperior",
- "bsuperior",
- "centsuperior",
- "dsuperior",
- "esuperior",
- "isuperior",
- "lsuperior",
- "msuperior",
- "nsuperior",
- "osuperior",
- "rsuperior",
- "ssuperior",
- "tsuperior",
- "ff",
- "ffi",
- "ffl",
- "parenleftinferior",
- "parenrightinferior",
- "Circumflexsmall",
- "hyphensuperior",
- "Gravesmall",
- "Asmall",
- "Bsmall",
- "Csmall",
- "Dsmall",
- "Esmall",
- "Fsmall",
- "Gsmall",
- "Hsmall",
- "Ismall",
- "Jsmall",
- "Ksmall",
- "Lsmall",
- "Msmall",
- "Nsmall",
- "Osmall",
- "Psmall",
- "Qsmall",
- "Rsmall",
- "Ssmall",
- "Tsmall",
- "Usmall",
- "Vsmall",
- "Wsmall",
- "Xsmall",
- "Ysmall",
- "Zsmall",
- "colonmonetary",
- "onefitted",
- "rupiah",
- "Tildesmall",
- "exclamdownsmall",
- "centoldstyle",
- "Lslashsmall",
- "Scaronsmall",
- "Zcaronsmall",
- "Dieresissmall",
- "Brevesmall",
- "Caronsmall",
- "Dotaccentsmall",
- "Macronsmall",
- "figuredash",
- "hypheninferior",
- "Ogoneksmall",
- "Ringsmall",
- "Cedillasmall",
- "questiondownsmall",
- "oneeighth",
- "threeeighths",
- "fiveeighths",
- "seveneighths",
- "onethird",
- "twothirds",
- "zerosuperior",
- "foursuperior",
- "fivesuperior",
- "sixsuperior",
- "sevensuperior",
- "eightsuperior",
- "ninesuperior",
- "zeroinferior",
- "oneinferior",
- "twoinferior",
- "threeinferior",
- "fourinferior",
- "fiveinferior",
- "sixinferior",
- "seveninferior",
- "eightinferior",
- "nineinferior",
- "centinferior",
- "dollarinferior",
- "periodinferior",
- "commainferior",
- "Agravesmall",
- "Aacutesmall",
- "Acircumflexsmall",
- "Atildesmall",
- "Adieresissmall",
- "Aringsmall",
- "AEsmall",
- "Ccedillasmall",
- "Egravesmall",
- "Eacutesmall",
- "Ecircumflexsmall",
- "Edieresissmall",
- "Igravesmall",
- "Iacutesmall",
- "Icircumflexsmall",
- "Idieresissmall",
- "Ethsmall",
- "Ntildesmall",
- "Ogravesmall",
- "Oacutesmall",
- "Ocircumflexsmall",
- "Otildesmall",
- "Odieresissmall",
- "OEsmall",
- "Oslashsmall",
- "Ugravesmall",
- "Uacutesmall",
- "Ucircumflexsmall",
- "Udieresissmall",
- "Yacutesmall",
- "Thornsmall",
- "Ydieresissmall",
- "001.000",
- "001.001",
- "001.002",
- "001.003",
- "Black",
- "Bold",
- "Book",
- "Light",
- "Medium",
- "Regular",
- "Roman",
- "Semibold"
-];
-
-var CFFEncodingMap = {
- "0": "-reserved-",
- "1": "hstem",
- "2": "-reserved-",
- "3": "vstem",
- "4": "vmoveto",
- "5": "rlineto",
- "6": "hlineto",
- "7": "vlineto",
- "8": "rrcurveto",
- "9": "-reserved-",
- "10": "callsubr",
- "11": "return",
- "12": {
- "3": "and",
- "4": "or",
- "5": "not",
- "9": "abs",
- "10": "add",
- "11": "div",
- "12": "sub",
- "14": "neg",
- "15": "eq",
- "18": "drop",
- "20": "put",
- "21": "get",
- "22": "ifelse",
- "23": "random",
- "24": "mul",
- "26": "sqrt",
- "27": "dup",
- "28": "exch",
- "29": "index",
- "30": "roll",
- "34": "hflex",
- "35": "flex",
- "36": "hflex1",
- "37": "flex1"
- },
- "13": "-reserved-",
- "14": "endchar",
- "15": "-reserved-",
- "16": "-reserved-",
- "17": "-reserved-",
- "18": "hstemhm",
- "19": "hintmask",
- "20": "cntrmask",
- "21": "rmoveto",
- "22": "hmoveto",
- "23": "vstemhm",
- "24": "rcurveline",
- "25": "rlivecurve",
- "26": "vvcurveto",
- "27": "hhcurveto",
- "29": "callgsubr",
- "30": "vhcurveto",
- "31": "hvcurveto"
-};
-
-var CFFDictDataMap = {
- "0": {
- name: "version",
- operand: "SID"
- },
- "1": {
- name: "Notice",
- operand: "SID"
- },
- "2": {
- name: "FullName",
- operand: "SID"
- },
- "3": {
- name: "FamilyName",
- operand: "SID"
- },
- "4": {
- name: "Weight",
- operand: "SID"
- },
- "5": {
- name: "FontBBox",
- operand: [0, 0, 0, 0]
- },
- "6": {
- name: "BlueValues"
- },
- "7": {
- name: "OtherBlues"
- },
- "8": {
- name: "FamilyBlues"
- },
- "9": {
- name: "FamilyOtherBlues"
- },
- "10": {
- name: "StdHW"
- },
- "11": {
- name: "StdVW"
- },
- "12": {
- "0": {
- name: "Copyright",
- operand: "SID"
- },
- "1": {
- name: "IsFixedPitch",
- operand: false
- },
- "2": {
- name: "ItalicAngle",
- operand: 0
- },
- "3": {
- name: "UnderlinePosition",
- operand: -100
- },
- "4": {
- name: "UnderlineThickness",
- operand: 50
- },
- "5": {
- name: "PaintType",
- operand: 0
- },
- "6": {
- name: "CharstringType",
- operand: 2
- },
- "7": {
- name: "FontMatrix",
- operand: [0.001, 0, 0, 0.001, 0 ,0]
- },
- "8": {
- name: "StrokeWidth",
- operand: 0
- },
- "9": {
- name: "BlueScale"
- },
- "10": {
- name: "BlueShift"
- },
- "11": {
- name: "BlueFuzz"
- },
- "12": {
- name: "StemSnapH"
- },
- "13": {
- name: "StemSnapV"
- },
- "14": {
- name: "ForceBold"
- },
- "17": {
- name: "LanguageGroup"
- },
- "18": {
- name: "ExpansionFactor"
- },
- "9": {
- name: "initialRandomSeed"
- },
- "20": {
- name: "SyntheticBase",
- operand: null
- },
- "21": {
- name: "PostScript",
- operand: "SID"
- },
- "22": {
- name: "BaseFontName",
- operand: "SID"
- },
- "23": {
- name: "BaseFontBlend",
- operand: "delta"
- }
- },
- "13": {
- name: "UniqueID",
- operand: null
- },
- "14": {
- name: "XUID",
- operand: []
- },
- "15": {
- name: "charset",
- operand: 0
- },
- "16": {
- name: "Encoding",
- operand: 0
- },
- "17": {
- name: "CharStrings",
- operand: null
- },
- "18": {
- name: "Private",
- operand: "number number"
- },
- "19": {
- name: "Subrs"
- },
- "20": {
- name: "defaultWidthX"
- },
- "21": {
- name: "nominalWidthX"
- }
-};
-
-var CFFDictPrivateDataMap = {
- "6": {
- name: "BluesValues",
- operand: "delta"
- },
- "7": {
- name: "OtherBlues",
- operand: "delta"
- },
- "8": {
- name: "FamilyBlues",
- operand: "delta"
- },
- "9": {
- name: "FamilyOtherBlues",
- operand: "delta"
- },
- "10": {
- name: "StdHW",
- operand: null
- },
- "11": {
- name: "StdVW",
- operand: null
- },
- "12": {
- "9": {
- name: "BlueScale",
- operand: 0.039625
- },
- "10": {
- name: "BlueShift",
- operand: 7
- },
- "11": {
- name: "BlueFuzz",
- operand: 1
- },
- "12": {
- name: "StemSnapH",
- operand: "delta"
- },
- "13": {
- name: "StemSnapV",
- operand: "delta"
- },
- "14": {
- name: "ForceBold",
- operand: "boolean"
- },
- "17": {
- name: "LanguageGroup",
- operand: 0
- },
- "18": {
- name: "ExpansionFactor",
- operand: 0.06
- },
- "19": {
- name: "initialRandomSeed",
- operand: 0
- }
- },
- "19": {
- name: "Subrs",
- operand: null
- },
- "20": {
- name: "defaultWidthX",
- operand: 0
- },
- "21": {
- name: "nominalWidthX",
- operand: 0
- }
-};
-
}
};
+const CFFStrings = [
+ ".notdef","space","exclam","quotedbl","numbersign","dollar","percent","ampersand",
+ "quoteright","parenleft","parenright","asterisk","plus","comma","hyphen","period",
+ "slash","zero","one","two","three","four","five","six","seven","eight","nine",
+ "colon","semicolon","less","equal","greater","question","at","A","B","C","D","E",
+ "F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y",
+ "Z","bracketleft","backslash","bracketright","asciicircum","underscore",
+ "quoteleft","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q",
+ "r","s","t","u","v","w","x","y","z","braceleft","bar","braceright","asciitilde",
+ "exclamdown","cent","sterling","fraction","yen","florin","section","currency",
+ "quotesingle","quotedblleft","guillemotleft","guilsinglleft","guilsinglright",
+ "fi","fl","endash","dagger","daggerdbl","periodcentered","paragraph","bullet",
+ "quotesinglbase","quotedblbase","quotedblright","guillemotright","ellipsis",
+ "perthousand","questiondown","grave","acute","circumflex","tilde","macron",
+ "breve","dotaccent","dieresis","ring","cedilla","hungarumlaut","ogonek","caron",
+ "emdash","AE","ordfeminine","Lslash","Oslash","OE","ordmasculine","ae","dotlessi",
+ "lslash","oslash","oe","germandbls","onesuperior","logicalnot","mu","trademark",
+ "Eth","onehalf","plusminus","Thorn","onequarter","divide","brokenbar","degree",
+ "thorn","threequarters","twosuperior","registered","minus","eth","multiply",
+ "threesuperior","copyright","Aacute","Acircumflex","Adieresis","Agrave","Aring",
+ "Atilde","Ccedilla","Eacute","Ecircumflex","Edieresis","Egrave","Iacute",
+ "Icircumflex","Idieresis","Igrave","Ntilde","Oacute","Ocircumflex","Odieresis",
+ "Ograve","Otilde","Scaron","Uacute","Ucircumflex","Udieresis","Ugrave","Yacute",
+ "Ydieresis","Zcaron","aacute","acircumflex","adieresis","agrave","aring","atilde",
+ "ccedilla","eacute","ecircumflex","edieresis","egrave","iacute","icircumflex",
+ "idieresis","igrave","ntilde","oacute","ocircumflex","odieresis","ograve",
+ "otilde","scaron","uacute","ucircumflex","udieresis","ugrave","yacute",
+ "ydieresis","zcaron","exclamsmall","Hungarumlautsmall","dollaroldstyle",
+ "dollarsuperior","ampersandsmall","Acutesmall","parenleftsuperior",
+ "parenrightsuperior","266 ff","onedotenleader","zerooldstyle","oneoldstyle",
+ "twooldstyle","threeoldstyle","fouroldstyle","fiveoldstyle","sixoldstyle",
+ "sevenoldstyle","eightoldstyle","nineoldstyle","commasuperior",
+ "threequartersemdash","periodsuperior","questionsmall","asuperior","bsuperior",
+ "centsuperior","dsuperior","esuperior","isuperior","lsuperior","msuperior",
+ "nsuperior","osuperior","rsuperior","ssuperior","tsuperior","ff","ffi","ffl",
+ "parenleftinferior","parenrightinferior","Circumflexsmall","hyphensuperior",
+ "Gravesmall","Asmall","Bsmall","Csmall","Dsmall","Esmall","Fsmall","Gsmall",
+ "Hsmall","Ismall","Jsmall","Ksmall","Lsmall","Msmall","Nsmall","Osmall","Psmall",
+ "Qsmall","Rsmall","Ssmall","Tsmall","Usmall","Vsmall","Wsmall","Xsmall","Ysmall",
+ "Zsmall","colonmonetary","onefitted","rupiah","Tildesmall","exclamdownsmall",
+ "centoldstyle","Lslashsmall","Scaronsmall","Zcaronsmall","Dieresissmall",
+ "Brevesmall","Caronsmall","Dotaccentsmall","Macronsmall","figuredash",
+ "hypheninferior","Ogoneksmall","Ringsmall","Cedillasmall","questiondownsmall",
+ "oneeighth","threeeighths","fiveeighths","seveneighths","onethird","twothirds",
+ "zerosuperior","foursuperior","fivesuperior","sixsuperior","sevensuperior",
+ "eightsuperior","ninesuperior","zeroinferior","oneinferior","twoinferior",
+ "threeinferior","fourinferior","fiveinferior","sixinferior","seveninferior",
+ "eightinferior","nineinferior","centinferior","dollarinferior","periodinferior",
+ "commainferior","Agravesmall","Aacutesmall","Acircumflexsmall","Atildesmall",
+ "Adieresissmall","Aringsmall","AEsmall","Ccedillasmall","Egravesmall",
+ "Eacutesmall","Ecircumflexsmall","Edieresissmall","Igravesmall","Iacutesmall",
+ "Icircumflexsmall","Idieresissmall","Ethsmall","Ntildesmall","Ogravesmall",
+ "Oacutesmall","Ocircumflexsmall","Otildesmall","Odieresissmall","OEsmall",
+ "Oslashsmall","Ugravesmall","Uacutesmall","Ucircumflexsmall","Udieresissmall",
+ "Yacutesmall","Thornsmall","Ydieresissmall","001.000","001.001","001.002",
+ "001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold"
+];
+
/**
* Take a Type1 file as input and wrap it into a Compact Font Format (CFF)
* wrapping Type2 charstrings.
+++ /dev/null
-/**
- * The Type2 reader code below is only used for debugging purpose since Type2
- * is only a CharString format and is never used directly as a Font file.
- *
- * So the code here is useful for dumping the data content of a .cff file in
- * order to investigate the similarity between a Type1 CharString and a Type2
- * CharString or to understand the structure of the CFF format.
- */
-
-
-/**
- * Build a charset by assigning the glyph name and the human readable form
- * of the glyph data.
- */
-function readCharset(aStream, aCharstrings) {
- var charset = {};
-
- var format = aStream.getByte();
- if (format == 0) {
- charset[".notdef"] = readCharstringEncoding(aCharstrings[0]);
-
- var count = aCharstrings.length - 1;
- for (var i = 1; i < count + 1; i++) {
- var sid = aStream.getByte() << 8 | aStream.getByte();
- charset[CFFStrings[sid]] = readCharstringEncoding(aCharstrings[i]);
- //log(CFFStrings[sid] + "::" + charset[CFFStrings[sid]]);
- }
- } else if (format == 1) {
- error("Charset Range are not supported");
- } else {
- error("Invalid charset format");
- }
-
- return charset;
-};
-
-/**
- * Take a Type2 binary charstring as input and transform it to a human
- * readable representation as specified by the 'The Type 2 Charstring Format',
- * chapter 3.1.
- */
-function readCharstringEncoding(aString) {
- var charstringTokens = [];
-
- var count = aString.length;
- for (var i = 0; i < count; ) {
- var value = aString[i++];
- var token = null;
-
- if (value < 0) {
- continue;
- } else if (value <= 11) {
- token = CFFEncodingMap[value];
- } else if (value == 12) {
- token = CFFEncodingMap[value][aString[i++]];
- } else if (value <= 18) {
- token = CFFEncodingMap[value];
- } else if (value <= 20) {
- var mask = aString[i++];
- token = CFFEncodingMap[value];
- } else if (value <= 27) {
- token = CFFEncodingMap[value];
- } else if (value == 28) {
- token = aString[i++] << 8 | aString[i++];
- } else if (value <= 31) {
- token = CFFEncodingMap[value];
- } else if (value < 247) {
- token = parseInt(value) - 139;
- } else if (value < 251) {
- token = ((value - 247) * 256) + aString[i++] + 108;
- } else if (value < 255) {
- token = -((value - 251) * 256) - aString[i++] - 108;
- } else {// value == 255
- token = aString[i++] << 24 | aString[i++] << 16 |
- aString[i++] << 8 | aString[i];
- }
-
- charstringTokens.push(token);
- }
-
- return charstringTokens;
-};
-
-
-/**
- * Take a binary DICT Data as input and transform it into a human readable
- * form as specified by 'The Compact Font Format Specification', chapter 5.
- */
-function readFontDictData(aString, aMap) {
- var fontDictDataTokens = [];
-
- var count = aString.length;
- for (var i = 0; i < count; i) {
- var value = aString[i++];
- var token = null;
-
- if (value == 12) {
- token = aMap[value][aString[i++]];
- } else if (value == 28) {
- token = aString[i++] << 8 | aString[i++];
- } else if (value == 29) {
- token = aString[i++] << 24 |
- aString[i++] << 16 |
- aString[i++] << 8 |
- aString[i++];
- } else if (value == 30) {
- token = "";
- var parsed = false;
- while (!parsed) {
- var byte = aString[i++];
-
- var nibbles = [parseInt(byte / 16), parseInt(byte % 16)];
- for (var j = 0; j < nibbles.length; j++) {
- var nibble = nibbles[j];
- switch (nibble) {
- case 0xA:
- token += ".";
- break;
- case 0xB:
- token += "E";
- break;
- case 0xC:
- token += "E-";
- break;
- case 0xD:
- break;
- case 0xE:
- token += "-";
- break;
- case 0xF:
- parsed = true;
- break;
- default:
- token += nibble;
- break;
- }
- }
- };
- token = parseFloat(token);
- } else if (value <= 31) {
- token = aMap[value];
- } else if (value <= 246) {
- token = parseInt(value) - 139;
- } else if (value <= 250) {
- token = ((value - 247) * 256) + aString[i++] + 108;
- } else if (value <= 254) {
- token = -((value - 251) * 256) - aString[i++] - 108;
- } else if (value == 255) {
- error("255 is not a valid DICT command");
- }
-
- fontDictDataTokens.push(token);
- }
-
- return fontDictDataTokens;
-};
-
-
-/**
- * Take a stream as input and return an array of objects.
- * In CFF an INDEX is a structure with the following format:
- * {
- * count: 2 bytes (Number of objects stored in INDEX),
- * offsize: 1 byte (Offset array element size),
- * offset: [count + 1] bytes (Offsets array),
- * data: - (Objects data)
- * }
- *
- * More explanation are given in the 'CFF Font Format Specification',
- * chapter 5.
- */
-function readFontIndexData(aStream, aIsByte) {
- var count = aStream.getByte() << 8 | aStream.getByte();
- var offsize = aStream.getByte();
-
- function getNextOffset() {
- switch (offsize) {
- case 0:
- return 0;
- case 1:
- return aStream.getByte();
- case 2:
- return aStream.getByte() << 8 | aStream.getByte();
- case 3:
- return aStream.getByte() << 16 | aStream.getByte() << 8 |
- aStream.getByte();
- case 4:
- return aStream.getByte() << 24 | aStream.getByte() << 16 |
- aStream.getByte() << 8 | aStream.getByte();
- }
- };
-
- var offsets = [];
- for (var i = 0; i < count + 1; i++)
- offsets.push(getNextOffset());
-
- log("Found " + count + " objects at offsets :" + offsets + " (offsize: " + offsize + ")");
-
- // Now extract the objects
- var relativeOffset = aStream.pos;
- var objects = [];
- for (var i = 0; i < count; i++) {
- var offset = offsets[i];
- aStream.pos = relativeOffset + offset - 1;
-
- var data = [];
- var length = offsets[i + 1] - 1;
- for (var j = offset - 1; j < length; j++)
- data.push(aIsByte ? aStream.getByte() : aStream.getChar());
- objects.push(data);
- }
-
- return objects;
-};
-
-var Type2Parser = function(aFilePath) {
- var font = new Dict();
-
- var xhr = new XMLHttpRequest();
- xhr.open("GET", aFilePath, false);
- xhr.mozResponseType = xhr.responseType = "arraybuffer";
- xhr.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200;
- xhr.send(null);
- this.data = new Stream(xhr.mozResponseArrayBuffer || xhr.mozResponse ||
- xhr.responseArrayBuffer || xhr.response);
-
- // Turn on this flag for additional debugging logs
- var debug = false;
-
- function dump(aStr) {
- if (debug)
- log(aStr);
- };
-
- function parseAsToken(aString, aMap) {
- var decoded = readFontDictData(aString, aMap);
-
- 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, {
- offset: stack.pop(),
- size: 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;
- }
- }
- }
- };
-
- this.parse = function(aStream) {
- font.set("major", aStream.getByte());
- font.set("minor", aStream.getByte());
- font.set("hdrSize", aStream.getByte());
- font.set("offsize", aStream.getByte());
-
- // Move the cursor after the header
- aStream.skip(font.get("hdrSize") - aStream.pos);
-
- // Read the NAME Index
- dump("Reading Index: Names");
- font.set("Names", readFontIndexData(aStream));
- log("Names: " + font.get("Names"));
-
- // Read the Top Dict Index
- dump("Reading Index: TopDict");
- var topDict = readFontIndexData(aStream, true);
- log("TopDict: " + topDict);
-
- // Read the String Index
- dump("Reading Index: Strings");
- var strings = readFontIndexData(aStream);
- log("strings: " + strings);
-
- // Fill up the Strings dictionary with the new unique strings
- for (var i = 0; i < strings.length; i++)
- CFFStrings.push(strings[i].join(""));
-
- // Parse the TopDict operator
- var objects = [];
- var count = topDict.length;
- for (var i = 0; i < count; i++)
- parseAsToken(topDict[i], CFFDictDataMap);
-
- // Read the Global Subr Index that comes just after the Strings Index
- // (cf. "The Compact Font Format Specification" Chapter 16)
- dump("Reading Global Subr Index");
- var subrs = readFontIndexData(aStream, true);
- dump(subrs);
-
- // Reading Private Dict
- var private = font.get("Private");
- log("Reading Private Dict (offset: " + private.offset + " size: " + private.size + ")");
- aStream.pos = private.offset;
-
- var privateDict = [];
- for (var i = 0; i < private.size; i++)
- privateDict.push(aStream.getByte());
- dump("private:" + privateDict);
- parseAsToken(privateDict, CFFDictPrivateDataMap);
-
- for (var p in font.map)
- dump(p + "::" + font.get(p));
-
- // Read CharStrings Index
- var charStringsOffset = font.get("CharStrings");
- dump("Read CharStrings Index (offset: " + charStringsOffset + ")");
- aStream.pos = charStringsOffset;
- var charStrings = readFontIndexData(aStream, true);
-
- // Read Charset
- dump("Read Charset for " + charStrings.length + " glyphs");
- var charsetEntry = font.get("charset");
- if (charsetEntry == 0) {
- error("Need to support CFFISOAdobeCharset");
- } else if (charsetEntry == 1) {
- error("Need to support CFFExpert");
- } else if (charsetEntry == 2) {
- error("Need to support CFFExpertSubsetCharset");
- } else {
- aStream.pos = charsetEntry;
- var charset = readCharset(aStream, charStrings);
- }
- }
-};
-
-/*
- * To try the Type2 decoder on a local file in the current directory:
- *
- * var cff = new Type2Parser("file.cff");
- * cff.parse(this.data);
- *
- * To try the Type2 decoder on a custom built CFF array:
- *
- * var file = new Uint8Array(cffFileArray, 0, cffFileSize);
- * var parser = new Type2Parser();
- * parser.parse(new Stream(file));
- *
- */
-
-
-/**
- * Write to a file to the disk (works only on Firefox in privilege mode)
- * but this is useful for dumping a font file to the disk and check with
- * fontforge or the ots program what's wrong with the file.
- *
- * writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
- */
-function writeToFile(aBytes, aFilePath) {
- netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
- var Cc = Components.classes,
- Ci = Components.interfaces;
- var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
- file.initWithPath(aFilePath);
-
- var stream = Cc["@mozilla.org/network/file-output-stream;1"]
- .createInstance(Ci.nsIFileOutputStream);
- stream.init(file, 0x04 | 0x08 | 0x20, 0600, 0);
-
- var bos = Cc["@mozilla.org/binaryoutputstream;1"]
- .createInstance(Ci.nsIBinaryOutputStream);
- bos.setOutputStream(stream);
- bos.writeByteArray(aBytes, aBytes.length);
- stream.close();
-};
-
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript" src="pdf.js"></script>
<script type="text/javascript" src="fonts.js"></script>
- <script type="text/javascript" src="cffStandardStrings.js"></script>
<script type="text/javascript" src="glyphlist.js"></script>
</head>
--- /dev/null
+var CFFStrings = [
+ ".notdef",
+ "space",
+ "exclam",
+ "quotedbl",
+ "numbersign",
+ "dollar",
+ "percent",
+ "ampersand",
+ "quoteright",
+ "parenleft",
+ "parenright",
+ "asterisk",
+ "plus",
+ "comma",
+ "hyphen",
+ "period",
+ "slash",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "four",
+ "five",
+ "six",
+ "seven",
+ "eight",
+ "nine",
+ "colon",
+ "semicolon",
+ "less",
+ "equal",
+ "greater",
+ "question",
+ "at",
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "bracketleft",
+ "backslash",
+ "bracketright",
+ "asciicircum",
+ "underscore",
+ "quoteleft",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "braceleft",
+ "bar",
+ "braceright",
+ "asciitilde",
+ "exclamdown",
+ "cent",
+ "sterling",
+ "fraction",
+ "yen",
+ "florin",
+ "section",
+ "currency",
+ "quotesingle",
+ "quotedblleft",
+ "guillemotleft",
+ "guilsinglleft",
+ "guilsinglright",
+ "fi",
+ "fl",
+ "endash",
+ "dagger",
+ "daggerdbl",
+ "periodcentered",
+ "paragraph",
+ "bullet",
+ "quotesinglbase",
+ "quotedblbase",
+ "quotedblright",
+ "guillemotright",
+ "ellipsis",
+ "perthousand",
+ "questiondown",
+ "grave",
+ "acute",
+ "circumflex",
+ "tilde",
+ "macron",
+ "breve",
+ "dotaccent",
+ "dieresis",
+ "ring",
+ "cedilla",
+ "hungarumlaut",
+ "ogonek",
+ "caron",
+ "emdash",
+ "AE",
+ "ordfeminine",
+ "Lslash",
+ "Oslash",
+ "OE",
+ "ordmasculine",
+ "ae",
+ "dotlessi",
+ "lslash",
+ "oslash",
+ "oe",
+ "germandbls",
+ "onesuperior",
+ "logicalnot",
+ "mu",
+ "trademark",
+ "Eth",
+ "onehalf",
+ "plusminus",
+ "Thorn",
+ "onequarter",
+ "divide",
+ "brokenbar",
+ "degree",
+ "thorn",
+ "threequarters",
+ "twosuperior",
+ "registered",
+ "minus",
+ "eth",
+ "multiply",
+ "threesuperior",
+ "copyright",
+ "Aacute",
+ "Acircumflex",
+ "Adieresis",
+ "Agrave",
+ "Aring",
+ "Atilde",
+ "Ccedilla",
+ "Eacute",
+ "Ecircumflex",
+ "Edieresis",
+ "Egrave",
+ "Iacute",
+ "Icircumflex",
+ "Idieresis",
+ "Igrave",
+ "Ntilde",
+ "Oacute",
+ "Ocircumflex",
+ "Odieresis",
+ "Ograve",
+ "Otilde",
+ "Scaron",
+ "Uacute",
+ "Ucircumflex",
+ "Udieresis",
+ "Ugrave",
+ "Yacute",
+ "Ydieresis",
+ "Zcaron",
+ "aacute",
+ "acircumflex",
+ "adieresis",
+ "agrave",
+ "aring",
+ "atilde",
+ "ccedilla",
+ "eacute",
+ "ecircumflex",
+ "edieresis",
+ "egrave",
+ "iacute",
+ "icircumflex",
+ "idieresis",
+ "igrave",
+ "ntilde",
+ "oacute",
+ "ocircumflex",
+ "odieresis",
+ "ograve",
+ "otilde",
+ "scaron",
+ "uacute",
+ "ucircumflex",
+ "udieresis",
+ "ugrave",
+ "yacute",
+ "ydieresis",
+ "zcaron",
+ "exclamsmall",
+ "Hungarumlautsmall",
+ "dollaroldstyle",
+ "dollarsuperior",
+ "ampersandsmall",
+ "Acutesmall",
+ "parenleftsuperior",
+ "parenrightsuperior",
+ "266 ff",
+ "onedotenleader",
+ "zerooldstyle",
+ "oneoldstyle",
+ "twooldstyle",
+ "threeoldstyle",
+ "fouroldstyle",
+ "fiveoldstyle",
+ "sixoldstyle",
+ "sevenoldstyle",
+ "eightoldstyle",
+ "nineoldstyle",
+ "commasuperior",
+ "threequartersemdash",
+ "periodsuperior",
+ "questionsmall",
+ "asuperior",
+ "bsuperior",
+ "centsuperior",
+ "dsuperior",
+ "esuperior",
+ "isuperior",
+ "lsuperior",
+ "msuperior",
+ "nsuperior",
+ "osuperior",
+ "rsuperior",
+ "ssuperior",
+ "tsuperior",
+ "ff",
+ "ffi",
+ "ffl",
+ "parenleftinferior",
+ "parenrightinferior",
+ "Circumflexsmall",
+ "hyphensuperior",
+ "Gravesmall",
+ "Asmall",
+ "Bsmall",
+ "Csmall",
+ "Dsmall",
+ "Esmall",
+ "Fsmall",
+ "Gsmall",
+ "Hsmall",
+ "Ismall",
+ "Jsmall",
+ "Ksmall",
+ "Lsmall",
+ "Msmall",
+ "Nsmall",
+ "Osmall",
+ "Psmall",
+ "Qsmall",
+ "Rsmall",
+ "Ssmall",
+ "Tsmall",
+ "Usmall",
+ "Vsmall",
+ "Wsmall",
+ "Xsmall",
+ "Ysmall",
+ "Zsmall",
+ "colonmonetary",
+ "onefitted",
+ "rupiah",
+ "Tildesmall",
+ "exclamdownsmall",
+ "centoldstyle",
+ "Lslashsmall",
+ "Scaronsmall",
+ "Zcaronsmall",
+ "Dieresissmall",
+ "Brevesmall",
+ "Caronsmall",
+ "Dotaccentsmall",
+ "Macronsmall",
+ "figuredash",
+ "hypheninferior",
+ "Ogoneksmall",
+ "Ringsmall",
+ "Cedillasmall",
+ "questiondownsmall",
+ "oneeighth",
+ "threeeighths",
+ "fiveeighths",
+ "seveneighths",
+ "onethird",
+ "twothirds",
+ "zerosuperior",
+ "foursuperior",
+ "fivesuperior",
+ "sixsuperior",
+ "sevensuperior",
+ "eightsuperior",
+ "ninesuperior",
+ "zeroinferior",
+ "oneinferior",
+ "twoinferior",
+ "threeinferior",
+ "fourinferior",
+ "fiveinferior",
+ "sixinferior",
+ "seveninferior",
+ "eightinferior",
+ "nineinferior",
+ "centinferior",
+ "dollarinferior",
+ "periodinferior",
+ "commainferior",
+ "Agravesmall",
+ "Aacutesmall",
+ "Acircumflexsmall",
+ "Atildesmall",
+ "Adieresissmall",
+ "Aringsmall",
+ "AEsmall",
+ "Ccedillasmall",
+ "Egravesmall",
+ "Eacutesmall",
+ "Ecircumflexsmall",
+ "Edieresissmall",
+ "Igravesmall",
+ "Iacutesmall",
+ "Icircumflexsmall",
+ "Idieresissmall",
+ "Ethsmall",
+ "Ntildesmall",
+ "Ogravesmall",
+ "Oacutesmall",
+ "Ocircumflexsmall",
+ "Otildesmall",
+ "Odieresissmall",
+ "OEsmall",
+ "Oslashsmall",
+ "Ugravesmall",
+ "Uacutesmall",
+ "Ucircumflexsmall",
+ "Udieresissmall",
+ "Yacutesmall",
+ "Thornsmall",
+ "Ydieresissmall",
+ "001.000",
+ "001.001",
+ "001.002",
+ "001.003",
+ "Black",
+ "Bold",
+ "Book",
+ "Light",
+ "Medium",
+ "Regular",
+ "Roman",
+ "Semibold"
+];
+
+var CFFEncodingMap = {
+ "0": "-reserved-",
+ "1": "hstem",
+ "2": "-reserved-",
+ "3": "vstem",
+ "4": "vmoveto",
+ "5": "rlineto",
+ "6": "hlineto",
+ "7": "vlineto",
+ "8": "rrcurveto",
+ "9": "-reserved-",
+ "10": "callsubr",
+ "11": "return",
+ "12": {
+ "3": "and",
+ "4": "or",
+ "5": "not",
+ "9": "abs",
+ "10": "add",
+ "11": "div",
+ "12": "sub",
+ "14": "neg",
+ "15": "eq",
+ "18": "drop",
+ "20": "put",
+ "21": "get",
+ "22": "ifelse",
+ "23": "random",
+ "24": "mul",
+ "26": "sqrt",
+ "27": "dup",
+ "28": "exch",
+ "29": "index",
+ "30": "roll",
+ "34": "hflex",
+ "35": "flex",
+ "36": "hflex1",
+ "37": "flex1"
+ },
+ "13": "-reserved-",
+ "14": "endchar",
+ "15": "-reserved-",
+ "16": "-reserved-",
+ "17": "-reserved-",
+ "18": "hstemhm",
+ "19": "hintmask",
+ "20": "cntrmask",
+ "21": "rmoveto",
+ "22": "hmoveto",
+ "23": "vstemhm",
+ "24": "rcurveline",
+ "25": "rlivecurve",
+ "26": "vvcurveto",
+ "27": "hhcurveto",
+ "29": "callgsubr",
+ "30": "vhcurveto",
+ "31": "hvcurveto"
+};
+
+var CFFDictDataMap = {
+ "0": {
+ name: "version",
+ operand: "SID"
+ },
+ "1": {
+ name: "Notice",
+ operand: "SID"
+ },
+ "2": {
+ name: "FullName",
+ operand: "SID"
+ },
+ "3": {
+ name: "FamilyName",
+ operand: "SID"
+ },
+ "4": {
+ name: "Weight",
+ operand: "SID"
+ },
+ "5": {
+ name: "FontBBox",
+ operand: [0, 0, 0, 0]
+ },
+ "6": {
+ name: "BlueValues"
+ },
+ "7": {
+ name: "OtherBlues"
+ },
+ "8": {
+ name: "FamilyBlues"
+ },
+ "9": {
+ name: "FamilyOtherBlues"
+ },
+ "10": {
+ name: "StdHW"
+ },
+ "11": {
+ name: "StdVW"
+ },
+ "12": {
+ "0": {
+ name: "Copyright",
+ operand: "SID"
+ },
+ "1": {
+ name: "IsFixedPitch",
+ operand: false
+ },
+ "2": {
+ name: "ItalicAngle",
+ operand: 0
+ },
+ "3": {
+ name: "UnderlinePosition",
+ operand: -100
+ },
+ "4": {
+ name: "UnderlineThickness",
+ operand: 50
+ },
+ "5": {
+ name: "PaintType",
+ operand: 0
+ },
+ "6": {
+ name: "CharstringType",
+ operand: 2
+ },
+ "7": {
+ name: "FontMatrix",
+ operand: [0.001, 0, 0, 0.001, 0 ,0]
+ },
+ "8": {
+ name: "StrokeWidth",
+ operand: 0
+ },
+ "9": {
+ name: "BlueScale"
+ },
+ "10": {
+ name: "BlueShift"
+ },
+ "11": {
+ name: "BlueFuzz"
+ },
+ "12": {
+ name: "StemSnapH"
+ },
+ "13": {
+ name: "StemSnapV"
+ },
+ "14": {
+ name: "ForceBold"
+ },
+ "17": {
+ name: "LanguageGroup"
+ },
+ "18": {
+ name: "ExpansionFactor"
+ },
+ "9": {
+ name: "initialRandomSeed"
+ },
+ "20": {
+ name: "SyntheticBase",
+ operand: null
+ },
+ "21": {
+ name: "PostScript",
+ operand: "SID"
+ },
+ "22": {
+ name: "BaseFontName",
+ operand: "SID"
+ },
+ "23": {
+ name: "BaseFontBlend",
+ operand: "delta"
+ }
+ },
+ "13": {
+ name: "UniqueID",
+ operand: null
+ },
+ "14": {
+ name: "XUID",
+ operand: []
+ },
+ "15": {
+ name: "charset",
+ operand: 0
+ },
+ "16": {
+ name: "Encoding",
+ operand: 0
+ },
+ "17": {
+ name: "CharStrings",
+ operand: null
+ },
+ "18": {
+ name: "Private",
+ operand: "number number"
+ },
+ "19": {
+ name: "Subrs"
+ },
+ "20": {
+ name: "defaultWidthX"
+ },
+ "21": {
+ name: "nominalWidthX"
+ }
+};
+
+var CFFDictPrivateDataMap = {
+ "6": {
+ name: "BluesValues",
+ operand: "delta"
+ },
+ "7": {
+ name: "OtherBlues",
+ operand: "delta"
+ },
+ "8": {
+ name: "FamilyBlues",
+ operand: "delta"
+ },
+ "9": {
+ name: "FamilyOtherBlues",
+ operand: "delta"
+ },
+ "10": {
+ name: "StdHW",
+ operand: null
+ },
+ "11": {
+ name: "StdVW",
+ operand: null
+ },
+ "12": {
+ "9": {
+ name: "BlueScale",
+ operand: 0.039625
+ },
+ "10": {
+ name: "BlueShift",
+ operand: 7
+ },
+ "11": {
+ name: "BlueFuzz",
+ operand: 1
+ },
+ "12": {
+ name: "StemSnapH",
+ operand: "delta"
+ },
+ "13": {
+ name: "StemSnapV",
+ operand: "delta"
+ },
+ "14": {
+ name: "ForceBold",
+ operand: "boolean"
+ },
+ "17": {
+ name: "LanguageGroup",
+ operand: 0
+ },
+ "18": {
+ name: "ExpansionFactor",
+ operand: 0.06
+ },
+ "19": {
+ name: "initialRandomSeed",
+ operand: 0
+ }
+ },
+ "19": {
+ name: "Subrs",
+ operand: null
+ },
+ "20": {
+ name: "defaultWidthX",
+ operand: 0
+ },
+ "21": {
+ name: "nominalWidthX",
+ operand: 0
+ }
+};
+
--- /dev/null
+/**
+ * The Type2 reader code below is only used for debugging purpose since Type2
+ * is only a CharString format and is never used directly as a Font file.
+ *
+ * So the code here is useful for dumping the data content of a .cff file in
+ * order to investigate the similarity between a Type1 CharString and a Type2
+ * CharString or to understand the structure of the CFF format.
+ */
+
+
+/**
+ * Build a charset by assigning the glyph name and the human readable form
+ * of the glyph data.
+ */
+function readCharset(aStream, aCharstrings) {
+ var charset = {};
+
+ var format = aStream.getByte();
+ if (format == 0) {
+ charset[".notdef"] = readCharstringEncoding(aCharstrings[0]);
+
+ var count = aCharstrings.length - 1;
+ for (var i = 1; i < count + 1; i++) {
+ var sid = aStream.getByte() << 8 | aStream.getByte();
+ charset[CFFStrings[sid]] = readCharstringEncoding(aCharstrings[i]);
+ //log(CFFStrings[sid] + "::" + charset[CFFStrings[sid]]);
+ }
+ } else if (format == 1) {
+ error("Charset Range are not supported");
+ } else {
+ error("Invalid charset format");
+ }
+
+ return charset;
+};
+
+/**
+ * Take a Type2 binary charstring as input and transform it to a human
+ * readable representation as specified by the 'The Type 2 Charstring Format',
+ * chapter 3.1.
+ */
+function readCharstringEncoding(aString) {
+ var charstringTokens = [];
+
+ var count = aString.length;
+ for (var i = 0; i < count; ) {
+ var value = aString[i++];
+ var token = null;
+
+ if (value < 0) {
+ continue;
+ } else if (value <= 11) {
+ token = CFFEncodingMap[value];
+ } else if (value == 12) {
+ token = CFFEncodingMap[value][aString[i++]];
+ } else if (value <= 18) {
+ token = CFFEncodingMap[value];
+ } else if (value <= 20) {
+ var mask = aString[i++];
+ token = CFFEncodingMap[value];
+ } else if (value <= 27) {
+ token = CFFEncodingMap[value];
+ } else if (value == 28) {
+ token = aString[i++] << 8 | aString[i++];
+ } else if (value <= 31) {
+ token = CFFEncodingMap[value];
+ } else if (value < 247) {
+ token = parseInt(value) - 139;
+ } else if (value < 251) {
+ token = ((value - 247) * 256) + aString[i++] + 108;
+ } else if (value < 255) {
+ token = -((value - 251) * 256) - aString[i++] - 108;
+ } else {// value == 255
+ token = aString[i++] << 24 | aString[i++] << 16 |
+ aString[i++] << 8 | aString[i];
+ }
+
+ charstringTokens.push(token);
+ }
+
+ return charstringTokens;
+};
+
+
+/**
+ * Take a binary DICT Data as input and transform it into a human readable
+ * form as specified by 'The Compact Font Format Specification', chapter 5.
+ */
+function readFontDictData(aString, aMap) {
+ var fontDictDataTokens = [];
+
+ var count = aString.length;
+ for (var i = 0; i < count; i) {
+ var value = aString[i++];
+ var token = null;
+
+ if (value == 12) {
+ token = aMap[value][aString[i++]];
+ } else if (value == 28) {
+ token = aString[i++] << 8 | aString[i++];
+ } else if (value == 29) {
+ token = aString[i++] << 24 |
+ aString[i++] << 16 |
+ aString[i++] << 8 |
+ aString[i++];
+ } else if (value == 30) {
+ token = "";
+ var parsed = false;
+ while (!parsed) {
+ var byte = aString[i++];
+
+ var nibbles = [parseInt(byte / 16), parseInt(byte % 16)];
+ for (var j = 0; j < nibbles.length; j++) {
+ var nibble = nibbles[j];
+ switch (nibble) {
+ case 0xA:
+ token += ".";
+ break;
+ case 0xB:
+ token += "E";
+ break;
+ case 0xC:
+ token += "E-";
+ break;
+ case 0xD:
+ break;
+ case 0xE:
+ token += "-";
+ break;
+ case 0xF:
+ parsed = true;
+ break;
+ default:
+ token += nibble;
+ break;
+ }
+ }
+ };
+ token = parseFloat(token);
+ } else if (value <= 31) {
+ token = aMap[value];
+ } else if (value <= 246) {
+ token = parseInt(value) - 139;
+ } else if (value <= 250) {
+ token = ((value - 247) * 256) + aString[i++] + 108;
+ } else if (value <= 254) {
+ token = -((value - 251) * 256) - aString[i++] - 108;
+ } else if (value == 255) {
+ error("255 is not a valid DICT command");
+ }
+
+ fontDictDataTokens.push(token);
+ }
+
+ return fontDictDataTokens;
+};
+
+
+/**
+ * Take a stream as input and return an array of objects.
+ * In CFF an INDEX is a structure with the following format:
+ * {
+ * count: 2 bytes (Number of objects stored in INDEX),
+ * offsize: 1 byte (Offset array element size),
+ * offset: [count + 1] bytes (Offsets array),
+ * data: - (Objects data)
+ * }
+ *
+ * More explanation are given in the 'CFF Font Format Specification',
+ * chapter 5.
+ */
+function readFontIndexData(aStream, aIsByte) {
+ var count = aStream.getByte() << 8 | aStream.getByte();
+ var offsize = aStream.getByte();
+
+ function getNextOffset() {
+ switch (offsize) {
+ case 0:
+ return 0;
+ case 1:
+ return aStream.getByte();
+ case 2:
+ return aStream.getByte() << 8 | aStream.getByte();
+ case 3:
+ return aStream.getByte() << 16 | aStream.getByte() << 8 |
+ aStream.getByte();
+ case 4:
+ return aStream.getByte() << 24 | aStream.getByte() << 16 |
+ aStream.getByte() << 8 | aStream.getByte();
+ }
+ };
+
+ var offsets = [];
+ for (var i = 0; i < count + 1; i++)
+ offsets.push(getNextOffset());
+
+ log("Found " + count + " objects at offsets :" + offsets + " (offsize: " + offsize + ")");
+
+ // Now extract the objects
+ var relativeOffset = aStream.pos;
+ var objects = [];
+ for (var i = 0; i < count; i++) {
+ var offset = offsets[i];
+ aStream.pos = relativeOffset + offset - 1;
+
+ var data = [];
+ var length = offsets[i + 1] - 1;
+ for (var j = offset - 1; j < length; j++)
+ data.push(aIsByte ? aStream.getByte() : aStream.getChar());
+ objects.push(data);
+ }
+
+ return objects;
+};
+
+var Type2Parser = function(aFilePath) {
+ var font = new Dict();
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", aFilePath, false);
+ xhr.mozResponseType = xhr.responseType = "arraybuffer";
+ xhr.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200;
+ xhr.send(null);
+ this.data = new Stream(xhr.mozResponseArrayBuffer || xhr.mozResponse ||
+ xhr.responseArrayBuffer || xhr.response);
+
+ // Turn on this flag for additional debugging logs
+ var debug = false;
+
+ function dump(aStr) {
+ if (debug)
+ log(aStr);
+ };
+
+ function parseAsToken(aString, aMap) {
+ var decoded = readFontDictData(aString, aMap);
+
+ 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, {
+ offset: stack.pop(),
+ size: 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;
+ }
+ }
+ }
+ };
+
+ this.parse = function(aStream) {
+ font.set("major", aStream.getByte());
+ font.set("minor", aStream.getByte());
+ font.set("hdrSize", aStream.getByte());
+ font.set("offsize", aStream.getByte());
+
+ // Move the cursor after the header
+ aStream.skip(font.get("hdrSize") - aStream.pos);
+
+ // Read the NAME Index
+ dump("Reading Index: Names");
+ font.set("Names", readFontIndexData(aStream));
+ log("Names: " + font.get("Names"));
+
+ // Read the Top Dict Index
+ dump("Reading Index: TopDict");
+ var topDict = readFontIndexData(aStream, true);
+ log("TopDict: " + topDict);
+
+ // Read the String Index
+ dump("Reading Index: Strings");
+ var strings = readFontIndexData(aStream);
+ log("strings: " + strings);
+
+ // Fill up the Strings dictionary with the new unique strings
+ for (var i = 0; i < strings.length; i++)
+ CFFStrings.push(strings[i].join(""));
+
+ // Parse the TopDict operator
+ var objects = [];
+ var count = topDict.length;
+ for (var i = 0; i < count; i++)
+ parseAsToken(topDict[i], CFFDictDataMap);
+
+ // Read the Global Subr Index that comes just after the Strings Index
+ // (cf. "The Compact Font Format Specification" Chapter 16)
+ dump("Reading Global Subr Index");
+ var subrs = readFontIndexData(aStream, true);
+ dump(subrs);
+
+ // Reading Private Dict
+ var private = font.get("Private");
+ log("Reading Private Dict (offset: " + private.offset + " size: " + private.size + ")");
+ aStream.pos = private.offset;
+
+ var privateDict = [];
+ for (var i = 0; i < private.size; i++)
+ privateDict.push(aStream.getByte());
+ dump("private:" + privateDict);
+ parseAsToken(privateDict, CFFDictPrivateDataMap);
+
+ for (var p in font.map)
+ dump(p + "::" + font.get(p));
+
+ // Read CharStrings Index
+ var charStringsOffset = font.get("CharStrings");
+ dump("Read CharStrings Index (offset: " + charStringsOffset + ")");
+ aStream.pos = charStringsOffset;
+ var charStrings = readFontIndexData(aStream, true);
+
+ // Read Charset
+ dump("Read Charset for " + charStrings.length + " glyphs");
+ var charsetEntry = font.get("charset");
+ if (charsetEntry == 0) {
+ error("Need to support CFFISOAdobeCharset");
+ } else if (charsetEntry == 1) {
+ error("Need to support CFFExpert");
+ } else if (charsetEntry == 2) {
+ error("Need to support CFFExpertSubsetCharset");
+ } else {
+ aStream.pos = charsetEntry;
+ var charset = readCharset(aStream, charStrings);
+ }
+ }
+};
+
+/*
+ * To try the Type2 decoder on a local file in the current directory:
+ *
+ * var cff = new Type2Parser("file.cff");
+ * cff.parse(this.data);
+ *
+ * To try the Type2 decoder on a custom built CFF array:
+ *
+ * var file = new Uint8Array(cffFileArray, 0, cffFileSize);
+ * var parser = new Type2Parser();
+ * parser.parse(new Stream(file));
+ *
+ */
+
+
+/**
+ * Write to a file to the disk (works only on Firefox in privilege mode)
+ * but this is useful for dumping a font file to the disk and check with
+ * fontforge or the ots program what's wrong with the file.
+ *
+ * writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
+ */
+function writeToFile(aBytes, aFilePath) {
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ var Cc = Components.classes,
+ Ci = Components.interfaces;
+ var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
+ file.initWithPath(aFilePath);
+
+ var stream = Cc["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ stream.init(file, 0x04 | 0x08 | 0x20, 0600, 0);
+
+ var bos = Cc["@mozilla.org/binaryoutputstream;1"]
+ .createInstance(Ci.nsIBinaryOutputStream);
+ bos.setOutputStream(stream);
+ bos.writeByteArray(aBytes, aBytes.length);
+ stream.close();
+};
+