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

This commit is contained in:
sbarman 2011-09-25 16:27:15 -07:00
commit 16c0e0410c
17 changed files with 932 additions and 801 deletions

View File

@ -12,6 +12,8 @@ PDF_JS_FILES = \
pdf.js \
crypto.js \
fonts.js \
metrics.js \
charsets.js \
glyphlist.js \
$(NULL)

View File

@ -27,6 +27,32 @@ For an online demo, visit:
This demo provides an interactive interface for displaying and browsing PDFs
using the pdf.js API.
**Getting the code**
To get a local copy of the current code, clone it using git:
```bash
git clone git://github.com/andreasgal/pdf.js.git pdfjs
cd pdfjs
```
Next, you need to start a local web server as some browsers don't allow opening
PDF files for a file:// url:
```bash
make server
```
If everything worked out, you can now serve
http://localhost:8888/web/viewer.html
You can also view all the test pdf files on the right side serving
http://localhost:8888/test/pdfs/?frame
**Hello world**
For a "hello world" example, take a look at:
@ -38,6 +64,20 @@ in a custom project.
## Contributing
pdf.js is a community-driver project, so contributors are always welcome.
Simply fork our repo and contribute away. A great place to start is our
open issues. For better consistency and long-term stability, please do look around the
code and try to follow our conventions.
If you __don't want to hack__ on the project or have short spare times, you still
can help! Just open PDFs in the
[online demo](http://andreasgal.github.com/pdf.js/web/viewer.html) and report
any breakage in rendering.
## Running the Tests
pdf.js comes with browser-level regression tests that allow one to probe
@ -64,16 +104,6 @@ images. The test type `load` simply tests whether the file loads without
raising any errors.
## Contributing
pdf.js is a community-driver project, so contributors are always welcome.
Simply fork our repo and contribute away. A great place to start is our
open issues.
For better consistency and long-term stability, please do look around the
code and try to follow our conventions.
## Additional resources
Our demo site is here:
@ -97,8 +127,28 @@ Join our mailing list:
Subscribe either using lists.mozilla.org or Google Groups:
https://lists.mozilla.org/listinfo/dev-pdf-js
https://groups.google.com/group/mozilla.dev.pdf-js/topics
Talk to us on IRC:
#pdfjs on irc.mozilla.org
## Additional resources to understand the structure of PDF
A really basic overview of PDF is described here:
http://partners.adobe.com/public/developer/en/livecycle/lc_pdf_overview_format.pdf
A more detailed file example:
http://gnupdf.org/Introduction_to_PDF
The PDF specification itself is an ISO and not free available. However, there is
a "PDF Reference" from Adobe:
http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/pdf_reference_1-7.pdf
Recommanded chapters to read: "2. Overview", "3.4 File Structure",
"4.1 Graphics Objects" that lists the PDF commands.

101
charsets.js Normal file
View File

@ -0,0 +1,101 @@
var ISOAdobeCharset = [
'.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'
];
var ExpertCharset = [
'.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle',
'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior',
'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma',
'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle',
'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle',
'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle',
'colon', 'semicolon', 'commasuperior', 'threequartersemdash',
'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior',
'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
'tsuperior', 'ff', 'fi', 'fl', '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', 'onequarter', 'onehalf', 'threequarters',
'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths',
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
'twosuperior', 'threesuperior', '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'
];
var ExpertSubsetCharset = [
'.notdef', 'space', 'dollaroldstyle', 'dollarsuperior',
'parenleftsuperior', 'parenrightsuperior', 'twodotenleader',
'onedotenleader', 'comma', 'hyphen', 'period', 'fraction',
'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle',
'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior',
'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior',
'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior',
'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted',
'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter',
'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths',
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
'periodinferior', 'commainferior'
];

View File

@ -3,7 +3,7 @@
'use strict';
var ARCFourCipher = (function() {
var ARCFourCipher = (function aRCFourCipher() {
function constructor(key) {
this.a = 0;
this.b = 0;
@ -21,7 +21,7 @@ var ARCFourCipher = (function() {
}
constructor.prototype = {
encryptBlock: function(data) {
encryptBlock: function aRCFourCipherEncryptBlock(data) {
var i, n = data.length, tmp, tmp2;
var a = this.a, b = this.b, s = this.s;
var output = new Uint8Array(n);
@ -45,7 +45,7 @@ var ARCFourCipher = (function() {
return constructor;
})();
var md5 = (function() {
var calculateMD5 = (function calculateMD5() {
var r = new Uint8Array([
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
@ -129,12 +129,12 @@ var md5 = (function() {
return hash;
})();
var NullCipher = (function() {
var NullCipher = (function nullCipher() {
function constructor() {
}
constructor.prototype = {
decryptBlock: function(data) {
decryptBlock: function nullCipherDecryptBlock(data) {
return data;
}
};
@ -142,7 +142,7 @@ var NullCipher = (function() {
return constructor;
})();
var AES128Cipher = (function() {
var AES128Cipher = (function aES128Cipher() {
var rcon = new Uint8Array([
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
@ -372,7 +372,7 @@ var AES128Cipher = (function() {
}
constructor.prototype = {
decryptBlock: function(data) {
decryptBlock: function aES128CipherDecryptBlock(data) {
var i, sourceLength = data.length;
var buffer = this.buffer, bufferLength = this.bufferPosition;
// waiting for IV values -- they are at the start of the stream
@ -395,19 +395,21 @@ var AES128Cipher = (function() {
return constructor;
})();
var CipherTransform = (function() {
var CipherTransform = (function cipherTransform() {
function constructor(stringCipherConstructor, streamCipherConstructor) {
this.stringCipherConstructor = stringCipherConstructor;
this.streamCipherConstructor = streamCipherConstructor;
}
constructor.prototype = {
createStream: function(stream) {
createStream: function cipherTransformCreateStream(stream) {
var cipher = new this.streamCipherConstructor();
return new DecryptStream(stream, function(data) {
return new DecryptStream(stream,
function cipherTransformDecryptStream(data) {
return cipher.decryptBlock(data);
});
}
);
},
decryptString: function(s) {
decryptString: function cipherTransformDecryptString(s) {
var cipher = new this.stringCipherConstructor();
var data = stringToBytes(s);
data = cipher.decryptBlock(data);
@ -417,7 +419,7 @@ var CipherTransform = (function() {
return constructor;
})();
var CipherTransformFactory = (function() {
var CipherTransformFactory = (function cipherTransformFactory() {
function prepareKeyData(fileId, password, ownerPassword, userPassword,
flags, revision, keyLength, encryptMetadata) {
var defaultPasswordBytes = new Uint8Array([
@ -450,11 +452,11 @@ var CipherTransformFactory = (function() {
hashData[i++] = 0xFF;
hashData[i++] = 0xFF;
}
var hash = md5(hashData, 0, i);
var hash = calculateMD5(hashData, 0, i);
var keyLengthInBytes = keyLength >> 3;
if (revision >= 3) {
for (j = 0; j < 50; ++j) {
hash = md5(hash, 0, keyLengthInBytes);
hash = calculateMD5(hash, 0, keyLengthInBytes);
}
}
var encryptionKey = hash.subarray(0, keyLengthInBytes);
@ -467,7 +469,7 @@ var CipherTransformFactory = (function() {
for (j = 0, n = fileId.length; j < n; ++j)
hashData[i++] = fileId[j];
cipher = new ARCFourCipher(encryptionKey);
var checkData = cipher.encryptBlock(md5(hashData, 0, i));
var checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i));
n = encryptionKey.length;
var derrivedKey = new Uint8Array(n), k;
for (j = 1; j <= 19; ++j) {
@ -542,7 +544,7 @@ var CipherTransformFactory = (function() {
key[i++] = 0x6C;
key[i++] = 0x54;
}
var hash = md5(key, 0, i);
var hash = calculateMD5(key, 0, i);
return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));
}
@ -552,18 +554,18 @@ var CipherTransformFactory = (function() {
if (cryptFilter != null)
cfm = cryptFilter.get('CFM');
if (!cfm || cfm.name == 'None') {
return function() {
return function cipherTransformFactoryBuildCipherConstructorNone() {
return new NullCipher();
};
}
if ('V2' == cfm.name) {
return function() {
return function cipherTransformFactoryBuildCipherConstructorV2() {
return new ARCFourCipher(
buildObjectKey(num, gen, key, false));
};
}
if ('AESV2' == cfm.name) {
return function() {
return function cipherTransformFactoryBuildCipherConstructorAESV2() {
return new AES128Cipher(
buildObjectKey(num, gen, key, true));
};
@ -573,7 +575,8 @@ var CipherTransformFactory = (function() {
}
constructor.prototype = {
createCipherTransform: function(num, gen) {
createCipherTransform: function buildCipherCreateCipherTransform(num,
gen) {
if (this.algorithm == 4) {
return new CipherTransform(
buildCipherConstructor(this.cf, this.stmf,
@ -583,7 +586,7 @@ var CipherTransformFactory = (function() {
}
// algorithms 1 and 2
var key = buildObjectKey(num, gen, this.encryptionKey, false);
var cipherConstructor = function() {
var cipherConstructor = function buildCipherCipherConstructor() {
return new ARCFourCipher(key);
};
return new CipherTransform(cipherConstructor, cipherConstructor);

159
fonts.js
View File

@ -124,7 +124,7 @@ var serifFonts = {
var FontLoader = {
listeningForFontLoad: false,
bind: function(fonts, callback) {
bind: function fontLoaderBind(fonts, callback) {
function checkFontsLoaded() {
for (var i = 0; i < objs.length; i++) {
var fontObj = objs[i];
@ -180,7 +180,8 @@ var FontLoader = {
// loaded in a subdocument. It's expected that the load of |rules|
// has already started in this (outer) document, so that they should
// be ordered before the load in the subdocument.
prepareFontLoadEvent: function(rules, names, objs) {
prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules, names,
objs) {
/** Hack begin */
// There's no event when a font has finished downloading so the
// following code is a dirty hack to 'guess' when a font is
@ -219,7 +220,7 @@ var FontLoader = {
if (!this.listeningForFontLoad) {
window.addEventListener(
'message',
function(e) {
function fontLoaderMessage(e) {
var fontNames = JSON.parse(e.data);
for (var i = 0; i < objs.length; ++i) {
var font = objs[i];
@ -247,7 +248,7 @@ var FontLoader = {
fontNamesArray += '"' + names[i] + '", ';
}
src += ' var fontNames=[' + fontNamesArray + '];\n';
src += ' window.onload = function () {\n';
src += ' window.onload = function fontLoaderOnload() {\n';
src += ' parent.postMessage(JSON.stringify(fontNames), "*");\n';
src += ' }';
src += '</script></head><body>';
@ -447,13 +448,14 @@ var Font = (function Font() {
}
var data;
switch (properties.type) {
var type = properties.type;
switch (type) {
case 'Type1':
case 'CIDFontType0':
this.mimetype = 'font/opentype';
var subtype = properties.subtype;
var cff = (subtype === 'Type1C') ?
var cff = (subtype == 'Type1C' || subtype == 'CIDFontType0C') ?
new Type2CFF(file, properties) : new CFF(name, file, properties);
// Wrap the CFF data inside an OTF font file
@ -475,7 +477,7 @@ var Font = (function Font() {
}
this.data = data;
this.type = properties.type;
this.type = type;
this.textMatrix = properties.textMatrix;
this.defaultWidth = properties.defaultWidth;
this.loadedName = getUniqueName();
@ -598,7 +600,7 @@ var Font = (function Font() {
var length = glyphs.length;
for (var n = 0; n < length; ++n)
codes.push({ unicode: glyphs[n].unicode, code: n });
codes.sort(function(a, b) {
codes.sort(function fontGetRangesSort(a, b) {
return a.unicode - b.unicode;
});
@ -927,7 +929,7 @@ var Font = (function Font() {
}
// Check that table are sorted by platformID then encodingID,
records.sort(function(a, b) {
records.sort(function fontReplaceCMapTableSort(a, b) {
return ((a.platformID << 16) + a.encodingID) -
((b.platformID << 16) + b.encodingID);
});
@ -1060,11 +1062,11 @@ var Font = (function Font() {
var itemSize, itemDecode, itemEncode;
if (isGlyphLocationsLong) {
itemSize = 4;
itemDecode = function(data, offset) {
itemDecode = function fontItemDecodeLong(data, offset) {
return (data[offset] << 24) | (data[offset + 1] << 16) |
(data[offset + 2] << 8) | data[offset + 3];
};
itemEncode = function(data, offset, value) {
itemEncode = function fontItemEncodeLong(data, offset, value) {
data[offset] = (value >>> 24) & 0xFF;
data[offset + 1] = (value >> 16) & 0xFF;
data[offset + 2] = (value >> 8) & 0xFF;
@ -1072,10 +1074,10 @@ var Font = (function Font() {
};
} else {
itemSize = 2;
itemDecode = function(data, offset) {
itemDecode = function fontItemDecode(data, offset) {
return (data[offset] << 9) | (data[offset + 1] << 1);
};
itemEncode = function(data, offset, value) {
itemEncode = function fontItemEncode(data, offset, value) {
data[offset] = (value >> 9) & 0xFF;
data[offset + 1] = (value >> 1) & 0xFF;
};
@ -1322,7 +1324,7 @@ var Font = (function Font() {
'cmap': createCMapTable(charstrings.slice(), font.glyphIds),
// Font header
'head': (function() {
'head': (function fontFieldsHead() {
return stringToArray(
'\x00\x01\x00\x00' + // Version number
'\x00\x00\x10\x00' + // fontRevision
@ -1344,7 +1346,7 @@ var Font = (function Font() {
})(),
// Horizontal header
'hhea': (function() {
'hhea': (function fontFieldsHhea() {
return stringToArray(
'\x00\x01\x00\x00' + // Version number
string16(properties.ascent) + // Typographic Ascent
@ -1367,7 +1369,7 @@ var Font = (function Font() {
})(),
// Horizontal metrics
'hmtx': (function() {
'hmtx': (function fontFieldsHmtx() {
var hmtx = '\x00\x00\x00\x00'; // Fake .notdef
for (var i = 0; i < charstrings.length; i++) {
hmtx += string16(charstrings[i].width) + string16(0);
@ -1376,7 +1378,7 @@ var Font = (function Font() {
})(),
// Maximum profile
'maxp': (function() {
'maxp': (function fontFieldsMaxp() {
return stringToArray(
'\x00\x00\x50\x00' + // Version number
string16(charstrings.length + 1)); // Num of glyphs
@ -1504,7 +1506,7 @@ var Font = (function Font() {
* program. Some of its logic depends on the Type2 charstrings
* structure.
*/
var Type1Parser = function() {
var Type1Parser = function type1Parser() {
/*
* Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
* of Plaintext Bytes. The function took a key as a parameter which can be
@ -2032,7 +2034,7 @@ var CFFStrings = [
var type1Parser = new Type1Parser();
var CFF = function(name, file, properties) {
var CFF = function cFF(name, file, properties) {
// Get the data block containing glyphs and subrs informations
var headerBlock = file.getBytes(properties.length1);
type1Parser.extractFontHeader(headerBlock, properties);
@ -2232,7 +2234,7 @@ CFF.prototype = {
'names': this.createCFFIndexHeader([name]),
'topDict': (function topDict(self) {
return function() {
return function cFFWrapTopDict() {
var header = '\x00\x01\x01\x01';
var dict =
'\xf8\x1b\x00' + // version
@ -2309,7 +2311,7 @@ CFF.prototype = {
'charstrings': this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs),
true),
'private': (function(self) {
'private': (function cFFWrapPrivate(self) {
var data =
'\x8b\x14' + // defaultWidth
'\x8b\x15'; // nominalWidth
@ -2362,7 +2364,7 @@ CFF.prototype = {
}
};
var Type2CFF = (function() {
var Type2CFF = (function type2CFF() {
// TODO: replace parsing code with the Type2Parser in font_utils.js
function constructor(file, properties) {
var bytes = file.getBytes();
@ -2387,16 +2389,21 @@ var Type2CFF = (function() {
var strings = this.getStrings(stringIndex);
var baseDict = this.parseDict(dictIndex.get(0));
var baseDict = this.parseDict(dictIndex.get(0).data);
var topDict = this.getTopDict(baseDict, strings);
var bytes = this.bytes;
var privateDict = {};
var privateInfo = topDict.Private;
if (privateInfo) {
var privOffset = privateInfo[1], privLength = privateInfo[0];
var privBytes = bytes.subarray(privOffset, privOffset + privLength);
baseDict = this.parseDict(privBytes);
var privDict = this.getPrivDict(baseDict, strings);
privateDict = this.getPrivDict(baseDict, strings);
} else {
privateDict.defaultWidthX = properties.defaultWidth;
}
var charStrings = this.parseIndex(topDict.CharStrings);
var charset = this.parseCharsets(topDict.charset,
@ -2412,10 +2419,37 @@ var Type2CFF = (function() {
if (hasSupplement)
bytes[topDict.Encoding] = 0;
// The CFF specification state that the 'dotsection' command
// (12, 0) is deprecated and treated as a no-op, but all Type2
// charstrings processors should support them. Unfortunately
// the font sanitizer don't. As a workaround the sequence (12, 0)
// is replaced by a useless (0, hmoveto).
var count = charStrings.length;
for (var i = 0; i < count; i++) {
var charstring = charStrings.get(i);
var start = charstring.start;
var data = charstring.data;
var length = data.length;
for (var j = 0; j <= length; j) {
var value = data[j++];
if (value == 12 && data[j++] == 0) {
bytes[start + j - 2] = 139;
bytes[start + j - 1] = 22;
} else if (value === 28) {
j += 2;
} else if (value >= 247 && value <= 254) {
j++;
} else if (value == 255) {
j += 4;
}
}
}
// charstrings contains info about glyphs (one element per glyph
// containing mappings for {unicode, width})
var charstrings = this.getCharStrings(charset, charStrings,
privDict, this.properties);
privateDict, this.properties);
// create the mapping between charstring and glyph id
var glyphIds = [];
@ -2432,10 +2466,8 @@ var Type2CFF = (function() {
},
getCharStrings: function cff_charstrings(charsets, charStrings,
privDict, properties) {
var defaultWidth = privDict['defaultWidthX'];
var nominalWidth = privDict['nominalWidthX'];
privateDict, properties) {
var defaultWidth = privateDict['defaultWidthX'];
var charstrings = [];
var differences = properties.differences;
var index = 0;
@ -2472,7 +2504,9 @@ var Type2CFF = (function() {
}
// sort the array by the unicode value
charstrings.sort(function(a, b) {return a.unicode - b.unicode});
charstrings.sort(function type2CFFGetCharStringsSort(a, b) {
return a.unicode - b.unicode;
});
return charstrings;
},
@ -2492,8 +2526,8 @@ var Type2CFF = (function() {
if (pos == 0 || pos == 1) {
var gid = 1;
var baseEncoding =
pos ? Encodings.ExpertEncoding : Encodings.StandardEncoding;
var baseEncoding = pos ? Encodings.ExpertEncoding.slice() :
Encodings.StandardEncoding.slice();
for (var i = 0; i < charset.length; i++) {
var index = baseEncoding.indexOf(charset[i]);
if (index != -1)
@ -2538,37 +2572,42 @@ var Type2CFF = (function() {
},
parseCharsets: function cff_parsecharsets(pos, length, strings) {
if (pos == 0) {
return ISOAdobeCharset.slice();
} else if (pos == 1) {
return ExpertCharset.slice();
} else if (pos == 2) {
return ExpertSubsetCharset.slice();
}
var bytes = this.bytes;
var format = bytes[pos++];
var charset = ['.notdef'];
// subtract 1 for the .notdef glyph
length -= 1;
switch (format) {
case 0:
for (var i = 0; i < length; ++i) {
var id = bytes[pos++];
id = (id << 8) | bytes[pos++];
charset.push(strings[id]);
for (var i = 0; i < length; i++) {
var sid = (bytes[pos++] << 8) | bytes[pos++];
charset.push(strings[sid]);
}
break;
case 1:
while (charset.length <= length) {
var first = bytes[pos++];
first = (first << 8) | bytes[pos++];
var numLeft = bytes[pos++];
for (var i = 0; i <= numLeft; ++i)
charset.push(strings[first++]);
var sid = (bytes[pos++] << 8) | bytes[pos++];
var count = bytes[pos++];
for (var i = 0; i <= count; i++)
charset.push(strings[sid++]);
}
break;
case 2:
while (charset.length <= length) {
var first = bytes[pos++];
first = (first << 8) | bytes[pos++];
var numLeft = bytes[pos++];
numLeft = (numLeft << 8) | bytes[pos++];
for (var i = 0; i <= numLeft; ++i)
charset.push(strings[first++]);
var sid = (bytes[pos++] << 8) | bytes[pos++];
var count = (bytes[pos++] << 8) | bytes[pos++];
for (var i = 0; i <= count; i++)
charset.push(strings[sid++]);
}
break;
default:
@ -2643,20 +2682,20 @@ var Type2CFF = (function() {
}
return dict;
},
getStrings: function cff_getstrings(stringIndex) {
function bytesToString(bytesArr) {
var s = '';
for (var i = 0, ii = bytesArr.length; i < ii; ++i)
s += String.fromCharCode(bytesArr[i]);
return s;
getStrings: function cff_getStrings(stringIndex) {
function bytesToString(bytesArray) {
var str = '';
for (var i = 0, length = bytesArray.length; i < length; i++)
str += String.fromCharCode(bytesArray[i]);
return str;
}
var stringArray = [];
for (var i = 0, ii = CFFStrings.length; i < ii; ++i)
for (var i = 0, length = CFFStrings.length; i < length; i++)
stringArray.push(CFFStrings[i]);
for (var i = 0, ii = stringIndex.length; i < ii; ++i)
stringArray.push(bytesToString(stringIndex.get(i)));
for (var i = 0, length = stringIndex.length; i < length; i++)
stringArray.push(bytesToString(stringIndex.get(i).data));
return stringArray;
},
@ -2702,7 +2741,7 @@ var Type2CFF = (function() {
} else if (value <= 254) {
return -((value - 251) * 256) - dict[pos++] - 108;
} else {
error('Incorrect byte');
error('255 is not a valid DICT command');
}
return -1;
}
@ -2779,7 +2818,11 @@ var Type2CFF = (function() {
var start = offsets[index];
var end = offsets[index + 1];
return bytes.subarray(start, end);
return {
start: start,
end: end,
data: bytes.subarray(start, end)
};
},
length: count,
endPos: end

676
pdf.js

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ function load() {
var r = new XMLHttpRequest();
r.open('GET', manifestFile, false);
r.onreadystatechange = function(e) {
r.onreadystatechange = function loadOnreadystatechange(e) {
if (r.readyState == 4) {
log('done\n');
manifest = JSON.parse(r.responseText);
@ -50,7 +50,21 @@ function load() {
r.send(null);
}
function cleanup() {
var styleSheet = document.styleSheets[0];
if (styleSheet) {
while (styleSheet.cssRules.length > 0)
styleSheet.deleteRule(0);
}
var guard = document.getElementById('content-end');
var body = document.body;
while (body.lastChild !== guard)
body.removeChild(body.lastChild);
}
function nextTask() {
cleanup();
if (currentTaskIdx == manifest.length) {
return done();
}
@ -62,7 +76,7 @@ function nextTask() {
var r = new XMLHttpRequest();
r.open('GET', task.file);
r.mozResponseType = r.responseType = 'arraybuffer';
r.onreadystatechange = function() {
r.onreadystatechange = function nextTaskOnreadystatechange() {
var failure;
if (r.readyState == 4) {
var data = r.mozResponseArrayBuffer || r.mozResponse ||
@ -85,11 +99,15 @@ function isLastPage(task) {
return (task.pageNum > task.pdfDoc.numPages);
}
function canvasToDataURL() {
return canvas.toDataURL('image/png');
}
function nextPage(task, loadError) {
var failure = loadError || '';
if (!task.pdfDoc) {
sendTaskResult(canvas.toDataURL('image/png'), task, failure);
sendTaskResult(canvasToDataURL(), task, failure);
log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n');
++currentTaskIdx;
nextTask();
@ -126,7 +144,7 @@ function nextPage(task, loadError) {
page.startRendering(
ctx,
function(e) {
function nextPageStartRendering(e) {
snapshotCurrentPage(task, (!failure && e) ?
('render : ' + e) : failure);
}
@ -146,13 +164,13 @@ function nextPage(task, loadError) {
function snapshotCurrentPage(task, failure) {
log('done, snapshotting... ');
sendTaskResult(canvas.toDataURL('image/png'), task, failure);
sendTaskResult(canvasToDataURL(), task, failure);
log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n');
// Set up the next request
var backoff = (inFlightRequests > 0) ? inFlightRequests * 10 : 0;
setTimeout(
function() {
function snapshotCurrentPageSetTimeout() {
++task.pageNum;
nextPage(task);
},
@ -201,7 +219,7 @@ function sendTaskResult(snapshot, task, failure) {
// (The POST URI is ignored atm.)
r.open('POST', '/submit_task_results', true);
r.setRequestHeader('Content-Type', 'application/json');
r.onreadystatechange = function(e) {
r.onreadystatechange = function sendTaskResultOnreadystatechange(e) {
if (r.readyState == 4) {
inFlightRequests--;
}

105
test/pdfs/extgstate.pdf Normal file
View File

@ -0,0 +1,105 @@
%PDF-1.4
%öäüß
1 0 obj
<<
/Type /Catalog
/Version /1.4
/Pages 2 0 R
>>
endobj
2 0 obj
<<
/Type /Pages
/Kids [3 0 R]
/Count 1
>>
endobj
3 0 obj
<<
/Type /Page
/MediaBox [0 0 612 792]
/Resources 4 0 R
/Parent 2 0 R
/Contents 5 0 R
>>
endobj
4 0 obj
<<
/ExtGState 6 0 R
/Font 7 0 R
/XObject <<
>>
>>
endobj
5 0 obj
<<
/Length 8 0 R
>>
stream
/GS1 gs
/F0 12 Tf
BT
100 700 Td
(I should be courier!) Tj
ET
50 600 m
400 600 l
S
endstream
endobj
6 0 obj
<<
/GS1 9 0 R
>>
endobj
7 0 obj
<<
/F0 10 0 R
>>
endobj
8 0 obj
82
endobj
9 0 obj
<<
/Type /ExtGState
/LW 10
/LC 1
/LJ 2
/ML 0.3000000119
/D [[0.0917000026 183.3300018311]
0]
/Font [10 0 R 36]
>>
endobj
10 0 obj
<<
/Type /Font
/Subtype /Type1
/BaseFont /Courier
/Encoding /WinAnsiEncoding
>>
endobj
xref
0 11
0000000000 65535 f
0000000015 00000 n
0000000078 00000 n
0000000135 00000 n
0000000239 00000 n
0000000304 00000 n
0000000441 00000 n
0000000473 00000 n
0000000505 00000 n
0000000523 00000 n
0000000653 00000 n
trailer
<<
/Root 1 0 R
/ID [<BFFF29B7D1C75EC69AC080682C2AFC5B> <BFFF29B7D1C75EC69AC080682C2AFC5B>]
/Size 11
>>
startxref
749
%%EOF

1
test/pdfs/f1040.pdf.link Normal file
View File

@ -0,0 +1 @@
http://www.irs.gov/pub/irs-pdf/f1040.pdf

View File

@ -0,0 +1 @@
https://issues.apache.org/jira/secure/attachment/12421789/survey.pdf

View File

@ -139,5 +139,23 @@
"link": true,
"rounds": 1,
"type": "load"
},
{ "id": "f1040",
"file": "pdfs/f1040.pdf",
"link": true,
"rounds": 1,
"type": "load"
},
{ "id": "hudsonsurvey",
"file": "pdfs/hudsonsurvey.pdf",
"link": true,
"rounds": 1,
"type": "load"
},
{ "id": "extgstate",
"file": "pdfs/extgstate.pdf",
"link": false,
"rounds": 1,
"type": "load"
}
]

View File

@ -7,12 +7,14 @@
<script type="text/javascript" src="/crypto.js"></script>
<script type="text/javascript" src="/glyphlist.js"></script>
<script type="text/javascript" src="/metrics.js"></script>
<script type="text/javascript" src="/charsets.js"></script>
<script type="text/javascript" src="driver.js"></script>
</head>
<body onload="load();">
<pre style="width:800; height:800; overflow: scroll;"id="stdout"></pre>
<p>Inflight requests: <span id="inFlightCount"></span></p>
<div id="content-end"><!-- cleanup() guard --></div>
</body>
</html>

View File

@ -3,400 +3,6 @@
'use strict';
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',

View File

@ -20,17 +20,27 @@ function readCharset(aStream, aCharstrings) {
var charset = {};
var format = aStream.getByte();
var count = aCharstrings.length - 1;
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');
for (var i = 1; i < count + 1; i++) {
var first = aStream.getByte();
first = (first << 8) | aStream.getByte();
var numLeft = aStream.getByte();
for (var j = 0; j <= numLeft; j++) {
var sid = first++;
if (CFFStrings[sid] == 'three')
log(aCharstrings[j]);
charset[CFFStrings[sid]] = readCharstringEncoding(aCharstrings[j]);
}
}
} else {
error('Invalid charset format');
}
@ -44,6 +54,9 @@ function readCharset(aStream, aCharstrings) {
* chapter 3.1.
*/
function readCharstringEncoding(aString) {
if (!aString)
return '';
var charstringTokens = [];
var count = aString.length;
@ -71,9 +84,9 @@ function readCharstringEncoding(aString) {
} else if (value < 247) {
token = parseInt(value, 10) - 139;
} else if (value < 251) {
token = ((value - 247) * 256) + aString[i++] + 108;
token = (value - 247) * 256 + aString[i++] + 108;
} else if (value < 255) {
token = -((value - 251) * 256) - aString[i++] - 108;
token = -(value - 251) * 256 - aString[i++] - 108;
} else {// value == 255
token = aString[i++] << 24 | aString[i++] << 16 |
aString[i++] << 8 | aString[i];
@ -146,9 +159,9 @@ function readFontDictData(aString, aMap) {
} else if (value <= 246) {
token = parseInt(value, 10) - 139;
} else if (value <= 250) {
token = ((value - 247) * 256) + aString[i++] + 108;
token = (value - 247) * 256 + aString[i++] + 108;
} else if (value <= 254) {
token = -((value - 251) * 256) - aString[i++] - 108;
token = -(value - 251) * 256 - aString[i++] - 108;
} else if (value == 255) {
error('255 is not a valid DICT command');
}
@ -199,7 +212,7 @@ function readFontIndexData(aStream, aIsByte) {
for (var i = 0; i < count + 1; i++)
offsets.push(getNextOffset());
log('Found ' + count + ' objects at offsets :' +
dump('Found ' + count + ' objects at offsets :' +
offsets + ' (offsize: ' + offsize + ')');
// Now extract the objects
@ -285,23 +298,20 @@ var Type2Parser = function(aFilePath) {
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'));
dump('Names: ' + font.get('Names'));
// Read the Top Dict Index
dump('Reading Index: TopDict');
var topDict = readFontIndexData(aStream, true);
log('TopDict: ' + topDict);
dump('TopDict: ' + topDict);
// Read the String Index
dump('Reading Index: Strings');
var strings = readFontIndexData(aStream);
log('strings: ' + strings);
dump('strings: ' + strings);
// Fill up the Strings dictionary with the new unique strings
for (var i = 0; i < strings.length; i++)
@ -321,7 +331,7 @@ var Type2Parser = function(aFilePath) {
// Reading Private Dict
var priv = font.get('Private');
log('Reading Private Dict (offset: ' + priv.offset +
dump('Reading Private Dict (offset: ' + priv.offset +
' size: ' + priv.size + ')');
aStream.pos = priv.offset;

View File

@ -119,6 +119,7 @@ span#info {
margin-right:auto;
line-height: 134px;
text-align: center;
overflow: hidden;
}
.thumbnail:not([data-loaded]) {
@ -195,9 +196,6 @@ span#info {
canvas {
margin: auto;
display: block;
box-shadow: 0px 4px 10px #000;
-moz-box-shadow: 0px 4px 10px #000;
-webkit-box-shadow: 0px 4px 10px #000;
}
.page {
@ -205,6 +203,10 @@ canvas {
height: 1056px;
margin: 10px auto;
position: relative;
overflow: hidden;
box-shadow: 0px 4px 10px #000;
-moz-box-shadow: 0px 4px 10px #000;
-webkit-box-shadow: 0px 4px 10px #000;
}
.page > a {
@ -271,3 +273,8 @@ canvas {
page-break-after: always;
}
}
#loading {
margin: 100px 0;
text-align: center;
}

View File

@ -11,6 +11,7 @@
<script type="text/javascript" src="../crypto.js"></script>
<script type="text/javascript" src="../glyphlist.js"></script>
<script type="text/javascript" src="../metrics.js"></script>
<script type="text/javascript" src="../charsets.js"></script>
</head>
<body>
@ -90,6 +91,7 @@
</div>
</div>
<div id="loading">Loading... 0%</div>
<div id="viewer"></div>
</body>
</html>

View File

@ -111,12 +111,14 @@ var PDFView = {
xhr.open('GET', url);
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
xhr.onprogress = PDFView.progressLevel;
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === xhr.expected) {
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
xhr.responseArrayBuffer || xhr.response);
document.getElementById('loading').style.display = 'none';
PDFView.load(data, scale);
}
};
@ -124,6 +126,11 @@ var PDFView = {
xhr.send(null);
},
progressLevel: function(evt) {
var p = Math.round((evt.loaded / evt.total) * 100);
document.getElementById('loading').innerHTML = 'Loading... ' + p + '%';
},
navigateTo: function(dest) {
if (typeof dest === 'string')
dest = this.destinations[dest];
@ -163,7 +170,7 @@ var PDFView = {
var page = pdf.getPage(i);
pages.push(new PageView(container, page, i, page.width, page.height,
page.stats, this.navigateTo.bind(this)));
thumbnails.push(new ThumbnailView(sidebar, pages[i - 1],
thumbnails.push(new ThumbnailView(sidebar, page, i,
page.width / page.height));
var pageRef = page.ref;
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
@ -230,13 +237,17 @@ var PDFView = {
}
};
var PageView = function(container, content, id, width, height,
var PageView = function(container, content, id, pageWidth, pageHeight,
stats, navigateTo) {
this.width = width;
this.height = height;
this.id = id;
this.content = content;
var view = this.content.view;
this.x = view.x;
this.y = view.y;
this.width = view.width;
this.height = view.height;
var anchor = document.createElement('a');
anchor.name = '' + this.id;
@ -265,11 +276,12 @@ var PageView = function(container, content, id, width, height,
return false;
};
}
var links = content.getLinks();
for (var i = 0; i < links.length; i++) {
var link = document.createElement('a');
link.style.left = Math.floor(links[i].x * scale) + 'px';
link.style.top = Math.floor(links[i].y * scale) + 'px';
link.style.left = (Math.floor(links[i].x - view.x) * scale) + 'px';
link.style.top = (Math.floor(links[i].y - view.y) * scale) + 'px';
link.style.width = Math.ceil(links[i].width * scale) + 'px';
link.style.height = Math.ceil(links[i].height * scale) + 'px';
link.href = links[i].url || '';
@ -357,8 +369,9 @@ var PageView = function(container, content, id, width, height,
canvas.id = 'page' + this.id;
canvas.mozOpaque = true;
canvas.width = this.width * this.scale;
canvas.height = this.height * this.scale;
var scale = this.scale;
canvas.width = pageWidth * scale;
canvas.height = pageHeight * scale;
div.appendChild(canvas);
var ctx = canvas.getContext('2d');
@ -366,6 +379,7 @@ var PageView = function(container, content, id, width, height,
ctx.fillStyle = 'rgb(255, 255, 255)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
ctx.translate(-this.x * scale, -this.y * scale);
stats.begin = Date.now();
this.content.startRendering(ctx, this.updateStats);
@ -384,12 +398,12 @@ var PageView = function(container, content, id, width, height,
};
};
var ThumbnailView = function(container, page, pageRatio) {
var ThumbnailView = function(container, page, id, pageRatio) {
var anchor = document.createElement('a');
anchor.href = '#' + page.id;
anchor.href = '#' + id;
var div = document.createElement('div');
div.id = 'thumbnailContainer' + page.id;
div.id = 'thumbnailContainer' + id;
div.className = 'thumbnail';
anchor.appendChild(div);
@ -400,7 +414,7 @@ var ThumbnailView = function(container, page, pageRatio) {
return;
var canvas = document.createElement('canvas');
canvas.id = 'thumbnail' + page.id;
canvas.id = 'thumbnail' + id;
canvas.mozOpaque = true;
var maxThumbSize = 134;
@ -418,7 +432,15 @@ var ThumbnailView = function(container, page, pageRatio) {
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
page.content.startRendering(ctx, function() { });
var view = page.view;
var scaleX = (canvas.width / page.width);
var scaleY = (canvas.height / page.height);
ctx.translate(-view.x * scaleX, -view.y * scaleY);
div.style.width = (view.width * scaleX) + 'px';
div.style.height = (view.height * scaleY) + 'px';
div.style.lineHeight = (view.height * scaleY) + 'px';
page.startRendering(ctx, function() { });
};
};