diff --git a/Makefile b/Makefile index fb4ffe9cb..56b597e5f 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,8 @@ PDF_JS_FILES = \ pdf.js \ crypto.js \ fonts.js \ + metrics.js \ + charsets.js \ glyphlist.js \ $(NULL) diff --git a/README.md b/README.md index c6cf92ede..f5d4eee9d 100644 --- a/README.md +++ b/README.md @@ -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. + diff --git a/charsets.js b/charsets.js new file mode 100644 index 000000000..59fcdf5cf --- /dev/null +++ b/charsets.js @@ -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' +]; + diff --git a/crypto.js b/crypto.js index a91f9e41d..4eb6bb581 100644 --- a/crypto.js +++ b/crypto.js @@ -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 cipher.decryptBlock(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); diff --git a/fonts.js b/fonts.js index eeefa02f8..bc1ad5569 100644 --- a/fonts.js +++ b/fonts.js @@ -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 += '
'; @@ -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; - var privOffset = privateInfo[1], privLength = privateInfo[0]; - var privBytes = bytes.subarray(privOffset, privOffset + privLength); - baseDict = this.parseDict(privBytes); - var privDict = this.getPrivDict(baseDict, strings); + if (privateInfo) { + var privOffset = privateInfo[1], privLength = privateInfo[0]; + var privBytes = bytes.subarray(privOffset, privOffset + privLength); + baseDict = this.parseDict(privBytes); + 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 diff --git a/pdf.js b/pdf.js index 24066d7bf..ff76fc983 100644 --- a/pdf.js +++ b/pdf.js @@ -61,7 +61,7 @@ function shadow(obj, prop, value) { configurable: true, writable: false }); } catch (e) { - obj.__defineGetter__(prop, function() { + obj.__defineGetter__(prop, function shadowDefineGetter() { return value; }); } @@ -112,7 +112,7 @@ function stringToPDFString(str) { return str2; } -var Stream = (function() { +var Stream = (function streamStream() { function constructor(arrayBuffer, start, length, dict) { this.bytes = new Uint8Array(arrayBuffer); this.start = start || 0; @@ -178,7 +178,7 @@ var Stream = (function() { return constructor; })(); -var StringStream = (function() { +var StringStream = (function stringStream() { function constructor(str) { var length = str.length; var bytes = new Uint8Array(length); @@ -193,7 +193,7 @@ var StringStream = (function() { })(); // super class for the decoding streams -var DecodeStream = (function() { +var DecodeStream = (function decodeStream() { function constructor() { this.pos = 0; this.bufferLength = 0; @@ -289,21 +289,21 @@ var DecodeStream = (function() { return constructor; })(); -var FakeStream = (function() { +var FakeStream = (function fakeStream() { function constructor(stream) { this.dict = stream.dict; DecodeStream.call(this); } constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function fakeStreamReadBlock() { var bufferLength = this.bufferLength; bufferLength += 1024; var buffer = this.ensureBuffer(bufferLength); this.bufferLength = bufferLength; }; - constructor.prototype.getBytes = function(length) { + constructor.prototype.getBytes = function fakeStreamGetBytes(length) { var end, pos = this.pos; if (length) { @@ -328,7 +328,7 @@ var FakeStream = (function() { return constructor; })(); -var StreamsSequenceStream = (function() { +var StreamsSequenceStream = (function streamSequenceStream() { function constructor(streams) { this.streams = streams; DecodeStream.call(this); @@ -336,7 +336,7 @@ var StreamsSequenceStream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function streamSequenceStreamReadBlock() { var streams = this.streams; if (streams.length == 0) { this.eof = true; @@ -354,7 +354,7 @@ var StreamsSequenceStream = (function() { return constructor; })(); -var FlateStream = (function() { +var FlateStream = (function flateStream() { var codeLenCodeMap = new Uint32Array([ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ]); @@ -474,7 +474,7 @@ var FlateStream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.getBits = function(bits) { + constructor.prototype.getBits = function flateStreamGetBits(bits) { var codeSize = this.codeSize; var codeBuf = this.codeBuf; var bytes = this.bytes; @@ -494,7 +494,7 @@ var FlateStream = (function() { return b; }; - constructor.prototype.getCode = function(table) { + constructor.prototype.getCode = function flateStreamGetCode(table) { var codes = table[0]; var maxLen = table[1]; var codeSize = this.codeSize; @@ -520,7 +520,8 @@ var FlateStream = (function() { return codeVal; }; - constructor.prototype.generateHuffmanTable = function(lengths) { + constructor.prototype.generateHuffmanTable = + function flateStreamGenerateHuffmanTable(lengths) { var n = lengths.length; // find max code length @@ -558,7 +559,7 @@ var FlateStream = (function() { return [codes, maxLen]; }; - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function flateStreamReadBlock() { // read block header var hdr = this.getBits(3); if (hdr & 1) @@ -692,7 +693,7 @@ var FlateStream = (function() { return constructor; })(); -var PredictorStream = (function() { +var PredictorStream = (function predictorStream() { function constructor(stream, params) { var predictor = this.predictor = params.get('Predictor') || 1; @@ -722,7 +723,8 @@ var PredictorStream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlockTiff = function() { + constructor.prototype.readBlockTiff = + function predictorStreamReadBlockTiff() { var rowBytes = this.rowBytes; var bufferLength = this.bufferLength; @@ -782,7 +784,7 @@ var PredictorStream = (function() { this.bufferLength += rowBytes; }; - constructor.prototype.readBlockPng = function() { + constructor.prototype.readBlockPng = function predictorStreamReadBlockPng() { var rowBytes = this.rowBytes; var pixBytes = this.pixBytes; @@ -864,7 +866,7 @@ var PredictorStream = (function() { // A JpegStream can't be read directly. We use the platform to render // the underlying JPEG data for us. -var JpegStream = (function() { +var JpegStream = (function jpegStream() { function isYcckImage(bytes) { var maxBytesScanned = Math.max(bytes.length - 16, 1024); // Looking for APP14, 'Adobe' and transform = 2 @@ -905,7 +907,7 @@ var JpegStream = (function() { // create DOM image var img = new Image(); - img.onload = (function() { + img.onload = (function jpegStreamOnload() { this.loaded = true; if (this.onLoad) this.onLoad(); @@ -915,10 +917,10 @@ var JpegStream = (function() { } constructor.prototype = { - getImage: function() { + getImage: function jpegStreamGetImage() { return this.domImage; }, - getChar: function() { + getChar: function jpegStreamGetChar() { error('internal error: getChar is not valid on JpegStream'); } }; @@ -930,31 +932,31 @@ var JpegStream = (function() { // Initialy for every that is in loading call imageLoading() // and, when images onload is fired, call imageLoaded() // When all images are loaded, the onLoad event is fired. -var ImagesLoader = (function() { +var ImagesLoader = (function imagesLoader() { function constructor() { this.loading = 0; } constructor.prototype = { - imageLoading: function() { + imageLoading: function imagesLoaderImageLoading() { ++this.loading; }, - imageLoaded: function() { + imageLoaded: function imagesLoaderImageLoaded() { if (--this.loading == 0 && this.onLoad) { this.onLoad(); delete this.onLoad; } }, - bind: function(jpegStream) { + bind: function imagesLoaderBind(jpegStream) { if (jpegStream.loaded) return; this.imageLoading(); jpegStream.onLoad = this.imageLoaded.bind(this); }, - notifyOnLoad: function(callback) { + notifyOnLoad: function imagesLoaderNotifyOnLoad(callback) { if (this.loading == 0) callback(); this.onLoad = callback; @@ -964,7 +966,7 @@ var ImagesLoader = (function() { return constructor; })(); -var DecryptStream = (function() { +var DecryptStream = (function decryptStream() { function constructor(str, decrypt) { this.str = str; this.dict = str.dict; @@ -977,7 +979,7 @@ var DecryptStream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function decryptStreamReadBlock() { var chunk = this.str.getBytes(chunkSize); if (!chunk || chunk.length == 0) { this.eof = true; @@ -997,7 +999,7 @@ var DecryptStream = (function() { return constructor; })(); -var Ascii85Stream = (function() { +var Ascii85Stream = (function ascii85Stream() { function constructor(str) { this.str = str; this.dict = str.dict; @@ -1008,7 +1010,7 @@ var Ascii85Stream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function ascii85StreamReadBlock() { var tildaCode = '~'.charCodeAt(0); var zCode = 'z'.charCodeAt(0); var str = this.str; @@ -1066,7 +1068,7 @@ var Ascii85Stream = (function() { return constructor; })(); -var AsciiHexStream = (function() { +var AsciiHexStream = (function asciiHexStream() { function constructor(str) { this.str = str; this.dict = str.dict; @@ -1103,7 +1105,7 @@ var AsciiHexStream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function asciiHexStreamReadBlock() { var gtCode = '>'.charCodeAt(0), bytes = this.str.getBytes(), c, n, decodeLength, buffer, bufferLength, i, length; @@ -1136,7 +1138,7 @@ var AsciiHexStream = (function() { return constructor; })(); -var CCITTFaxStream = (function() { +var CCITTFaxStream = (function cCITTFaxStream() { var ccittEOL = -2; var twoDimPass = 0; @@ -1611,7 +1613,7 @@ var CCITTFaxStream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function cCITTFaxStreamReadBlock() { while (!this.eof) { var c = this.lookChar(); this.buf = EOF; @@ -1620,7 +1622,8 @@ var CCITTFaxStream = (function() { } }; - constructor.prototype.addPixels = function(a1, blackPixels) { + constructor.prototype.addPixels = + function cCITTFaxStreamAddPixels(a1, blackPixels) { var codingLine = this.codingLine; var codingPos = this.codingPos; @@ -1639,7 +1642,8 @@ var CCITTFaxStream = (function() { this.codingPos = codingPos; }; - constructor.prototype.addPixelsNeg = function(a1, blackPixels) { + constructor.prototype.addPixelsNeg = + function cCITTFaxStreamAddPixelsNeg(a1, blackPixels) { var codingLine = this.codingLine; var codingPos = this.codingPos; @@ -1667,7 +1671,7 @@ var CCITTFaxStream = (function() { this.codingPos = codingPos; }; - constructor.prototype.lookChar = function() { + constructor.prototype.lookChar = function cCITTFaxStreamLookChar() { if (this.buf != EOF) return this.buf; @@ -1959,7 +1963,7 @@ var CCITTFaxStream = (function() { return this.buf; }; - constructor.prototype.getTwoDimCode = function() { + constructor.prototype.getTwoDimCode = function cCITTFaxStreamGetTwoDimCode() { var code = 0; var p; if (this.eoblock) { @@ -1986,7 +1990,7 @@ var CCITTFaxStream = (function() { return EOF; }; - constructor.prototype.getWhiteCode = function() { + constructor.prototype.getWhiteCode = function cCITTFaxStreamGetWhiteCode() { var code = 0; var p; var n; @@ -2036,7 +2040,7 @@ var CCITTFaxStream = (function() { return 1; }; - constructor.prototype.getBlackCode = function() { + constructor.prototype.getBlackCode = function cCITTFaxStreamGetBlackCode() { var code, p; if (this.eoblock) { code = this.lookBits(13); @@ -2099,7 +2103,7 @@ var CCITTFaxStream = (function() { return 1; }; - constructor.prototype.lookBits = function(n) { + constructor.prototype.lookBits = function cCITTFaxStreamLookBits(n) { var c; while (this.inputBits < n) { if ((c = this.str.getByte()) == null) { @@ -2114,7 +2118,7 @@ var CCITTFaxStream = (function() { return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n)); }; - constructor.prototype.eatBits = function(n) { + constructor.prototype.eatBits = function cCITTFaxStreamEatBits(n) { if ((this.inputBits -= n) < 0) this.inputBits = 0; }; @@ -2122,7 +2126,7 @@ var CCITTFaxStream = (function() { return constructor; })(); -var LZWStream = (function() { +var LZWStream = (function lZWStream() { function constructor(str, earlyChange) { this.str = str; this.dict = str.dict; @@ -2151,7 +2155,7 @@ var LZWStream = (function() { constructor.prototype = Object.create(DecodeStream.prototype); - constructor.prototype.readBits = function(n) { + constructor.prototype.readBits = function lZWStreamReadBits(n) { var bitsCached = this.bitsCached; var cachedData = this.cachedData; while (bitsCached < n) { @@ -2169,7 +2173,7 @@ var LZWStream = (function() { return (cachedData >>> bitsCached) & ((1 << n) - 1); }; - constructor.prototype.readBlock = function() { + constructor.prototype.readBlock = function lZWStreamReadBlock() { var blockSize = 512; var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize; var i, j, q; @@ -2252,7 +2256,7 @@ var LZWStream = (function() { })(); -var Name = (function() { +var Name = (function nameName() { function constructor(name) { this.name = name; } @@ -2263,7 +2267,7 @@ var Name = (function() { return constructor; })(); -var Cmd = (function() { +var Cmd = (function cmdCmd() { function constructor(cmd) { this.cmd = cmd; } @@ -2274,13 +2278,13 @@ var Cmd = (function() { return constructor; })(); -var Dict = (function() { +var Dict = (function dictDict() { function constructor() { this.map = Object.create(null); } constructor.prototype = { - get: function(key1, key2, key3) { + get: function dictGet(key1, key2, key3) { var value; if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map || typeof key2 == 'undefined') { @@ -2294,15 +2298,15 @@ var Dict = (function() { return this.map[key3] || null; }, - set: function(key, value) { + set: function dictSet(key, value) { this.map[key] = value; }, - has: function(key) { + has: function dictHas(key) { return key in this.map; }, - forEach: function(callback) { + forEach: function dictForEach(callback) { for (var key in this.map) { callback(key, this.map[key]); } @@ -2312,7 +2316,7 @@ var Dict = (function() { return constructor; })(); -var Ref = (function() { +var Ref = (function refRef() { function constructor(num, gen) { this.num = num; this.gen = gen; @@ -2326,17 +2330,17 @@ var Ref = (function() { // The reference is identified by number and generation, // this structure stores only one instance of the reference. -var RefSet = (function() { +var RefSet = (function refSet() { function constructor() { this.dict = {}; } constructor.prototype = { - has: function(ref) { + has: function refSetHas(ref) { return !!this.dict['R' + ref.num + '.' + ref.gen]; }, - put: function(ref) { + put: function refSetPut(ref) { this.dict['R' + ref.num + '.' + ref.gen] = ref; } }; @@ -2413,12 +2417,12 @@ function IsNone(v) { return v == None; } -var Lexer = (function() { +var Lexer = (function lexer() { function constructor(stream) { this.stream = stream; } - constructor.isSpace = function(ch) { + constructor.isSpace = function lexerIsSpace(ch) { return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a'; }; @@ -2453,7 +2457,7 @@ var Lexer = (function() { } constructor.prototype = { - getNumber: function(ch) { + getNumber: function lexerGetNumber(ch) { var floating = false; var str = ch; var stream = this.stream; @@ -2481,7 +2485,7 @@ var Lexer = (function() { error('Invalid floating point number: ' + value); return value; }, - getString: function() { + getString: function lexerGetString() { var numParen = 1; var done = false; var str = ''; @@ -2565,7 +2569,7 @@ var Lexer = (function() { } while (!done); return str; }, - getName: function(ch) { + getName: function lexerGetName(ch) { var str = ''; var stream = this.stream; while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) { @@ -2592,7 +2596,7 @@ var Lexer = (function() { str.length); return new Name(str); }, - getHexString: function(ch) { + getHexString: function lexerGetHexString(ch) { var str = ''; var stream = this.stream; for (;;) { @@ -2621,7 +2625,7 @@ var Lexer = (function() { } return str; }, - getObj: function() { + getObj: function lexerGetObj() { // skip whitespace and comments var comment = false; var stream = this.stream; @@ -2696,7 +2700,7 @@ var Lexer = (function() { return null; return new Cmd(str); }, - skipToNextLine: function() { + skipToNextLine: function lexerSkipToNextLine() { var stream = this.stream; while (true) { var ch = stream.getChar(); @@ -2709,7 +2713,7 @@ var Lexer = (function() { } } }, - skip: function() { + skip: function lexerSkip() { this.stream.skip(); } }; @@ -2717,7 +2721,7 @@ var Lexer = (function() { return constructor; })(); -var Parser = (function() { +var Parser = (function parserParser() { function constructor(lexer, allowStreams, xref) { this.lexer = lexer; this.allowStreams = allowStreams; @@ -2727,11 +2731,11 @@ var Parser = (function() { } constructor.prototype = { - refill: function() { + refill: function parserRefill() { this.buf1 = this.lexer.getObj(); this.buf2 = this.lexer.getObj(); }, - shift: function() { + shift: function parserShift() { if (IsCmd(this.buf2, 'ID')) { this.buf1 = this.buf2; this.buf2 = null; @@ -2742,7 +2746,7 @@ var Parser = (function() { this.buf2 = this.lexer.getObj(); } }, - getObj: function(cipherTransform) { + getObj: function parserGetObj(cipherTransform) { if (IsCmd(this.buf1, 'BI')) { // inline image this.shift(); return this.makeInlineImage(cipherTransform); @@ -2804,7 +2808,7 @@ var Parser = (function() { this.shift(); return obj; }, - makeInlineImage: function(cipherTransform) { + makeInlineImage: function parserMakeInlineImage(cipherTransform) { var lexer = this.lexer; var stream = lexer.stream; @@ -2844,7 +2848,7 @@ var Parser = (function() { return imageStream; }, - makeStream: function(dict, cipherTransform) { + makeStream: function parserMakeStream(dict, cipherTransform) { var lexer = this.lexer; var stream = lexer.stream; @@ -2877,7 +2881,7 @@ var Parser = (function() { stream.parameters = dict; return stream; }, - filter: function(stream, dict, length) { + filter: function parserFilter(stream, dict, length) { var filter = dict.get('Filter', 'F'); var params = dict.get('DecodeParms', 'DP'); if (IsName(filter)) @@ -2901,7 +2905,7 @@ var Parser = (function() { } return stream; }, - makeFilter: function(stream, name, length, params) { + makeFilter: function parserMakeFilter(stream, name, length, params) { if (name == 'FlateDecode' || name == 'Fl') { if (params) { return new PredictorStream(new FlateStream(stream), params); @@ -2935,7 +2939,7 @@ var Parser = (function() { return constructor; })(); -var Linearization = (function() { +var Linearization = (function linearizationLinearization() { function constructor(stream) { this.parser = new Parser(new Lexer(stream), false); var obj1 = this.parser.getObj(); @@ -2951,7 +2955,7 @@ var Linearization = (function() { } constructor.prototype = { - getInt: function(name) { + getInt: function linearizationGetInt(name) { var linDict = this.linDict; var obj; if (IsDict(linDict) && @@ -2962,7 +2966,7 @@ var Linearization = (function() { error('"' + name + '" field in linearization table is invalid'); return 0; }, - getHint: function(index) { + getHint: function linearizationGetHint(index) { var linDict = this.linDict; var obj1, obj2; if (IsDict(linDict) && @@ -3012,7 +3016,7 @@ var Linearization = (function() { return constructor; })(); -var XRef = (function() { +var XRef = (function xRefXRef() { function constructor(stream, startXRef, mainXRefEntriesOffset) { this.stream = stream; this.entries = []; @@ -3098,10 +3102,11 @@ var XRef = (function() { // check for 'XRefStm' key if (IsInt(obj = dict.get('XRefStm'))) { var pos = obj; - if (pos in this.xrefstms) - error('Invalid XRef table'); - this.xrefstms[pos] = 1; // avoid infinite recursion - this.readXRef(pos); + // ignore previously loaded xref streams (possible infinite recursion) + if (!(pos in this.xrefstms)) { + this.xrefstms[pos] = 1; + this.readXRef(pos); + } } return dict; @@ -3180,18 +3185,18 @@ var XRef = (function() { error('Invalid XRef'); return null; }, - getEntry: function(i) { + getEntry: function xRefGetEntry(i) { var e = this.entries[i]; if (e.free) error('reading an XRef stream not implemented yet'); return e; }, - fetchIfRef: function(obj) { + fetchIfRef: function xRefFetchIfRef(obj) { if (!IsRef(obj)) return obj; return this.fetch(obj); }, - fetch: function(ref, suppressEncryption) { + fetch: function xRefFetch(ref, suppressEncryption) { var num = ref.num; var e = this.cache[num]; if (e) @@ -3274,7 +3279,7 @@ var XRef = (function() { } return e; }, - getCatalogObj: function() { + getCatalogObj: function xRefGetCatalogObj() { return this.fetch(this.root); } }; @@ -3282,7 +3287,7 @@ var XRef = (function() { return constructor; })(); -var Page = (function() { +var Page = (function pagePage() { function constructor(xref, pageNumber, pageDict, ref) { this.pageNumber = pageNumber; this.pageDict = pageDict; @@ -3298,10 +3303,10 @@ var Page = (function() { } constructor.prototype = { - getPageProp: function(key) { + getPageProp: function pageGetPageProp(key) { return this.xref.fetchIfRef(this.pageDict.get(key)); }, - inheritPageProp: function(key) { + inheritPageProp: function pageInheritPageProp(key) { var dict = this.pageDict; var obj = dict.get(key); while (obj === undefined) { @@ -3320,8 +3325,35 @@ var Page = (function() { }, get mediaBox() { var obj = this.inheritPageProp('MediaBox'); - return shadow(this, 'mediaBox', - ((IsArray(obj) && obj.length == 4) ? obj : null)); + // Reset invalid media box to letter size. + if (!IsArray(obj) || obj.length !== 4) + obj = [0, 0, 612, 792]; + return shadow(this, 'mediaBox', obj); + }, + get view() { + var obj = this.inheritPageProp('CropBox'); + var view = { + x: 0, + y: 0, + width: this.width, + height: this.height + }; + if (IsArray(obj) && obj.length == 4) { + var rotate = this.rotate; + if (rotate == 0 || rotate == 180) { + view.x = obj[0]; + view.y = obj[1]; + view.width = obj[2] - view.x; + view.height = obj[3] - view.y; + } else { + view.x = obj[1]; + view.y = obj[0]; + view.width = obj[3] - view.x; + view.height = obj[2] - view.y; + } + } + + return shadow(this, 'cropBox', view); }, get annotations() { return shadow(this, 'annotations', this.inheritPageProp('Annots')); @@ -3362,7 +3394,7 @@ var Page = (function() { } return shadow(this, 'rotate', rotate); }, - startRendering: function(canvasCtx, continuation) { + startRendering: function pageStartRendering(canvasCtx, continuation) { var self = this; var stats = self.stats; stats.compile = stats.fonts = stats.render = 0; @@ -3374,10 +3406,10 @@ var Page = (function() { this.compile(gfx, fonts, images); stats.compile = Date.now(); - var displayContinuation = function() { + var displayContinuation = function pageDisplayContinuation() { // Always defer call to display() to work around bug in // Firefox error reporting from XHR callbacks. - setTimeout(function() { + setTimeout(function pageSetTimeout() { var exc = null; try { self.display(gfx); @@ -3391,9 +3423,9 @@ var Page = (function() { var fontObjs = FontLoader.bind( fonts, - function() { + function pageFontObjs() { stats.fonts = Date.now(); - images.notifyOnLoad(function() { + images.notifyOnLoad(function pageNotifyOnLoad() { stats.images = Date.now(); displayContinuation(); }); @@ -3404,7 +3436,7 @@ var Page = (function() { }, - compile: function(gfx, fonts, images) { + compile: function pageCompile(gfx, fonts, images) { if (this.code) { // content was compiled return; @@ -3422,7 +3454,7 @@ var Page = (function() { } this.code = gfx.compile(content, xref, resources, fonts, images); }, - display: function(gfx) { + display: function pageDisplay(gfx) { assert(this.code instanceof Function, 'page content must be compiled first'); var xref = this.xref; @@ -3436,7 +3468,7 @@ var Page = (function() { gfx.execute(this.code, xref, resources); gfx.endDrawing(); }, - rotatePoint: function(x, y) { + rotatePoint: function pageRotatePoint(x, y) { var rotate = this.rotate; switch (rotate) { case 180: @@ -3450,7 +3482,7 @@ var Page = (function() { return {x: x, y: this.height - y}; } }, - getLinks: function() { + getLinks: function pageGetLinks() { var xref = this.xref; var annotations = xref.fetchIfRef(this.annotations) || []; var i, n = annotations.length; @@ -3497,7 +3529,7 @@ var Page = (function() { return constructor; })(); -var Catalog = (function() { +var Catalog = (function catalogCatalog() { function constructor(xref) { this.xref = xref; var obj = xref.getCatalogObj(); @@ -3575,7 +3607,7 @@ var Catalog = (function() { // shadow the prototype getter return shadow(this, 'num', obj); }, - traverseKids: function(pagesDict) { + traverseKids: function catalogTraverseKids(pagesDict) { var pageCache = this.pageCache; var kids = pagesDict.get('Kids'); assertWellFormed(IsArray(kids), @@ -3613,7 +3645,7 @@ var Catalog = (function() { if (nameDictionaryRef) { // reading simple destination dictionary obj = xref.fetchIfRef(nameDictionaryRef); - obj.forEach(function(key, value) { + obj.forEach(function catalogForEach(key, value) { if (!value) return; dests[key] = fetchDestination(xref, value); }); @@ -3645,7 +3677,7 @@ var Catalog = (function() { } return shadow(this, 'destinations', dests); }, - getPage: function(n) { + getPage: function catalogGetPage(n) { var pageCache = this.pageCache; if (!pageCache) { pageCache = this.pageCache = []; @@ -3658,7 +3690,7 @@ var Catalog = (function() { return constructor; })(); -var PDFDoc = (function() { +var PDFDoc = (function pDFDoc() { function constructor(stream) { assertWellFormed(stream.length > 0, 'stream must have data'); this.stream = stream; @@ -3737,7 +3769,7 @@ var PDFDoc = (function() { }, // Find the header, remove leading garbage and setup the stream // starting from the header. - checkHeader: function() { + checkHeader: function pDFDocCheckHeader() { var stream = this.stream; stream.reset(); if (find(stream, '%PDF-', 1024)) { @@ -3747,7 +3779,7 @@ var PDFDoc = (function() { } // May not be a PDF file, continue anyway. }, - setup: function(ownerPassword, userPassword) { + setup: function pDFDocSetup(ownerPassword, userPassword) { this.checkHeader(); this.xref = new XRef(this.stream, this.startXRef, @@ -3760,7 +3792,7 @@ var PDFDoc = (function() { // shadow the prototype getter return shadow(this, 'numPages', num); }, - getPage: function(n) { + getPage: function pDFDocGetPage(n) { return this.catalog.getPage(n); } }; @@ -4035,7 +4067,7 @@ var Encodings = { var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; -var EvalState = (function() { +var EvalState = (function evalState() { function constructor() { // Are soft masks and alpha values shapes or opacities? this.alphaIsShape = false; @@ -4058,7 +4090,7 @@ var EvalState = (function() { return constructor; })(); -var PartialEvaluator = (function() { +var PartialEvaluator = (function partialEvaluator() { function constructor() { this.state = new EvalState(); this.stateStack = []; @@ -4162,7 +4194,8 @@ var PartialEvaluator = (function() { }; constructor.prototype = { - evaluate: function(stream, xref, resources, fonts, images) { + evaluate: function partialEvaluatorEvaluate(stream, xref, resources, fonts, + images) { resources = xref.fetchIfRef(resources) || new Dict(); var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict(); var patterns = xref.fetchIfRef(resources.get('Pattern')) || new Dict(); @@ -4240,13 +4273,15 @@ var PartialEvaluator = (function() { } } - return function(gfx) { + return function partialEvaluatorReturn(gfx) { for (var i = 0, length = argsArray.length; i < length; i++) gfx[fnArray[i]].apply(gfx, argsArray[i]); }; }, - extractEncoding: function(dict, xref, properties) { + extractEncoding: function partialEvaluatorExtractEncoding(dict, + xref, + properties) { var type = properties.type, encoding; if (properties.composite) { if (type == 'CIDFontType2') { @@ -4276,8 +4311,9 @@ var PartialEvaluator = (function() { properties.widths = glyphsWidths; var cidToGidMap = dict.get('CIDToGIDMap'); - if (!cidToGidMap || !IsRef(cidToGidMap)) - return GlyphsUnicode; + if (!cidToGidMap || !IsRef(cidToGidMap)) { + return Object.create(GlyphsUnicode); + } // Extract the encoding from the CIDToGIDMap var glyphsStream = xref.fetchIfRef(cidToGidMap); @@ -4314,7 +4350,7 @@ var PartialEvaluator = (function() { '9.7.5.3'); } } - return GlyphsUnicode; + return Object.create(GlyphsUnicode); } var differences = properties.differences; @@ -4486,7 +4522,34 @@ var PartialEvaluator = (function() { return glyphs; }, - translateFont: function(dict, xref, resources) { + getBaseFontMetricsAndMap: function getBaseFontMetricsAndMap(name) { + var map = {}; + if (/^Symbol(-?(Bold|Italic))*$/.test(name)) { + // special case for symbols + var encoding = Encodings.symbolsEncoding.slice(); + for (var i = 0, n = encoding.length, j; i < n; i++) { + if (!(j = encoding[i])) + continue; + map[i] = GlyphsUnicode[j] || 0; + } + } + + var defaultWidth = 0; + var widths = Metrics[stdFontMap[name] || name]; + if (IsNum(widths)) { + defaultWidth = widths; + widths = null; + } + + return { + defaultWidth: defaultWidth, + widths: widths || [], + map: map + }; + }, + + translateFont: function partialEvaluatorTranslateFont(dict, xref, + resources) { var baseDict = dict; var type = dict.get('Subtype'); assertWellFormed(IsName(type), 'invalid font Subtype'); @@ -4521,30 +4584,15 @@ var PartialEvaluator = (function() { return null; // Using base font name as a font name. - baseFontName = baseFontName.name; - var map = {}; - if (/^Symbol(-?(Bold|Italic))*$/.test(baseFontName)) { - // special case for symbols - var encoding = Encodings.symbolsEncoding; - for (var i = 0, n = encoding.length, j; i < n; i++) { - if (!(j = encoding[i])) - continue; - map[i] = GlyphsUnicode[j] || 0; - } - } + baseFontName = baseFontName.name.replace(/,/g, '_'); + var metricsAndMap = this.getBaseFontMetricsAndMap(baseFontName); - var defaultWidth = 0; - var widths = Metrics[stdFontMap[baseFontName] || baseFontName]; - if (IsNum(widths)) { - defaultWidth = widths; - widths = null; - } var properties = { type: type.name, - encoding: map, + encoding: metricsAndMap.map, differences: [], - widths: widths || {}, - defaultWidth: defaultWidth, + widths: metricsAndMap.widths, + defaultWidth: metricsAndMap.defaultWidth, firstChar: 0, lastChar: 256 }; @@ -4564,7 +4612,25 @@ var PartialEvaluator = (function() { // a variant. var firstChar = xref.fetchIfRef(dict.get('FirstChar')) || 0; var lastChar = xref.fetchIfRef(dict.get('LastChar')) || 256; - var widths = xref.fetchIfRef(dict.get('Widths')) || []; + var defaultWidth = 0; + var glyphWidths = {}; + var encoding = {}; + var widths = xref.fetchIfRef(dict.get('Widths')); + if (widths) { + for (var i = 0, j = firstChar; i < widths.length; i++, j++) + glyphWidths[j] = widths[i]; + defaultWidth = parseFloat(descriptor.get('MissingWidth')) || 0; + } else { + // Trying get the BaseFont metrics (see comment above). + var baseFontName = dict.get('BaseFont'); + if (IsName(baseFontName)) { + var metricsAndMap = this.getBaseFontMetricsAndMap(baseFontName.name); + + glyphWidths = metricsAndMap.widths; + defaultWidth = metricsAndMap.defaultWidth; + encoding = metricsAndMap.map; + } + } var fontName = xref.fetchIfRef(descriptor.get('FontName')); assertWellFormed(IsName(fontName), 'invalid font name'); @@ -4603,17 +4669,12 @@ var PartialEvaluator = (function() { descent: descriptor.get('Descent'), xHeight: descriptor.get('XHeight'), capHeight: descriptor.get('CapHeight'), - defaultWidth: parseFloat(descriptor.get('MissingWidth')) || 0, + defaultWidth: defaultWidth, flags: descriptor.get('Flags'), italicAngle: descriptor.get('ItalicAngle'), differences: [], - widths: (function() { - var glyphWidths = {}; - for (var i = 0; i < widths.length; i++) - glyphWidths[firstChar++] = widths[i]; - return glyphWidths; - })(), - encoding: {} + widths: glyphWidths, + encoding: encoding }; properties.glyphs = this.extractEncoding(dict, xref, properties); @@ -4631,7 +4692,7 @@ var PartialEvaluator = (function() { //