9 changed files with 8678 additions and 163 deletions
@ -0,0 +1,689 @@
@@ -0,0 +1,689 @@
|
||||
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 |
||||
} |
||||
}; |
||||
|
@ -0,0 +1,391 @@
@@ -0,0 +1,391 @@
|
||||
/** |
||||
* 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(); |
||||
}; |
||||
|
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / |
||||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ |
||||
|
||||
body { |
||||
margin: 6px; |
||||
padding: 0px; |
||||
background-color: #c0bdb7; |
||||
} |
||||
|
||||
#controls { |
||||
position:fixed; |
||||
left: 0px; |
||||
top: 0px; |
||||
width: 100%; |
||||
padding: 7px; |
||||
border-bottom: 1px solid black; |
||||
background-color: rgb(242, 240, 238); |
||||
} |
||||
|
||||
span#info { |
||||
float: right; |
||||
font: 14px sans-serif; |
||||
margin-right: 10px; |
||||
} |
||||
|
||||
#viewer { |
||||
margin: auto; |
||||
border: 1px solid black; |
||||
width: 8.5in; |
||||
height: 11in; |
||||
} |
||||
|
||||
#pageNumber { |
||||
text-align: right; |
||||
} |
@ -1,155 +1,34 @@
@@ -1,155 +1,34 @@
|
||||
<html> |
||||
<head> |
||||
<title>Simple pdf.js page viewer</title> |
||||
<script type="text/javascript" |
||||
src="pdf.js"></script> |
||||
<style type"text/css"> |
||||
body { |
||||
margin: 6px; |
||||
padding: 0px; |
||||
background-color: #c0bdb7; |
||||
} |
||||
#controls { |
||||
border-bottom: 1px solid black; |
||||
position:fixed; |
||||
left: 0px; top: 0px; |
||||
width: 100%; |
||||
padding: 7px; |
||||
background-color: rgb(242, 240, 238); |
||||
} |
||||
span#info { |
||||
float: right; |
||||
font: 14px sans-serif; |
||||
margin-right: 10px; |
||||
} |
||||
#viewer { |
||||
margin: auto; |
||||
border: 1px solid black; |
||||
width: 8.5in; |
||||
height: 11in; |
||||
} |
||||
#pageNumber { |
||||
text-align: right; |
||||
} |
||||
</style> |
||||
|
||||
<script type="text/javascript"> |
||||
function queryParams() { |
||||
var qs = window.location.search.substring(1); |
||||
var kvs = qs.split("&"); |
||||
var params = { }; |
||||
for (var i = 0; i < kvs.length; ++i) { |
||||
var kv = kvs[i].split("="); |
||||
params[unescape(kv[0])] = unescape(kv[1]); |
||||
} |
||||
return params; |
||||
} |
||||
|
||||
var canvas, numPages, pageDisplay, pageNum; |
||||
function load(userInput) { |
||||
canvas = document.getElementById("canvas"); |
||||
canvas.mozOpaque = true; |
||||
pageDisplay = document.getElementById("pageNumber"); |
||||
infoDisplay = document.getElementById("info"); |
||||
pageNum = parseInt(queryParams().page) || 1; |
||||
fileName = userInput; |
||||
if(!userInput){ |
||||
fileName = queryParams().file || "compressed.tracemonkey-pldi-09.pdf"; |
||||
} |
||||
open(fileName); |
||||
} |
||||
|
||||
function open(url) { |
||||
document.title = url; |
||||
req = new XMLHttpRequest(); |
||||
req.open("GET", url); |
||||
req.mozResponseType = req.responseType = "arraybuffer"; |
||||
req.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200; |
||||
req.onreadystatechange = xhrstate; |
||||
req.send(null); |
||||
} |
||||
|
||||
function xhrstate() { |
||||
if (req.readyState == 4 && req.status == req.expected) { |
||||
var data = req.mozResponseArrayBuffer || |
||||
req.mozResponse || |
||||
req.responseArrayBuffer || |
||||
req.response; |
||||
pdf = new PDFDoc(new Stream(data)); |
||||
numPages = pdf.numPages; |
||||
document.getElementById("numPages").innerHTML = numPages.toString(); |
||||
gotoPage(pageNum); |
||||
} |
||||
} |
||||
|
||||
function displayPage(num) { |
||||
pageDisplay.value = num; |
||||
|
||||
var t0 = Date.now(); |
||||
|
||||
var page = pdf.getPage(pageNum = num); |
||||
|
||||
var t1 = Date.now(); |
||||
|
||||
var ctx = canvas.getContext("2d"); |
||||
ctx.save(); |
||||
ctx.fillStyle = "rgb(255, 255, 255)"; |
||||
ctx.fillRect(0, 0, canvas.width, canvas.height); |
||||
ctx.restore(); |
||||
|
||||
var gfx = new CanvasGraphics(ctx); |
||||
|
||||
// page.compile will collect all fonts for us, once we have loaded them |
||||
// we can trigger the actual page rendering with page.display |
||||
var fonts = []; |
||||
page.compile(gfx, fonts); |
||||
|
||||
var t2 = Date.now(); |
||||
|
||||
// This should be called when font loading is complete |
||||
page.display(gfx); |
||||
|
||||
var t3 = Date.now(); |
||||
|
||||
infoDisplay.innerHTML = "Time to load/compile/render: "+ (t1 - t0) + "/" + (t2 - t1) + "/" + (t3 - t2) + " ms"; |
||||
} |
||||
|
||||
function nextPage() { |
||||
if (pageNum < numPages) |
||||
++pageNum; |
||||
displayPage(pageNum); |
||||
} |
||||
|
||||
function prevPage() { |
||||
if (pageNum > 1) |
||||
--pageNum; |
||||
displayPage(pageNum); |
||||
} |
||||
function gotoPage(num) { |
||||
if (0 <= num && num <= numPages) |
||||
pageNum = num; |
||||
displayPage(pageNum); |
||||
} |
||||
</script> |
||||
</head> |
||||
|
||||
<body onload="load();"> |
||||
<div id="controls"> |
||||
<head> |
||||
<title>Simple pdf.js page viewer</title> |
||||
<link rel="stylesheet" href="test.css"></link> |
||||
|
||||
<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="Encodings.js"></script> |
||||
<script type="text/javascript" src="glyphlist.js"></script> |
||||
</head> |
||||
|
||||
<body onload="load();"> |
||||
<div id="controls"> |
||||
<input type="file" style="float: right; margin: auto 32px;" onChange="load(this.value.toString());"></input> |
||||
<!-- This only opens supported PDFs from the source path... |
||||
-- Can we use JSONP to overcome the same-origin restrictions? --> |
||||
<button onclick="prevPage();">Previous</button> |
||||
<button onclick="nextPage();">Next</button> |
||||
<input type="text" id="pageNumber" onchange="gotoPage(this.value);" |
||||
value="1" size="4"></input> |
||||
/ <span id="numPages">--</span> |
||||
<span id="info"></span> |
||||
</div> |
||||
<div id="viewer"> |
||||
<!-- Canvas dimensions must be specified in CSS pixels. CSS pixels |
||||
-- are always 96 dpi. These dimensions are 8.5x11in at 96dpi. --> |
||||
<canvas id="canvas" width="816" height="1056"></canvas> |
||||
</div> |
||||
</body> |
||||
|
||||
<button onclick="prevPage();">Previous</button> |
||||
<button onclick="nextPage();">Next</button> |
||||
<input type="text" id="pageNumber" onchange="gotoPage(this.value);" |
||||
value="1" size="4"></input> |
||||
<span id="numPages">--</span> |
||||
<span id="info"></span> |
||||
</div> |
||||
|
||||
<div id="viewer"> |
||||
<!-- Canvas dimensions must be specified in CSS pixels. CSS pixels |
||||
are always 96 dpi. These dimensions are 8.5x11in at 96dpi. --> |
||||
<canvas id="canvas" width="816" height="1056"></canvas> |
||||
</div> |
||||
</body> |
||||
</html> |
||||
|
||||
|
@ -0,0 +1,117 @@
@@ -0,0 +1,117 @@
|
||||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / |
||||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ |
||||
|
||||
var pdfDocument, canvas, pageDisplay, pageNum, pageInterval; |
||||
function load(userInput) { |
||||
canvas = document.getElementById("canvas"); |
||||
canvas.mozOpaque = true; |
||||
pageNum = parseInt(queryParams().page) || 1; |
||||
fileName = userInput; |
||||
if (!userInput) { |
||||
fileName = queryParams().file || "compressed.tracemonkey-pldi-09.pdf"; |
||||
} |
||||
open(fileName); |
||||
} |
||||
|
||||
function queryParams() { |
||||
var qs = window.location.search.substring(1); |
||||
var kvs = qs.split("&"); |
||||
var params = { }; |
||||
for (var i = 0; i < kvs.length; ++i) { |
||||
var kv = kvs[i].split("="); |
||||
params[unescape(kv[0])] = unescape(kv[1]); |
||||
} |
||||
return params; |
||||
} |
||||
|
||||
function open(url) { |
||||
document.title = url; |
||||
req = new XMLHttpRequest(); |
||||
req.open("GET", url); |
||||
req.mozResponseType = req.responseType = "arraybuffer"; |
||||
req.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200; |
||||
req.onreadystatechange = function() { |
||||
if (req.readyState == 4 && req.status == req.expected) { |
||||
var data = req.mozResponseArrayBuffer || req.mozResponse || |
||||
req.responseArrayBuffer || req.response; |
||||
pdfDocument = new PDFDoc(new Stream(data)); |
||||
numPages = pdfDocument.numPages; |
||||
document.getElementById("numPages").innerHTML = numPages.toString(); |
||||
goToPage(pageNum); |
||||
} |
||||
}; |
||||
req.send(null); |
||||
} |
||||
|
||||
function gotoPage(num) { |
||||
if (0 <= num && num <= numPages) |
||||
pageNum = num; |
||||
displayPage(pageNum); |
||||
} |
||||
|
||||
function displayPage(num) { |
||||
if (pageNum != num) |
||||
window.clearTimeout(pageInterval); |
||||
|
||||
document.getElementById("pageNumber").value = num; |
||||
|
||||
var t0 = Date.now(); |
||||
|
||||
var page = pdfDocument.getPage(pageNum = num); |
||||
|
||||
var t1 = Date.now(); |
||||
var ctx = canvas.getContext("2d"); |
||||
ctx.save(); |
||||
ctx.fillStyle = "rgb(255, 255, 255)"; |
||||
ctx.fillRect(0, 0, canvas.width, canvas.height); |
||||
ctx.restore(); |
||||
|
||||
var gfx = new CanvasGraphics(ctx); |
||||
|
||||
// page.compile will collect all fonts for us, once we have loaded them
|
||||
// we can trigger the actual page rendering with page.display
|
||||
var fonts = []; |
||||
page.compile(gfx, fonts); |
||||
var t2 = Date.now(); |
||||
|
||||
var interval = 0; |
||||
for (var i = 0; i < fonts.length; i++) { |
||||
if (fonts[i].loading) { |
||||
interval = 10; |
||||
break; |
||||
} |
||||
}; |
||||
|
||||
// FIXME This need to be replaced by an event
|
||||
pageInterval = setInterval(function() { |
||||
for (var i = 0; i < fonts.length; i++) { |
||||
if (fonts[i].loading) |
||||
return; |
||||
} |
||||
var t3 = Date.now(); |
||||
|
||||
clearInterval(pageInterval); |
||||
page.display(gfx); |
||||
|
||||
var t4 = Date.now(); |
||||
|
||||
var infoDisplay = document.getElementById("info"); |
||||
infoDisplay.innerHTML = "Time to load/compile/fonts/render: "+ (t1 - t0) + "/" + (t2 - t1) + "/" + (t3 - t2) + "/" + (t4 - t3) + " ms"; |
||||
}, interval); |
||||
} |
||||
|
||||
function nextPage() { |
||||
if (pageNum < pdfDocument.numPages) |
||||
displayPage(++pageNum); |
||||
} |
||||
|
||||
function prevPage() { |
||||
if (pageNum > 1) |
||||
displayPage(--pageNum); |
||||
} |
||||
|
||||
function goToPage(num) { |
||||
if (0 <= num && num <= numPages) |
||||
displayPage(pageNum = num); |
||||
} |
||||
|
Loading…
Reference in new issue