Merge branch 'master' of github.com:andreasgal/pdf.js

This commit is contained in:
sbarman 2011-06-17 08:33:14 -07:00
commit 6ad785b188
9 changed files with 8675 additions and 157 deletions

1552
Encodings.js Normal file

File diff suppressed because it is too large Load Diff

689
cffStandardStrings.js Normal file
View 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
}
};

1484
fonts.js Normal file

File diff suppressed because it is too large Load Diff

391
fonts_utils.js Normal file
View 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

File diff suppressed because it is too large Load Diff

101
pdf.js
View File

@ -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
View 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
View File

@ -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
View 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);
}