Merge branch 'master' of github.com:andreasgal/pdf.js
This commit is contained in:
commit
6ad785b188
1552
Encodings.js
Normal file
1552
Encodings.js
Normal file
File diff suppressed because it is too large
Load Diff
689
cffStandardStrings.js
Normal file
689
cffStandardStrings.js
Normal file
@ -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
|
||||
}
|
||||
};
|
||||
|
391
fonts_utils.js
Normal file
391
fonts_utils.js
Normal file
@ -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();
|
||||
};
|
||||
|
4289
glyphlist.js
Normal file
4289
glyphlist.js
Normal file
File diff suppressed because it is too large
Load Diff
101
pdf.js
101
pdf.js
@ -5,6 +5,7 @@ var ERRORS = 0, WARNINGS = 1, TODOS = 5;
|
||||
var verbosity = WARNINGS;
|
||||
|
||||
function log(msg) {
|
||||
msg = msg.toString ? msg.toString() : msg;
|
||||
if (console && console.log)
|
||||
console.log(msg);
|
||||
else if (print)
|
||||
@ -51,7 +52,7 @@ var Stream = (function() {
|
||||
this.bytes = new Uint8Array(arrayBuffer);
|
||||
this.start = start || 0;
|
||||
this.pos = this.start;
|
||||
this.end = (start + length) || arrayBuffer.byteLength;
|
||||
this.end = (start + length) || this.bytes.byteLength;
|
||||
this.dict = dict;
|
||||
}
|
||||
|
||||
@ -92,7 +93,7 @@ var Stream = (function() {
|
||||
return ch;
|
||||
},
|
||||
skip: function(n) {
|
||||
if (!n)
|
||||
if (!n && !IsNum(n))
|
||||
n = 1;
|
||||
this.pos += n;
|
||||
},
|
||||
@ -522,6 +523,9 @@ var Name = (function() {
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
toString: function() {
|
||||
return this.name;
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
@ -533,6 +537,9 @@ var Cmd = (function() {
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
toString: function() {
|
||||
return this.cmd;
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
@ -555,6 +562,16 @@ var Dict = (function() {
|
||||
},
|
||||
set: function(key, value) {
|
||||
this.map[key] = value;
|
||||
},
|
||||
forEach: function(aCallback) {
|
||||
for (var key in this.map)
|
||||
aCallback(key, this.map[key]);
|
||||
},
|
||||
toString: function() {
|
||||
var keys = [];
|
||||
for (var key in this.map)
|
||||
keys.push(key);
|
||||
return "Dict with " + keys.length + " keys: " + keys;
|
||||
}
|
||||
};
|
||||
|
||||
@ -777,6 +794,7 @@ var Lexer = (function() {
|
||||
x = (x << 3) + (ch - '0');
|
||||
}
|
||||
}
|
||||
|
||||
str += String.fromCharCode(x);
|
||||
break;
|
||||
case '\r':
|
||||
@ -898,10 +916,11 @@ var Lexer = (function() {
|
||||
stream.skip();
|
||||
return new Cmd(">>");
|
||||
}
|
||||
case "{":
|
||||
case "}":
|
||||
return new Cmd(ch);
|
||||
// fall through
|
||||
case ')':
|
||||
case '{':
|
||||
case '}':
|
||||
error("Illegal character");
|
||||
return Error;
|
||||
}
|
||||
@ -1706,7 +1725,49 @@ var CanvasGraphics = (function() {
|
||||
|
||||
constructor.prototype = {
|
||||
translateFont: function(fontDict, xref, resources) {
|
||||
return "translated";
|
||||
var descriptor = xref.fetch(fontDict.get("FontDescriptor"));
|
||||
var fontName = descriptor.get("FontName").name;
|
||||
fontName = fontName.replace("+", "_");
|
||||
|
||||
var font = Fonts[fontName];
|
||||
if (!font) {
|
||||
var fontFile = descriptor.get2("FontFile", "FontFile2");
|
||||
fontFile = xref.fetchIfRef(fontFile);
|
||||
|
||||
// Generate the custom cmap of the font if needed
|
||||
var encodingMap = {};
|
||||
if (fontDict.has("Encoding")) {
|
||||
var encoding = xref.fetchIfRef(fontDict.get("Encoding"));
|
||||
if (IsDict(encoding)) {
|
||||
// Build an map between codes and glyphs
|
||||
var differences = encoding.get("Differences");
|
||||
var index = 0;
|
||||
for (var j = 0; j < differences.length; j++) {
|
||||
var data = differences[j];
|
||||
IsNum(data) ? index = data : encodingMap[index++] = data;
|
||||
}
|
||||
|
||||
// Get the font charset
|
||||
var charset = descriptor.get("CharSet").split("/");
|
||||
} else if (IsName(encoding)) {
|
||||
var encoding = Encodings[encoding];
|
||||
var widths = xref.fetchIfRef(fontDict.get("Widths"));
|
||||
var firstchar = xref.fetchIfRef(fontDict.get("FirstChar"));
|
||||
|
||||
var charset = [];
|
||||
for (var j = 0; j < widths.length; j++) {
|
||||
var index = widths[j];
|
||||
if (index)
|
||||
charset.push(encoding[j + firstchar]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fontBBox = descriptor.get("FontBBox");
|
||||
var subtype = fontDict.get("Subtype").name;
|
||||
new Font(fontName, fontFile, encodingMap, charset, fontBBox, subtype);
|
||||
}
|
||||
return Fonts[fontName];
|
||||
},
|
||||
|
||||
beginDrawing: function(mediaBox) {
|
||||
@ -1943,16 +2004,25 @@ var CanvasGraphics = (function() {
|
||||
this.current.leading = leading;
|
||||
},
|
||||
setFont: function(fontRef, size) {
|
||||
var fontRes = this.res.get("Font");
|
||||
if (!fontRes)
|
||||
return;
|
||||
fontRes = this.xref.fetchIfRef(fontRes);
|
||||
var font = fontRes.get(fontRef.name);
|
||||
var font = this.res.get("Font");
|
||||
if (!IsDict(font))
|
||||
return;
|
||||
|
||||
font = font.get(fontRef.name);
|
||||
font = this.xref.fetchIfRef(font);
|
||||
if (!font)
|
||||
return;
|
||||
|
||||
var fontName = "";
|
||||
var fontDescriptor = font.get("FontDescriptor");
|
||||
if (fontDescriptor.num) {
|
||||
var fontDescriptor = this.xref.fetchIfRef(fontDescriptor);
|
||||
fontName = fontDescriptor.get("FontName").name.replace("+", "_");
|
||||
Fonts.active = fontName;
|
||||
}
|
||||
|
||||
this.current.fontSize = size;
|
||||
TODO("using hard-coded font for testing");
|
||||
this.ctx.font = this.current.fontSize +'px "Nimbus Roman No9 L"';
|
||||
this.ctx.font = this.current.fontSize +'px "' + fontName + '"';
|
||||
},
|
||||
setTextRenderingMode: function(mode) {
|
||||
TODO("text rendering mode");
|
||||
@ -1982,7 +2052,12 @@ var CanvasGraphics = (function() {
|
||||
this.ctx.scale(1, -1);
|
||||
this.ctx.transform.apply(this.ctx, this.current.textMatrix);
|
||||
|
||||
this.ctx.fillText(text, this.current.x, this.current.y);
|
||||
// Replace characters code by glyphs code
|
||||
var glyphs = [];
|
||||
for (var i = 0; i < text.length; i++)
|
||||
glyphs[i] = String.fromCharCode(Fonts.unicodeFromCode(text[i].charCodeAt(0)));
|
||||
|
||||
this.ctx.fillText(glyphs.join(""), this.current.x, this.current.y);
|
||||
this.current.x += this.ctx.measureText(text).width;
|
||||
|
||||
this.ctx.restore();
|
||||
|
35
test.css
Normal file
35
test.css
Normal file
@ -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: 12.75in;
|
||||
height: 16.5in;
|
||||
}
|
||||
|
||||
#pageNumber {
|
||||
text-align: right;
|
||||
}
|
174
test.html
174
test.html
@ -1,149 +1,35 @@
|
||||
<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>
|
||||
<head>
|
||||
<title>Simple pdf.js page viewer</title>
|
||||
<link rel="stylesheet" href="test.css"></link>
|
||||
|
||||
<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;
|
||||
}
|
||||
<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>
|
||||
|
||||
var canvas, numPages, pageDisplay, pageNum;
|
||||
function load() {
|
||||
canvas = document.getElementById("canvas");
|
||||
canvas.mozOpaque = true;
|
||||
pageDisplay = document.getElementById("pageNumber");
|
||||
infoDisplay = document.getElementById("info");
|
||||
pageNum = parseInt(queryParams().page) || 1;
|
||||
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">
|
||||
<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>
|
||||
<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. 816x1056 is 8.5x11in at 96dpi. -->
|
||||
<!-- We're rendering here at 1.5x scale. -->
|
||||
<canvas id="canvas" width="1224" height="1584"></canvas>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
117
test.js
Normal file
117
test.js
Normal file
@ -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…
x
Reference in New Issue
Block a user