diff --git a/Makefile b/Makefile index 56b597e5f..a9cd2793f 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,8 @@ browser-test: # To install gjslint, see: # # -SRC_DIRS := . utils worker web test +SRC_DIRS := . utils worker web test examples/helloworld extensions/firefox \ + extensions/firefox/components GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js)) lint: gjslint $(GJSLINT_FILES) @@ -165,9 +166,9 @@ PDF_WEB_FILES = \ extension: # Copy a standalone version of pdf.js inside the content directory @rm -Rf $(EXTENSION_SRC)/$(CONTENT_DIR)/ - @mkdir $(EXTENSION_SRC)/$(CONTENT_DIR)/ + @mkdir -p $(EXTENSION_SRC)/$(CONTENT_DIR)/web @cp $(PDF_JS_FILES) $(EXTENSION_SRC)/$(CONTENT_DIR)/ - @cp -r $(PDF_WEB_FILES) $(EXTENSION_SRC)/$(CONTENT_DIR)/ + @cp -r $(PDF_WEB_FILES) $(EXTENSION_SRC)/$(CONTENT_DIR)/web/ # Create the xpi @cd $(EXTENSION_SRC); zip -r $(EXTENSION_NAME) * diff --git a/README.md b/README.md index f5d4eee9d..5601c0af3 100644 --- a/README.md +++ b/README.md @@ -18,46 +18,42 @@ successful. ## Getting started -**Online demo** +### Online demo For an online demo, visit: - http://andreasgal.github.com/pdf.js/web/viewer.html ++ http://andreasgal.github.com/pdf.js/web/viewer.html This demo provides an interactive interface for displaying and browsing PDFs using the pdf.js API. -**Getting the code** +### 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 -``` + $ 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 -``` + $ make server If everything worked out, you can now serve - http://localhost:8888/web/viewer.html ++ 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 ++ http://localhost:8888/test/pdfs/?frame -**Hello world** +### Hello world For a "hello world" example, take a look at: - examples/helloworld/ ++ [examples/helloworld/hello.js](https://github.com/andreasgal/pdf.js/blob/master/examples/helloworld/hello.js) This example illustrates the bare minimum ingredients for integrating pdf.js in a custom project. @@ -66,16 +62,22 @@ in a custom project. ## Contributing -pdf.js is a community-driver project, so contributors are always welcome. +pdf.js is a community-driven 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. +[open issues](https://github.com/andreasgal/pdf.js/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 +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. +Our Github contributors so far: + ++ https://github.com/andreasgal/pdf.js/contributors + +You can add your name to it! :) + ## Running the Tests @@ -108,47 +110,46 @@ raising any errors. Our demo site is here: - http://andreasgal.github.com/pdf.js/web/viewer.html ++ http://andreasgal.github.com/pdf.js/web/viewer.html You can read more about pdf.js here: - http://andreasgal.com/2011/06/15/pdf-js/ - - http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/ - -Follow us on twitter: @pdfjs - - http://twitter.com/#!/pdfjs - -Join our mailing list: - - dev-pdf-js@lists.mozilla.org - -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 ++ http://andreasgal.com/2011/06/15/pdf-js/ ++ http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/ Talk to us on IRC: - #pdfjs on irc.mozilla.org ++ #pdfjs on irc.mozilla.org + +Join our mailing list: + ++ dev-pdf-js@lists.mozilla.org + +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 + +Follow us on twitter: @pdfjs + ++ http://twitter.com/#!/pdfjs + + ## 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 ++ http://partners.adobe.com/public/developer/en/livecycle/lc_pdf_overview_format.pdf A more detailed file example: - http://gnupdf.org/Introduction_to_PDF ++ http://gnupdf.org/Introduction_to_PDF -The PDF specification itself is an ISO and not free available. However, there is +The PDF specification itself is an ISO and not freely 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 ++ 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", +Recommended chapters to read: "2. Overview", "3.4 File Structure", "4.1 Graphics Objects" that lists the PDF commands. - diff --git a/crypto.js b/crypto.js index 553221816..5699ea1df 100644 --- a/crypto.js +++ b/crypto.js @@ -493,16 +493,16 @@ var CipherTransformFactory = (function cipherTransformFactory() { function constructor(dict, fileId, password) { var filter = dict.get('Filter'); - if (!IsName(filter) || filter.name != 'Standard') + if (!isName(filter) || filter.name != 'Standard') error('unknown encryption method'); this.dict = dict; var algorithm = dict.get('V'); - if (!IsInt(algorithm) || + if (!isInt(algorithm) || (algorithm != 1 && algorithm != 2 && algorithm != 4)) error('unsupported encryption algorithm'); this.algorithm = algorithm; var keyLength = dict.get('Length') || 40; - if (!IsInt(keyLength) || + if (!isInt(keyLength) || keyLength < 40 || (keyLength % 8) != 0) error('invalid key length'); // prepare keys diff --git a/examples/helloworld/README.md b/examples/helloworld/README.md index 8395733f3..9b7b3001a 100644 --- a/examples/helloworld/README.md +++ b/examples/helloworld/README.md @@ -1,16 +1,18 @@ ## "Hello World" overview This example is a minimalistic application of the pdf.js project. The file -`helloworld.pdf` is from the GNUpdf project (see [Introduction to PDF at GNUpdf](http://gnupdf.org/Introduction_to_PDF), and contains a simple and +`helloworld.pdf` is from the GNUpdf project (see [Introduction to PDF at +GNUpdf] (http://gnupdf.org/Introduction_to_PDF)), and contains a simple and human-readable PDF. ## Getting started -Point your browser to `index.html`. Voila. Take a peek at `hello.js` to see +Point your browser to `index.html`. Voila. Take a peek at `hello.js` to see how to make basic calls to `pdf.js`. ## Additional resources + [GNUpdf - Introduction to PDF](http://gnupdf.org/Introduction_to_PDF) + diff --git a/examples/helloworld/hello.js b/examples/helloworld/hello.js index 21799c33a..9c653ca24 100644 --- a/examples/helloworld/hello.js +++ b/examples/helloworld/hello.js @@ -1,36 +1,17 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + // // See README for overview // +'use strict'; -// -// Ajax GET request, for binary files -// (like jQuery's $.get(), but supports the binary type ArrayBuffer) -// -var ajaxGet = function(url, callback){ - var xhr = new XMLHttpRequest(); - xhr.open('GET', url); - xhr.mozResponseType = xhr.responseType = 'arraybuffer'; - xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200; - xhr.onreadystatechange = function() { - if (xhr.readyState === 4 && xhr.status === xhr.expected) { - var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || - xhr.responseArrayBuffer || xhr.response); - - callback(data); - } - }; - xhr.send(null); -} - -// -// This is where the fun happens -// -ajaxGet('helloworld.pdf', function(data){ +getPdf('helloworld.pdf', function getPdfHelloWorld(data) { // // Instantiate PDFDoc with PDF data // - var pdf = new PDFDoc(new Stream(data)); + var pdf = new PDFDoc(data); var page = pdf.getPage(1); var scale = 1.5; @@ -40,10 +21,11 @@ ajaxGet('helloworld.pdf', function(data){ var canvas = document.getElementById('the-canvas'); var context = canvas.getContext('2d'); canvas.height = page.height * scale; - canvas.width = page.width * scale; + canvas.width = page.width * scale; // // Render PDF page into canvas context // page.startRendering(context); }); + diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js index 8dc13275a..5384a05df 100644 --- a/extensions/firefox/bootstrap.js +++ b/extensions/firefox/bootstrap.js @@ -1,31 +1,36 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + let Cc = Components.classes; let Ci = Components.interfaces; let Cm = Components.manager; let Cu = Components.utils; -Cu.import("resource://gre/modules/Services.jsm"); +Cu.import('resource://gre/modules/Services.jsm'); function log(str) { - dump(str + "\n"); -}; + dump(str + '\n'); +} function startup(aData, aReason) { - let manifestPath = "chrome.manifest"; - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); + let manifestPath = 'chrome.manifest'; + let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile); try { file.initWithPath(aData.installPath.path); file.append(manifestPath); Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(file); - } catch(e) { + } catch (e) { log(e); } -}; +} function shutdown(aData, aReason) { -}; +} function install(aData, aReason) { - let url = "chrome://pdf.js/content/web/viewer.html?file=%s"; - Services.prefs.setCharPref("extensions.pdf.js.url", url); -}; + let url = 'chrome://pdf.js/content/web/viewer.html?file=%s'; + Services.prefs.setCharPref('extensions.pdf.js.url', url); +} diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index bafb83b12..e4b6a2a55 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -1,52 +1,65 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; -const PDF_CONTENT_TYPE = "application/pdf"; +const PDF_CONTENT_TYPE = 'application/pdf'; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -// TODO -// Add some download progress event +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); function log(aMsg) { - let msg = "pdfContentHandler.js: " + (aMsg.join ? aMsg.join("") : aMsg); - Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService) + let msg = 'pdfContentHandler.js: ' + (aMsg.join ? aMsg.join('') : aMsg); + Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService) .logStringMessage(msg); - dump(msg + "\n"); -}; + dump(msg + '\n'); +} + +function fireEventTo(aName, aData, aWindow) { + let window = aWindow.wrappedJSObject; + let evt = window.document.createEvent('CustomEvent'); + evt.initCustomEvent('pdf' + aName, false, false, aData); + window.document.dispatchEvent(evt); +} function loadDocument(aWindow, aDocumentUrl) { - let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] + let xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1'] .createInstance(Ci.nsIXMLHttpRequest); - xhr.open("GET", aDocumentUrl); - xhr.mozResponseType = xhr.responseType = "arraybuffer"; - xhr.onreadystatechange = function() { - if (xhr.readyState == 4 && xhr.status == 200) { - let data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || - xhr.responseArrayBuffer || xhr.response); - try { - var view = new Uint8Array(data); + xhr.onprogress = function updateProgress(evt) { + if (evt.lengthComputable) + fireEventTo(evt.type, evt.loaded / evt.total, aWindow); + }; - // I think accessing aWindow.wrappedJSObject returns a - // XPCSafeJSObjectWrapper and so it is safe but mrbkap can confirm that - let window = aWindow.wrappedJSObject; - var arrayBuffer = new window.ArrayBuffer(data.byteLength); - var view2 = new window.Uint8Array(arrayBuffer); - view2.set(view); + xhr.onerror = function error(evt) { + fireEventTo(evt.type, false, aWindow); + }; - let evt = window.document.createEvent("CustomEvent"); - evt.initCustomEvent("pdfloaded", false, false, arrayBuffer); - window.document.dispatchEvent(evt); - } catch(e) { - log("Error - " + e); - } + xhr.onload = function load(evt) { + let data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || + xhr.responseArrayBuffer || xhr.response); + try { + let view = new Uint8Array(data); + + let window = aWindow.wrappedJSObject; + let arrayBuffer = new window.ArrayBuffer(data.byteLength); + let view2 = new window.Uint8Array(arrayBuffer); + view2.set(view); + + fireEventTo(evt.type, arrayBuffer, aWindow); + } catch (e) { + log('Error - ' + e); } }; + + xhr.open('GET', aDocumentUrl); + xhr.responseType = 'arraybuffer'; xhr.send(null); -}; +} let WebProgressListener = { init: function(aWindow, aUrl) { @@ -64,11 +77,12 @@ let WebProgressListener = { .getInterface(Ci.nsIWebProgress); try { webProgress.removeProgressListener(this); - } catch(e) {} + } catch (e) {} webProgress.addProgressListener(this, flags); }, - onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, + aStatus) { const complete = Ci.nsIWebProgressListener.STATE_IS_WINDOW + Ci.nsIWebProgressListener.STATE_STOP; if ((aStateFlags & complete) == complete && this._locationHasChanged) { @@ -77,14 +91,17 @@ let WebProgressListener = { } }, - onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) { + onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, + aMaxSelf, aCurTotal, aMaxTotal) { }, - onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI) { + onLocationChange: function onLocationChange(aWebProgress, aRequest, + aLocationURI) { this._locationHasChanged = true; }, - onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { + onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, + aMessage) { }, onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) { @@ -127,16 +144,16 @@ pdfContentHandler.prototype = { WebProgressListener.init(window, uri.spec); try { - let url = Services.prefs.getCharPref("extensions.pdf.js.url"); - url = url.replace("%s", uri.spec); + let url = Services.prefs.getCharPref('extensions.pdf.js.url'); + url = url.replace('%s', uri.spec); window.location = url; - } catch(e) { - log("Error - " + e); + } catch (e) { + log('Error retrieving the pdf.js base url - ' + e); } }, - classID: Components.ID("{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]), + classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]) }; var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]); diff --git a/extensions/firefox/install.rdf b/extensions/firefox/install.rdf index 5d4f966a1..0dfd6bf57 100644 --- a/extensions/firefox/install.rdf +++ b/extensions/firefox/install.rdf @@ -12,7 +12,7 @@ {ec8030f7-c20a-464f-9b0e-13a3a9e97384} 6.0 - 9.0.* + 10.0.* true diff --git a/fonts.js b/fonts.js index 706f8ef6e..1a58d4c6c 100644 --- a/fonts.js +++ b/fonts.js @@ -1222,7 +1222,7 @@ var Font = (function Font() { encoding[i] = { unicode: i <= 0x1f || (i >= 127 && i <= 255) ? i + kCmapGlyphOffset : i, - width: IsNum(width) ? width : properties.defaultWidth + width: isNum(width) ? width : properties.defaultWidth }; } } else { @@ -2212,7 +2212,7 @@ CFF.prototype = { var cmd = map[command]; assert(cmd, 'Unknow command: ' + command); - if (IsArray(cmd)) + if (isArray(cmd)) charstring.splice(i++, 1, cmd[0], cmd[1]); else charstring[i] = cmd; @@ -2338,7 +2338,7 @@ CFF.prototype = { continue; var value = properties.private[field]; - if (IsArray(value)) { + if (isArray(value)) { data += self.encodeNumber(value[0]); for (var i = 1; i < value.length; i++) data += self.encodeNumber(value[i] - value[i - 1]); @@ -2497,7 +2497,7 @@ var Type2CFF = (function type2CFF() { var width = mapping.width; properties.glyphs[glyph] = properties.encoding[index] = { unicode: code, - width: IsNum(width) ? width : defaultWidth + width: isNum(width) ? width : defaultWidth }; charstrings.push({ diff --git a/pdf.js b/pdf.js index 18368aa0b..da6dbc016 100644 --- a/pdf.js +++ b/pdf.js @@ -55,16 +55,10 @@ function assertWellFormed(cond, msg) { } function shadow(obj, prop, value) { - try { - Object.defineProperty(obj, prop, { value: value, - enumerable: true, - configurable: true, - writable: false }); - } catch (e) { - obj.__defineGetter__(prop, function shadowDefineGetter() { - return value; - }); - } + Object.defineProperty(obj, prop, { value: value, + enumerable: true, + configurable: true, + writable: false }); return value; } @@ -112,6 +106,42 @@ function stringToPDFString(str) { return str2; } +// +// getPdf() +// Convenience function to perform binary Ajax GET +// Usage: getPdf('http://...', callback) +// getPdf({ +// url:String , +// [,progress:Function, error:Function] +// }, +// callback) +// +function getPdf(arg, callback) { + var params = arg; + if (typeof arg === 'string') + params = { url: arg }; + + var xhr = new XMLHttpRequest(); + xhr.open('GET', params.url); + xhr.mozResponseType = xhr.responseType = 'arraybuffer'; + xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200; + + if ('progress' in params) + xhr.onprogrss = params.progress || undefined; + + if ('error' in params) + xhr.onerror = params.error || undefined; + + xhr.onreadystatechange = function getPdfOnreadystatechange() { + if (xhr.readyState === 4 && xhr.status === xhr.expected) { + var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || + xhr.responseArrayBuffer || xhr.response); + callback(data); + } + }; + xhr.send(null); +} + var Stream = (function streamStream() { function constructor(arrayBuffer, start, length, dict) { this.bytes = new Uint8Array(arrayBuffer); @@ -172,7 +202,8 @@ var Stream = (function streamStream() { }, makeSubStream: function stream_makeSubstream(start, length, dict) { return new Stream(this.bytes.buffer, start, length, dict); - } + }, + isStream: true }; return constructor; @@ -867,16 +898,16 @@ var PredictorStream = (function predictorStream() { // A JpegStream can't be read directly. We use the platform to render // the underlying JPEG data for us. var JpegStream = (function jpegStream() { - function isYcckImage(bytes) { + function isAdobeImage(bytes) { var maxBytesScanned = Math.max(bytes.length - 16, 1024); - // Looking for APP14, 'Adobe' and transform = 2 + // Looking for APP14, 'Adobe' for (var i = 0; i < maxBytesScanned; ++i) { if (bytes[i] == 0xFF && bytes[i + 1] == 0xEE && bytes[i + 2] == 0x00 && bytes[i + 3] == 0x0E && bytes[i + 4] == 0x41 && bytes[i + 5] == 0x64 && bytes[i + 6] == 0x6F && bytes[i + 7] == 0x62 && bytes[i + 8] == 0x65 && bytes[i + 9] == 0x00) - return bytes[i + 15] == 0x02; + return true; // scanning until frame tag if (bytes[i] == 0xFF && bytes[i + 1] == 0xC0) break; @@ -884,7 +915,7 @@ var JpegStream = (function jpegStream() { return false; } - function fixYcckImage(bytes) { + function fixAdobeImage(bytes) { // Inserting 'EMBED' marker after JPEG signature var embedMarker = new Uint8Array([0xFF, 0xEC, 0, 8, 0x45, 0x4D, 0x42, 0x45, 0x44, 0]); @@ -902,8 +933,8 @@ var JpegStream = (function jpegStream() { // need to be removed this.dict = dict; - if (isYcckImage(bytes)) - bytes = fixYcckImage(bytes); + if (isAdobeImage(bytes)) + bytes = fixAdobeImage(bytes); // create DOM image var img = new Image(); @@ -1152,9 +1183,9 @@ var CCITTFaxStream = (function ccittFaxStream() { var twoDimVertL3 = 8; var twoDimTable = [ - [-1, -1], [-1, -1], // 000000x - [7, twoDimVertL3], // 0000010 - [7, twoDimVertR3], // 0000011 + [-1, -1], [-1, -1], // 000000x + [7, twoDimVertL3], // 0000010 + [7, twoDimVertR3], // 0000011 [6, twoDimVertL2], [6, twoDimVertL2], // 000010x [6, twoDimVertR2], [6, twoDimVertR2], // 000011x [4, twoDimPass], [4, twoDimPass], // 0001xxx @@ -1220,118 +1251,118 @@ var CCITTFaxStream = (function ccittFaxStream() { ]; var whiteTable1 = [ - [-1, -1], // 00000 - [12, ccittEOL], // 00001 - [-1, -1], [-1, -1], // 0001x - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 001xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 010xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 011xx - [11, 1792], [11, 1792], // 1000x - [12, 1984], // 10010 - [12, 2048], // 10011 - [12, 2112], // 10100 - [12, 2176], // 10101 - [12, 2240], // 10110 - [12, 2304], // 10111 - [11, 1856], [11, 1856], // 1100x - [11, 1920], [11, 1920], // 1101x - [12, 2368], // 11100 - [12, 2432], // 11101 - [12, 2496], // 11110 - [12, 2560] // 11111 + [-1, -1], // 00000 + [12, ccittEOL], // 00001 + [-1, -1], [-1, -1], // 0001x + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 001xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 010xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 011xx + [11, 1792], [11, 1792], // 1000x + [12, 1984], // 10010 + [12, 2048], // 10011 + [12, 2112], // 10100 + [12, 2176], // 10101 + [12, 2240], // 10110 + [12, 2304], // 10111 + [11, 1856], [11, 1856], // 1100x + [11, 1920], [11, 1920], // 1101x + [12, 2368], // 11100 + [12, 2432], // 11101 + [12, 2496], // 11110 + [12, 2560] // 11111 ]; var whiteTable2 = [ - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx - [8, 29], [8, 29], // 00000010x - [8, 30], [8, 30], // 00000011x - [8, 45], [8, 45], // 00000100x - [8, 46], [8, 46], // 00000101x - [7, 22], [7, 22], [7, 22], [7, 22], // 0000011xx - [7, 23], [7, 23], [7, 23], [7, 23], // 0000100xx - [8, 47], [8, 47], // 00001010x - [8, 48], [8, 48], // 00001011x - [6, 13], [6, 13], [6, 13], [6, 13], // 000011xxx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000000xx + [8, 29], [8, 29], // 00000010x + [8, 30], [8, 30], // 00000011x + [8, 45], [8, 45], // 00000100x + [8, 46], [8, 46], // 00000101x + [7, 22], [7, 22], [7, 22], [7, 22], // 0000011xx + [7, 23], [7, 23], [7, 23], [7, 23], // 0000100xx + [8, 47], [8, 47], // 00001010x + [8, 48], [8, 48], // 00001011x + [6, 13], [6, 13], [6, 13], [6, 13], // 000011xxx [6, 13], [6, 13], [6, 13], [6, 13], - [7, 20], [7, 20], [7, 20], [7, 20], // 0001000xx - [8, 33], [8, 33], // 00010010x - [8, 34], [8, 34], // 00010011x - [8, 35], [8, 35], // 00010100x - [8, 36], [8, 36], // 00010101x - [8, 37], [8, 37], // 00010110x - [8, 38], [8, 38], // 00010111x - [7, 19], [7, 19], [7, 19], [7, 19], // 0001100xx - [8, 31], [8, 31], // 00011010x - [8, 32], [8, 32], // 00011011x - [6, 1], [6, 1], [6, 1], [6, 1], // 000111xxx + [7, 20], [7, 20], [7, 20], [7, 20], // 0001000xx + [8, 33], [8, 33], // 00010010x + [8, 34], [8, 34], // 00010011x + [8, 35], [8, 35], // 00010100x + [8, 36], [8, 36], // 00010101x + [8, 37], [8, 37], // 00010110x + [8, 38], [8, 38], // 00010111x + [7, 19], [7, 19], [7, 19], [7, 19], // 0001100xx + [8, 31], [8, 31], // 00011010x + [8, 32], [8, 32], // 00011011x + [6, 1], [6, 1], [6, 1], [6, 1], // 000111xxx [6, 1], [6, 1], [6, 1], [6, 1], - [6, 12], [6, 12], [6, 12], [6, 12], // 001000xxx + [6, 12], [6, 12], [6, 12], [6, 12], // 001000xxx [6, 12], [6, 12], [6, 12], [6, 12], - [8, 53], [8, 53], // 00100100x - [8, 54], [8, 54], // 00100101x - [7, 26], [7, 26], [7, 26], [7, 26], // 0010011xx - [8, 39], [8, 39], // 00101000x - [8, 40], [8, 40], // 00101001x - [8, 41], [8, 41], // 00101010x - [8, 42], [8, 42], // 00101011x - [8, 43], [8, 43], // 00101100x - [8, 44], [8, 44], // 00101101x - [7, 21], [7, 21], [7, 21], [7, 21], // 0010111xx - [7, 28], [7, 28], [7, 28], [7, 28], // 0011000xx - [8, 61], [8, 61], // 00110010x - [8, 62], [8, 62], // 00110011x - [8, 63], [8, 63], // 00110100x - [8, 0], [8, 0], // 00110101x - [8, 320], [8, 320], // 00110110x - [8, 384], [8, 384], // 00110111x - [5, 10], [5, 10], [5, 10], [5, 10], // 00111xxxx + [8, 53], [8, 53], // 00100100x + [8, 54], [8, 54], // 00100101x + [7, 26], [7, 26], [7, 26], [7, 26], // 0010011xx + [8, 39], [8, 39], // 00101000x + [8, 40], [8, 40], // 00101001x + [8, 41], [8, 41], // 00101010x + [8, 42], [8, 42], // 00101011x + [8, 43], [8, 43], // 00101100x + [8, 44], [8, 44], // 00101101x + [7, 21], [7, 21], [7, 21], [7, 21], // 0010111xx + [7, 28], [7, 28], [7, 28], [7, 28], // 0011000xx + [8, 61], [8, 61], // 00110010x + [8, 62], [8, 62], // 00110011x + [8, 63], [8, 63], // 00110100x + [8, 0], [8, 0], // 00110101x + [8, 320], [8, 320], // 00110110x + [8, 384], [8, 384], // 00110111x + [5, 10], [5, 10], [5, 10], [5, 10], // 00111xxxx [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], [5, 10], - [5, 11], [5, 11], [5, 11], [5, 11], // 01000xxxx + [5, 11], [5, 11], [5, 11], [5, 11], // 01000xxxx [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], [5, 11], - [7, 27], [7, 27], [7, 27], [7, 27], // 0100100xx - [8, 59], [8, 59], // 01001010x - [8, 60], [8, 60], // 01001011x - [9, 1472], // 010011000 - [9, 1536], // 010011001 - [9, 1600], // 010011010 - [9, 1728], // 010011011 - [7, 18], [7, 18], [7, 18], [7, 18], // 0100111xx - [7, 24], [7, 24], [7, 24], [7, 24], // 0101000xx - [8, 49], [8, 49], // 01010010x - [8, 50], [8, 50], // 01010011x - [8, 51], [8, 51], // 01010100x - [8, 52], [8, 52], // 01010101x - [7, 25], [7, 25], [7, 25], [7, 25], // 0101011xx - [8, 55], [8, 55], // 01011000x - [8, 56], [8, 56], // 01011001x - [8, 57], [8, 57], // 01011010x - [8, 58], [8, 58], // 01011011x - [6, 192], [6, 192], [6, 192], [6, 192], // 010111xxx + [7, 27], [7, 27], [7, 27], [7, 27], // 0100100xx + [8, 59], [8, 59], // 01001010x + [8, 60], [8, 60], // 01001011x + [9, 1472], // 010011000 + [9, 1536], // 010011001 + [9, 1600], // 010011010 + [9, 1728], // 010011011 + [7, 18], [7, 18], [7, 18], [7, 18], // 0100111xx + [7, 24], [7, 24], [7, 24], [7, 24], // 0101000xx + [8, 49], [8, 49], // 01010010x + [8, 50], [8, 50], // 01010011x + [8, 51], [8, 51], // 01010100x + [8, 52], [8, 52], // 01010101x + [7, 25], [7, 25], [7, 25], [7, 25], // 0101011xx + [8, 55], [8, 55], // 01011000x + [8, 56], [8, 56], // 01011001x + [8, 57], [8, 57], // 01011010x + [8, 58], [8, 58], // 01011011x + [6, 192], [6, 192], [6, 192], [6, 192], // 010111xxx [6, 192], [6, 192], [6, 192], [6, 192], - [6, 1664], [6, 1664], [6, 1664], [6, 1664], // 011000xxx + [6, 1664], [6, 1664], [6, 1664], [6, 1664], // 011000xxx [6, 1664], [6, 1664], [6, 1664], [6, 1664], - [8, 448], [8, 448], // 01100100x - [8, 512], [8, 512], // 01100101x - [9, 704], // 011001100 - [9, 768], // 011001101 - [8, 640], [8, 640], // 01100111x - [8, 576], [8, 576], // 01101000x - [9, 832], // 011010010 - [9, 896], // 011010011 - [9, 960], // 011010100 - [9, 1024], // 011010101 - [9, 1088], // 011010110 - [9, 1152], // 011010111 - [9, 1216], // 011011000 - [9, 1280], // 011011001 - [9, 1344], // 011011010 - [9, 1408], // 011011011 - [7, 256], [7, 256], [7, 256], [7, 256], // 0110111xx - [4, 2], [4, 2], [4, 2], [4, 2], // 0111xxxxx + [8, 448], [8, 448], // 01100100x + [8, 512], [8, 512], // 01100101x + [9, 704], // 011001100 + [9, 768], // 011001101 + [8, 640], [8, 640], // 01100111x + [8, 576], [8, 576], // 01101000x + [9, 832], // 011010010 + [9, 896], // 011010011 + [9, 960], // 011010100 + [9, 1024], // 011010101 + [9, 1088], // 011010110 + [9, 1152], // 011010111 + [9, 1216], // 011011000 + [9, 1280], // 011011001 + [9, 1344], // 011011010 + [9, 1408], // 011011011 + [7, 256], [7, 256], [7, 256], [7, 256], // 0110111xx + [4, 2], [4, 2], [4, 2], [4, 2], // 0111xxxxx [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], @@ -1339,7 +1370,7 @@ var CCITTFaxStream = (function ccittFaxStream() { [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], [4, 2], - [4, 3], [4, 3], [4, 3], [4, 3], // 1000xxxxx + [4, 3], [4, 3], [4, 3], [4, 3], // 1000xxxxx [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], @@ -1347,23 +1378,23 @@ var CCITTFaxStream = (function ccittFaxStream() { [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], [4, 3], - [5, 128], [5, 128], [5, 128], [5, 128], // 10010xxxx + [5, 128], [5, 128], [5, 128], [5, 128], // 10010xxxx [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], [5, 128], - [5, 8], [5, 8], [5, 8], [5, 8], // 10011xxxx + [5, 8], [5, 8], [5, 8], [5, 8], // 10011xxxx [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], [5, 8], - [5, 9], [5, 9], [5, 9], [5, 9], // 10100xxxx + [5, 9], [5, 9], [5, 9], [5, 9], // 10100xxxx [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], [5, 9], - [6, 16], [6, 16], [6, 16], [6, 16], // 101010xxx + [6, 16], [6, 16], [6, 16], [6, 16], // 101010xxx [6, 16], [6, 16], [6, 16], [6, 16], - [6, 17], [6, 17], [6, 17], [6, 17], // 101011xxx + [6, 17], [6, 17], [6, 17], [6, 17], // 101011xxx [6, 17], [6, 17], [6, 17], [6, 17], - [4, 4], [4, 4], [4, 4], [4, 4], // 1011xxxxx + [4, 4], [4, 4], [4, 4], [4, 4], // 1011xxxxx [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], @@ -1371,7 +1402,7 @@ var CCITTFaxStream = (function ccittFaxStream() { [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], [4, 4], - [4, 5], [4, 5], [4, 5], [4, 5], // 1100xxxxx + [4, 5], [4, 5], [4, 5], [4, 5], // 1100xxxxx [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], @@ -1379,15 +1410,15 @@ var CCITTFaxStream = (function ccittFaxStream() { [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], [4, 5], - [6, 14], [6, 14], [6, 14], [6, 14], // 110100xxx + [6, 14], [6, 14], [6, 14], [6, 14], // 110100xxx [6, 14], [6, 14], [6, 14], [6, 14], - [6, 15], [6, 15], [6, 15], [6, 15], // 110101xxx + [6, 15], [6, 15], [6, 15], [6, 15], // 110101xxx [6, 15], [6, 15], [6, 15], [6, 15], - [5, 64], [5, 64], [5, 64], [5, 64], // 11011xxxx + [5, 64], [5, 64], [5, 64], [5, 64], // 11011xxxx [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], [5, 64], - [4, 6], [4, 6], [4, 6], [4, 6], // 1110xxxxx + [4, 6], [4, 6], [4, 6], [4, 6], // 1110xxxxx [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], @@ -1395,7 +1426,7 @@ var CCITTFaxStream = (function ccittFaxStream() { [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], [4, 6], - [4, 7], [4, 7], [4, 7], [4, 7], // 1111xxxxx + [4, 7], [4, 7], [4, 7], [4, 7], // 1111xxxxx [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], [4, 7], @@ -1406,100 +1437,100 @@ var CCITTFaxStream = (function ccittFaxStream() { ]; var blackTable1 = [ - [-1, -1], [-1, -1], // 000000000000x - [12, ccittEOL], [12, ccittEOL], // 000000000001x - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000010xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000011xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000100xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000101xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000110xx - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000111xx - [11, 1792], [11, 1792], [11, 1792], [11, 1792], // 00000001000xx - [12, 1984], [12, 1984], // 000000010010x - [12, 2048], [12, 2048], // 000000010011x - [12, 2112], [12, 2112], // 000000010100x - [12, 2176], [12, 2176], // 000000010101x - [12, 2240], [12, 2240], // 000000010110x - [12, 2304], [12, 2304], // 000000010111x - [11, 1856], [11, 1856], [11, 1856], [11, 1856], // 00000001100xx - [11, 1920], [11, 1920], [11, 1920], [11, 1920], // 00000001101xx - [12, 2368], [12, 2368], // 000000011100x - [12, 2432], [12, 2432], // 000000011101x - [12, 2496], [12, 2496], // 000000011110x - [12, 2560], [12, 2560], // 000000011111x - [10, 18], [10, 18], [10, 18], [10, 18], // 0000001000xxx + [-1, -1], [-1, -1], // 000000000000x + [12, ccittEOL], [12, ccittEOL], // 000000000001x + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000001xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000010xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000011xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000100xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000101xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000110xx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 00000000111xx + [11, 1792], [11, 1792], [11, 1792], [11, 1792], // 00000001000xx + [12, 1984], [12, 1984], // 000000010010x + [12, 2048], [12, 2048], // 000000010011x + [12, 2112], [12, 2112], // 000000010100x + [12, 2176], [12, 2176], // 000000010101x + [12, 2240], [12, 2240], // 000000010110x + [12, 2304], [12, 2304], // 000000010111x + [11, 1856], [11, 1856], [11, 1856], [11, 1856], // 00000001100xx + [11, 1920], [11, 1920], [11, 1920], [11, 1920], // 00000001101xx + [12, 2368], [12, 2368], // 000000011100x + [12, 2432], [12, 2432], // 000000011101x + [12, 2496], [12, 2496], // 000000011110x + [12, 2560], [12, 2560], // 000000011111x + [10, 18], [10, 18], [10, 18], [10, 18], // 0000001000xxx [10, 18], [10, 18], [10, 18], [10, 18], - [12, 52], [12, 52], // 000000100100x - [13, 640], // 0000001001010 - [13, 704], // 0000001001011 - [13, 768], // 0000001001100 - [13, 832], // 0000001001101 - [12, 55], [12, 55], // 000000100111x - [12, 56], [12, 56], // 000000101000x - [13, 1280], // 0000001010010 - [13, 1344], // 0000001010011 - [13, 1408], // 0000001010100 - [13, 1472], // 0000001010101 - [12, 59], [12, 59], // 000000101011x - [12, 60], [12, 60], // 000000101100x - [13, 1536], // 0000001011010 - [13, 1600], // 0000001011011 - [11, 24], [11, 24], [11, 24], [11, 24], // 00000010111xx - [11, 25], [11, 25], [11, 25], [11, 25], // 00000011000xx - [13, 1664], // 0000001100100 - [13, 1728], // 0000001100101 - [12, 320], [12, 320], // 000000110011x - [12, 384], [12, 384], // 000000110100x - [12, 448], [12, 448], // 000000110101x - [13, 512], // 0000001101100 - [13, 576], // 0000001101101 - [12, 53], [12, 53], // 000000110111x - [12, 54], [12, 54], // 000000111000x - [13, 896], // 0000001110010 - [13, 960], // 0000001110011 - [13, 1024], // 0000001110100 - [13, 1088], // 0000001110101 - [13, 1152], // 0000001110110 - [13, 1216], // 0000001110111 - [10, 64], [10, 64], [10, 64], [10, 64], // 0000001111xxx + [12, 52], [12, 52], // 000000100100x + [13, 640], // 0000001001010 + [13, 704], // 0000001001011 + [13, 768], // 0000001001100 + [13, 832], // 0000001001101 + [12, 55], [12, 55], // 000000100111x + [12, 56], [12, 56], // 000000101000x + [13, 1280], // 0000001010010 + [13, 1344], // 0000001010011 + [13, 1408], // 0000001010100 + [13, 1472], // 0000001010101 + [12, 59], [12, 59], // 000000101011x + [12, 60], [12, 60], // 000000101100x + [13, 1536], // 0000001011010 + [13, 1600], // 0000001011011 + [11, 24], [11, 24], [11, 24], [11, 24], // 00000010111xx + [11, 25], [11, 25], [11, 25], [11, 25], // 00000011000xx + [13, 1664], // 0000001100100 + [13, 1728], // 0000001100101 + [12, 320], [12, 320], // 000000110011x + [12, 384], [12, 384], // 000000110100x + [12, 448], [12, 448], // 000000110101x + [13, 512], // 0000001101100 + [13, 576], // 0000001101101 + [12, 53], [12, 53], // 000000110111x + [12, 54], [12, 54], // 000000111000x + [13, 896], // 0000001110010 + [13, 960], // 0000001110011 + [13, 1024], // 0000001110100 + [13, 1088], // 0000001110101 + [13, 1152], // 0000001110110 + [13, 1216], // 0000001110111 + [10, 64], [10, 64], [10, 64], [10, 64], // 0000001111xxx [10, 64], [10, 64], [10, 64], [10, 64] ]; var blackTable2 = [ - [8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx + [8, 13], [8, 13], [8, 13], [8, 13], // 00000100xxxx [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], [8, 13], - [11, 23], [11, 23], // 00000101000x - [12, 50], // 000001010010 - [12, 51], // 000001010011 - [12, 44], // 000001010100 - [12, 45], // 000001010101 - [12, 46], // 000001010110 - [12, 47], // 000001010111 - [12, 57], // 000001011000 - [12, 58], // 000001011001 - [12, 61], // 000001011010 - [12, 256], // 000001011011 - [10, 16], [10, 16], [10, 16], [10, 16], // 0000010111xx - [10, 17], [10, 17], [10, 17], [10, 17], // 0000011000xx - [12, 48], // 000001100100 - [12, 49], // 000001100101 - [12, 62], // 000001100110 - [12, 63], // 000001100111 - [12, 30], // 000001101000 - [12, 31], // 000001101001 - [12, 32], // 000001101010 - [12, 33], // 000001101011 - [12, 40], // 000001101100 - [12, 41], // 000001101101 - [11, 22], [11, 22], // 00000110111x - [8, 14], [8, 14], [8, 14], [8, 14], // 00000111xxxx + [11, 23], [11, 23], // 00000101000x + [12, 50], // 000001010010 + [12, 51], // 000001010011 + [12, 44], // 000001010100 + [12, 45], // 000001010101 + [12, 46], // 000001010110 + [12, 47], // 000001010111 + [12, 57], // 000001011000 + [12, 58], // 000001011001 + [12, 61], // 000001011010 + [12, 256], // 000001011011 + [10, 16], [10, 16], [10, 16], [10, 16], // 0000010111xx + [10, 17], [10, 17], [10, 17], [10, 17], // 0000011000xx + [12, 48], // 000001100100 + [12, 49], // 000001100101 + [12, 62], // 000001100110 + [12, 63], // 000001100111 + [12, 30], // 000001101000 + [12, 31], // 000001101001 + [12, 32], // 000001101010 + [12, 33], // 000001101011 + [12, 40], // 000001101100 + [12, 41], // 000001101101 + [11, 22], [11, 22], // 00000110111x + [8, 14], [8, 14], [8, 14], [8, 14], // 00000111xxxx [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], [8, 14], - [7, 10], [7, 10], [7, 10], [7, 10], // 0000100xxxxx + [7, 10], [7, 10], [7, 10], [7, 10], // 0000100xxxxx [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], @@ -1507,7 +1538,7 @@ var CCITTFaxStream = (function ccittFaxStream() { [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], [7, 10], - [7, 11], [7, 11], [7, 11], [7, 11], // 0000101xxxxx + [7, 11], [7, 11], [7, 11], [7, 11], // 0000101xxxxx [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], @@ -1515,27 +1546,27 @@ var CCITTFaxStream = (function ccittFaxStream() { [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], [7, 11], - [9, 15], [9, 15], [9, 15], [9, 15], // 000011000xxx + [9, 15], [9, 15], [9, 15], [9, 15], // 000011000xxx [9, 15], [9, 15], [9, 15], [9, 15], - [12, 128], // 000011001000 - [12, 192], // 000011001001 - [12, 26], // 000011001010 - [12, 27], // 000011001011 - [12, 28], // 000011001100 - [12, 29], // 000011001101 - [11, 19], [11, 19], // 00001100111x - [11, 20], [11, 20], // 00001101000x - [12, 34], // 000011010010 - [12, 35], // 000011010011 - [12, 36], // 000011010100 - [12, 37], // 000011010101 - [12, 38], // 000011010110 - [12, 39], // 000011010111 - [11, 21], [11, 21], // 00001101100x - [12, 42], // 000011011010 - [12, 43], // 000011011011 - [10, 0], [10, 0], [10, 0], [10, 0], // 0000110111xx - [7, 12], [7, 12], [7, 12], [7, 12], // 0000111xxxxx + [12, 128], // 000011001000 + [12, 192], // 000011001001 + [12, 26], // 000011001010 + [12, 27], // 000011001011 + [12, 28], // 000011001100 + [12, 29], // 000011001101 + [11, 19], [11, 19], // 00001100111x + [11, 20], [11, 20], // 00001101000x + [12, 34], // 000011010010 + [12, 35], // 000011010011 + [12, 36], // 000011010100 + [12, 37], // 000011010101 + [12, 38], // 000011010110 + [12, 39], // 000011010111 + [11, 21], [11, 21], // 00001101100x + [12, 42], // 000011011010 + [12, 43], // 000011011011 + [10, 0], [10, 0], [10, 0], [10, 0], // 0000110111xx + [7, 12], [7, 12], [7, 12], [7, 12], // 0000111xxxxx [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], [7, 12], @@ -1546,21 +1577,21 @@ var CCITTFaxStream = (function ccittFaxStream() { ]; var blackTable3 = [ - [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx - [6, 9], // 000100 - [6, 8], // 000101 - [5, 7], [5, 7], // 00011x - [4, 6], [4, 6], [4, 6], [4, 6], // 0010xx - [4, 5], [4, 5], [4, 5], [4, 5], // 0011xx - [3, 1], [3, 1], [3, 1], [3, 1], // 010xxx + [-1, -1], [-1, -1], [-1, -1], [-1, -1], // 0000xx + [6, 9], // 000100 + [6, 8], // 000101 + [5, 7], [5, 7], // 00011x + [4, 6], [4, 6], [4, 6], [4, 6], // 0010xx + [4, 5], [4, 5], [4, 5], [4, 5], // 0011xx + [3, 1], [3, 1], [3, 1], [3, 1], // 010xxx [3, 1], [3, 1], [3, 1], [3, 1], - [3, 4], [3, 4], [3, 4], [3, 4], // 011xxx + [3, 4], [3, 4], [3, 4], [3, 4], // 011xxx [3, 4], [3, 4], [3, 4], [3, 4], - [2, 3], [2, 3], [2, 3], [2, 3], // 10xxxx + [2, 3], [2, 3], [2, 3], [2, 3], // 10xxxx [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], [2, 3], - [2, 2], [2, 2], [2, 2], [2, 2], // 11xxxx + [2, 2], [2, 2], [2, 2], [2, 2], // 11xxxx [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2], [2, 2] @@ -2348,57 +2379,57 @@ var RefSet = (function refSet() { return constructor; })(); -function IsBool(v) { +function isBool(v) { return typeof v == 'boolean'; } -function IsInt(v) { +function isInt(v) { return typeof v == 'number' && ((v | 0) == v); } -function IsNum(v) { +function isNum(v) { return typeof v == 'number'; } -function IsString(v) { +function isString(v) { return typeof v == 'string'; } -function IsNull(v) { +function isNull(v) { return v === null; } -function IsName(v) { +function isName(v) { return v instanceof Name; } -function IsCmd(v, cmd) { +function isCmd(v, cmd) { return v instanceof Cmd && (!cmd || v.cmd == cmd); } -function IsDict(v, type) { +function isDict(v, type) { return v instanceof Dict && (!type || v.get('Type').name == type); } -function IsArray(v) { +function isArray(v) { return v instanceof Array; } -function IsStream(v) { +function isStream(v) { return typeof v == 'object' && v != null && ('getChar' in v); } -function IsRef(v) { +function isRef(v) { return v instanceof Ref; } -function IsPDFFunction(v) { +function isPDFFunction(v) { var fnDict; if (typeof v != 'object') return false; - else if (IsDict(v)) + else if (isDict(v)) fnDict = v; - else if (IsStream(v)) + else if (isStream(v)) fnDict = v.dict; else return false; @@ -2407,13 +2438,13 @@ function IsPDFFunction(v) { var EOF = {}; -function IsEOF(v) { +function isEOF(v) { return v == EOF; } var None = {}; -function IsNone(v) { +function isNone(v) { return v == None; } @@ -2447,7 +2478,7 @@ var Lexer = (function lexer() { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx ]; - function ToHexDigit(ch) { + function toHexDigit(ch) { if (ch >= '0' && ch <= '9') return ch.charCodeAt(0) - 48; ch = ch.toUpperCase(); @@ -2576,10 +2607,10 @@ var Lexer = (function lexer() { stream.skip(); if (ch == '#') { ch = stream.lookChar(); - var x = ToHexDigit(ch); + var x = toHexDigit(ch); if (x != -1) { stream.skip(); - var x2 = ToHexDigit(stream.getChar()); + var x2 = toHexDigit(stream.getChar()); if (x2 == -1) error('Illegal digit in hex char in name: ' + x2); str += String.fromCharCode((x << 4) | x2); @@ -2610,14 +2641,14 @@ var Lexer = (function lexer() { } if (specialChars[ch.charCodeAt(0)] != 1) { var x, x2; - if ((x = ToHexDigit(ch)) == -1) + if ((x = toHexDigit(ch)) == -1) error('Illegal character in hex string: ' + ch); ch = stream.getChar(); while (specialChars[ch.charCodeAt(0)] == 1) ch = stream.getChar(); - if ((x2 = ToHexDigit(ch)) == -1) + if ((x2 = toHexDigit(ch)) == -1) error('Illegal character in hex string: ' + ch); str += String.fromCharCode((x << 4) | x2); @@ -2736,7 +2767,7 @@ var Parser = (function parserParser() { this.buf2 = this.lexer.getObj(); }, shift: function parserShift() { - if (IsCmd(this.buf2, 'ID')) { + if (isCmd(this.buf2, 'ID')) { this.buf1 = this.buf2; this.buf2 = null; // skip byte after ID @@ -2747,50 +2778,50 @@ var Parser = (function parserParser() { } }, getObj: function parserGetObj(cipherTransform) { - if (IsCmd(this.buf1, 'BI')) { // inline image + if (isCmd(this.buf1, 'BI')) { // inline image this.shift(); return this.makeInlineImage(cipherTransform); } - if (IsCmd(this.buf1, '[')) { // array + if (isCmd(this.buf1, '[')) { // array this.shift(); var array = []; - while (!IsCmd(this.buf1, ']') && !IsEOF(this.buf1)) + while (!isCmd(this.buf1, ']') && !isEOF(this.buf1)) array.push(this.getObj()); - if (IsEOF(this.buf1)) + if (isEOF(this.buf1)) error('End of file inside array'); this.shift(); return array; } - if (IsCmd(this.buf1, '<<')) { // dictionary or stream + if (isCmd(this.buf1, '<<')) { // dictionary or stream this.shift(); var dict = new Dict(); - while (!IsCmd(this.buf1, '>>') && !IsEOF(this.buf1)) { - if (!IsName(this.buf1)) { + while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) { + if (!isName(this.buf1)) { error('Dictionary key must be a name object'); } else { var key = this.buf1.name; this.shift(); - if (IsEOF(this.buf1)) + if (isEOF(this.buf1)) break; dict.set(key, this.getObj(cipherTransform)); } } - if (IsEOF(this.buf1)) + if (isEOF(this.buf1)) error('End of file inside dictionary'); // stream objects are not allowed inside content streams or // object streams - if (IsCmd(this.buf2, 'stream')) { + if (isCmd(this.buf2, 'stream')) { return this.allowStreams ? this.makeStream(dict, cipherTransform) : dict; } this.shift(); return dict; } - if (IsInt(this.buf1)) { // indirect reference or integer + if (isInt(this.buf1)) { // indirect reference or integer var num = this.buf1; this.shift(); - if (IsInt(this.buf1) && IsCmd(this.buf2, 'R')) { + if (isInt(this.buf1) && isCmd(this.buf2, 'R')) { var ref = new Ref(num, this.buf1); this.shift(); this.shift(); @@ -2798,7 +2829,7 @@ var Parser = (function parserParser() { } return num; } - if (IsString(this.buf1)) { // string + if (isString(this.buf1)) { // string var str = this.buf1; this.shift(); if (cipherTransform) @@ -2817,13 +2848,13 @@ var Parser = (function parserParser() { // parse dictionary var dict = new Dict(); - while (!IsCmd(this.buf1, 'ID') && !IsEOF(this.buf1)) { - if (!IsName(this.buf1)) { + while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) { + if (!isName(this.buf1)) { error('Dictionary key must be a name object'); } else { var key = this.buf1.name; this.shift(); - if (IsEOF(this.buf1)) + if (isEOF(this.buf1)) break; dict.set(key, this.getObj(cipherTransform)); } @@ -2832,14 +2863,37 @@ var Parser = (function parserParser() { // parse image stream var startPos = stream.pos; - var c1 = stream.getChar(); - var c2 = stream.getChar(); - while (!(c1 == 'E' && c2 == 'I') && c2 != null) { - c1 = c2; - c2 = stream.getChar(); + // searching for the /\sEI\s/ + var state = 0, ch; + while (state != 4 && (ch = stream.getByte()) != null) { + switch (ch) { + case 0x20: + case 0x0D: + case 0x0A: + state = state === 3 ? 4 : 1; + break; + case 0x45: + state = state === 1 ? 2 : 0; + break; + case 0x49: + state = state === 2 ? 3 : 0; + break; + default: + state = 0; + break; + } } - var length = (stream.pos - 2) - startPos; + // TODO improve the small images performance to remove the limit + var inlineImgLimit = 500; + if (++this.inlineImg >= inlineImgLimit) { + if (this.inlineImg === inlineImgLimit) + warn('Too many inline images'); + this.shift(); + return null; + } + + var length = (stream.pos - 4) - startPos; var imageStream = stream.makeSubStream(startPos, length, dict); if (cipherTransform) imageStream = cipherTransform.createStream(imageStream); @@ -2864,7 +2918,7 @@ var Parser = (function parserParser() { var xref = this.xref; if (xref) length = xref.fetchIfRef(length); - if (!IsInt(length)) { + if (!isInt(length)) { error('Bad ' + length + ' attribute in stream'); length = 0; } @@ -2873,7 +2927,7 @@ var Parser = (function parserParser() { stream.pos = pos + length; this.shift(); // '>>' this.shift(); // 'stream' - if (!IsCmd(this.buf1, 'endstream')) + if (!isCmd(this.buf1, 'endstream')) error('Missing endstream'); this.shift(); @@ -2887,18 +2941,18 @@ var Parser = (function parserParser() { filter: function parserFilter(stream, dict, length) { var filter = dict.get('Filter', 'F'); var params = dict.get('DecodeParms', 'DP'); - if (IsName(filter)) + if (isName(filter)) return this.makeFilter(stream, filter.name, length, params); - if (IsArray(filter)) { + if (isArray(filter)) { var filterArray = filter; var paramsArray = params; for (var i = 0, ii = filterArray.length; i < ii; ++i) { filter = filterArray[i]; - if (!IsName(filter)) + if (!isName(filter)) error('Bad filter name: ' + filter); else { params = null; - if (IsArray(paramsArray) && (i in paramsArray)) + if (isArray(paramsArray) && (i in paramsArray)) params = paramsArray[i]; stream = this.makeFilter(stream, filter.name, length, params); // after the first stream the length variable is invalid @@ -2949,10 +3003,10 @@ var Linearization = (function linearizationLinearization() { var obj2 = this.parser.getObj(); var obj3 = this.parser.getObj(); this.linDict = this.parser.getObj(); - if (IsInt(obj1) && IsInt(obj2) && IsCmd(obj3, 'obj') && - IsDict(this.linDict)) { + if (isInt(obj1) && isInt(obj2) && isCmd(obj3, 'obj') && + isDict(this.linDict)) { var obj = this.linDict.get('Linearized'); - if (!(IsNum(obj) && obj > 0)) + if (!(isNum(obj) && obj > 0)) this.linDict = null; } } @@ -2961,8 +3015,8 @@ var Linearization = (function linearizationLinearization() { getInt: function linearizationGetInt(name) { var linDict = this.linDict; var obj; - if (IsDict(linDict) && - IsInt(obj = linDict.get(name)) && + if (isDict(linDict) && + isInt(obj = linDict.get(name)) && obj > 0) { return obj; } @@ -2972,10 +3026,10 @@ var Linearization = (function linearizationLinearization() { getHint: function linearizationGetHint(index) { var linDict = this.linDict; var obj1, obj2; - if (IsDict(linDict) && - IsArray(obj1 = linDict.get('H')) && + if (isDict(linDict) && + isArray(obj1 = linDict.get('H')) && obj1.length >= 2 && - IsInt(obj2 = obj1[index]) && + isInt(obj2 = obj1[index]) && obj2 > 0) { return obj2; } @@ -2983,7 +3037,7 @@ var Linearization = (function linearizationLinearization() { return 0; }, get length() { - if (!IsDict(this.linDict)) + if (!isDict(this.linDict)) return 0; return this.getInt('L'); }, @@ -3037,7 +3091,7 @@ var XRef = (function xRefXRef() { } // get the root dictionary (catalog) object - if (!IsRef(this.root = trailerDict.get('Root'))) + if (!isRef(this.root = trailerDict.get('Root'))) error('Invalid root reference'); } @@ -3045,28 +3099,28 @@ var XRef = (function xRefXRef() { readXRefTable: function readXRefTable(parser) { var obj; while (true) { - if (IsCmd(obj = parser.getObj(), 'trailer')) + if (isCmd(obj = parser.getObj(), 'trailer')) break; - if (!IsInt(obj)) + if (!isInt(obj)) error('Invalid XRef table'); var first = obj; - if (!IsInt(obj = parser.getObj())) + if (!isInt(obj = parser.getObj())) error('Invalid XRef table'); var n = obj; if (first < 0 || n < 0 || (first + n) != ((first + n) | 0)) error('Invalid XRef table: ' + first + ', ' + n); for (var i = first; i < first + n; ++i) { var entry = {}; - if (!IsInt(obj = parser.getObj())) + if (!isInt(obj = parser.getObj())) error('Invalid XRef table: ' + first + ', ' + n); entry.offset = obj; - if (!IsInt(obj = parser.getObj())) + if (!isInt(obj = parser.getObj())) error('Invalid XRef table: ' + first + ', ' + n); entry.gen = obj; obj = parser.getObj(); - if (IsCmd(obj, 'n')) { + if (isCmd(obj, 'n')) { entry.uncompressed = true; - } else if (IsCmd(obj, 'f')) { + } else if (isCmd(obj, 'f')) { entry.free = true; } else { error('Invalid XRef table: ' + first + ', ' + n); @@ -3085,15 +3139,15 @@ var XRef = (function xRefXRef() { // read the trailer dictionary var dict; - if (!IsDict(dict = parser.getObj())) + if (!isDict(dict = parser.getObj())) error('Invalid XRef table'); // get the 'Prev' pointer var prev; obj = dict.get('Prev'); - if (IsInt(obj)) { + if (isInt(obj)) { prev = obj; - } else if (IsRef(obj)) { + } else if (isRef(obj)) { // certain buggy PDF generators generate "/Prev NNN 0 R" instead // of "/Prev NNN" prev = obj.num; @@ -3103,7 +3157,7 @@ var XRef = (function xRefXRef() { } // check for 'XRefStm' key - if (IsInt(obj = dict.get('XRefStm'))) { + if (isInt(obj = dict.get('XRefStm'))) { var pos = obj; // ignore previously loaded xref streams (possible infinite recursion) if (!(pos in this.xrefstms)) { @@ -3123,13 +3177,13 @@ var XRef = (function xRefXRef() { var i, j; while (range.length > 0) { var first = range[0], n = range[1]; - if (!IsInt(first) || !IsInt(n)) + if (!isInt(first) || !isInt(n)) error('Invalid XRef range fields: ' + first + ', ' + n); var typeFieldWidth = byteWidths[0]; var offsetFieldWidth = byteWidths[1]; var generationFieldWidth = byteWidths[2]; - if (!IsInt(typeFieldWidth) || !IsInt(offsetFieldWidth) || - !IsInt(generationFieldWidth)) { + if (!isInt(typeFieldWidth) || !isInt(offsetFieldWidth) || + !isInt(generationFieldWidth)) { error('Invalid XRef entry fields length: ' + first + ', ' + n); } for (i = 0; i < n; ++i) { @@ -3164,7 +3218,7 @@ var XRef = (function xRefXRef() { range.splice(0, 2); } var prev = streamParameters.get('Prev'); - if (IsInt(prev)) + if (isInt(prev)) this.readXRef(prev); return streamParameters; }, @@ -3261,11 +3315,11 @@ var XRef = (function xRefXRef() { stream.pos = trailers[i]; var parser = new Parser(new Lexer(stream), true); var obj = parser.getObj(); - if (!IsCmd(obj, 'trailer')) + if (!isCmd(obj, 'trailer')) continue; // read the trailer dictionary var dict; - if (!IsDict(dict = parser.getObj())) + if (!isDict(dict = parser.getObj())) continue; // taking the first one with 'ID' if (dict.has('ID')) @@ -3281,13 +3335,13 @@ var XRef = (function xRefXRef() { var parser = new Parser(new Lexer(stream), true); var obj = parser.getObj(); // parse an old-style xref table - if (IsCmd(obj, 'xref')) + if (isCmd(obj, 'xref')) return this.readXRefTable(parser); // parse an xref stream - if (IsInt(obj)) { - if (!IsInt(parser.getObj()) || - !IsCmd(parser.getObj(), 'obj') || - !IsStream(obj = parser.getObj())) { + if (isInt(obj)) { + if (!isInt(parser.getObj()) || + !isCmd(parser.getObj(), 'obj') || + !isStream(obj = parser.getObj())) { error('Invalid XRef stream'); } return this.readXRefStream(obj); @@ -3301,7 +3355,7 @@ var XRef = (function xRefXRef() { return e; }, fetchIfRef: function xRefFetchIfRef(obj) { - if (!IsRef(obj)) + if (!isRef(obj)) return obj; return this.fetch(obj); }, @@ -3322,12 +3376,12 @@ var XRef = (function xRefXRef() { var obj1 = parser.getObj(); var obj2 = parser.getObj(); var obj3 = parser.getObj(); - if (!IsInt(obj1) || obj1 != num || - !IsInt(obj2) || obj2 != gen || - !IsCmd(obj3)) { + if (!isInt(obj1) || obj1 != num || + !isInt(obj2) || obj2 != gen || + !isCmd(obj3)) { error('bad XRef entry'); } - if (!IsCmd(obj3, 'obj')) { + if (!isCmd(obj3, 'obj')) { // some bad pdfs use "obj1234" and really mean 1234 if (obj3.cmd.indexOf('obj') == 0) { num = parseInt(obj3.cmd.substring(3), 10); @@ -3349,18 +3403,18 @@ var XRef = (function xRefXRef() { e = parser.getObj(); } // Don't cache streams since they are mutable. - if (!IsStream(e)) + if (!isStream(e)) this.cache[num] = e; return e; } // compressed entry stream = this.fetch(new Ref(e.offset, 0)); - if (!IsStream(stream)) + if (!isStream(stream)) error('bad ObjStm stream'); var first = stream.parameters.get('First'); var n = stream.parameters.get('N'); - if (!IsInt(first) || !IsInt(n)) { + if (!isInt(first) || !isInt(n)) { error('invalid first and n parameters for ObjStm stream'); } parser = new Parser(new Lexer(stream), false); @@ -3368,12 +3422,12 @@ var XRef = (function xRefXRef() { // read the object numbers to populate cache for (i = 0; i < n; ++i) { num = parser.getObj(); - if (!IsInt(num)) { + if (!isInt(num)) { error('invalid object number in the ObjStm stream: ' + num); } nums.push(num); var offset = parser.getObj(); - if (!IsInt(offset)) { + if (!isInt(offset)) { error('invalid object offset in the ObjStm stream: ' + offset); } } @@ -3435,7 +3489,7 @@ var Page = (function pagePage() { get mediaBox() { var obj = this.inheritPageProp('MediaBox'); // Reset invalid media box to letter size. - if (!IsArray(obj) || obj.length !== 4) + if (!isArray(obj) || obj.length !== 4) obj = [0, 0, 612, 792]; return shadow(this, 'mediaBox', obj); }, @@ -3447,19 +3501,13 @@ var Page = (function pagePage() { 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; - } + if (isArray(obj) && obj.length == 4) { + var tl = this.rotatePoint(obj[0], obj[1]); + var br = this.rotatePoint(obj[2], obj[3]); + view.x = Math.min(tl.x, br.x); + view.y = Math.min(tl.y, br.y); + view.width = Math.abs(tl.x - br.x); + view.height = Math.abs(tl.y - br.y); } return shadow(this, 'cropBox', view); @@ -3554,7 +3602,7 @@ var Page = (function pagePage() { var xref = this.xref; var content = xref.fetchIfRef(this.content); var resources = xref.fetchIfRef(this.resources); - if (IsArray(content)) { + if (isArray(content)) { // fetching items var i, n = content.length; for (i = 0; i < n; ++i) @@ -3569,7 +3617,7 @@ var Page = (function pagePage() { var xref = this.xref; var resources = xref.fetchIfRef(this.resources); var mediaBox = xref.fetchIfRef(this.mediaBox); - assertWellFormed(IsDict(resources), 'invalid page resources'); + assertWellFormed(isDict(resources), 'invalid page resources'); gfx.beginDrawing({ x: mediaBox[0], y: mediaBox[1], width: this.width, height: this.height, @@ -3598,10 +3646,10 @@ var Page = (function pagePage() { var links = []; for (i = 0; i < n; ++i) { var annotation = xref.fetch(annotations[i]); - if (!IsDict(annotation)) + if (!isDict(annotation)) continue; var subtype = annotation.get('Subtype'); - if (!IsName(subtype) || subtype.name != 'Link') + if (!isName(subtype) || subtype.name != 'Link') continue; var rect = annotation.get('Rect'); var topLeftCorner = this.rotatePoint(rect[0], rect[1]); @@ -3627,7 +3675,7 @@ var Page = (function pagePage() { } else if (annotation.has('Dest')) { // simple destination link var dest = annotation.get('Dest'); - link.dest = IsName(dest) ? dest.name : dest; + link.dest = isName(dest) ? dest.name : dest; } links.push(link); } @@ -3642,16 +3690,16 @@ var Catalog = (function catalogCatalog() { function constructor(xref) { this.xref = xref; var obj = xref.getCatalogObj(); - assertWellFormed(IsDict(obj), 'catalog object is not a dictionary'); + assertWellFormed(isDict(obj), 'catalog object is not a dictionary'); this.catDict = obj; } constructor.prototype = { get toplevelPagesDict() { var pagesObj = this.catDict.get('Pages'); - assertWellFormed(IsRef(pagesObj), 'invalid top-level pages reference'); + assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference'); var xrefObj = this.xref.fetch(pagesObj); - assertWellFormed(IsDict(xrefObj), 'invalid top-level pages dictionary'); + assertWellFormed(isDict(xrefObj), 'invalid top-level pages dictionary'); // shadow the prototype getter return shadow(this, 'toplevelPagesDict', xrefObj); }, @@ -3659,10 +3707,10 @@ var Catalog = (function catalogCatalog() { var obj = this.catDict.get('Outlines'); var xref = this.xref; var root = { items: [] }; - if (IsRef(obj)) { + if (isRef(obj)) { obj = xref.fetch(obj).get('First'); var processed = new RefSet(); - if (IsRef(obj)) { + if (isRef(obj)) { var queue = [{obj: obj, parent: root}]; // to avoid recursion keeping track of the items // in the processed dictionary @@ -3677,7 +3725,7 @@ var Catalog = (function catalogCatalog() { dest = xref.fetchIfRef(dest).get('D'); else if (outlineDict.has('Dest')) { dest = outlineDict.get('Dest'); - if (IsName(dest)) + if (isName(dest)) dest = dest.name; } var title = xref.fetchIfRef(outlineDict.get('Title')); @@ -3692,12 +3740,12 @@ var Catalog = (function catalogCatalog() { }; i.parent.items.push(outlineItem); obj = outlineDict.get('First'); - if (IsRef(obj) && !processed.has(obj)) { + if (isRef(obj) && !processed.has(obj)) { queue.push({obj: obj, parent: outlineItem}); processed.put(obj); } obj = outlineDict.get('Next'); - if (IsRef(obj) && !processed.has(obj)) { + if (isRef(obj) && !processed.has(obj)) { queue.push({obj: obj, parent: i.parent}); processed.put(obj); } @@ -3710,7 +3758,7 @@ var Catalog = (function catalogCatalog() { get numPages() { var obj = this.toplevelPagesDict.get('Count'); assertWellFormed( - IsInt(obj), + isInt(obj), 'page count in top level pages object is not an integer' ); // shadow the prototype getter @@ -3719,18 +3767,18 @@ var Catalog = (function catalogCatalog() { traverseKids: function catalogTraverseKids(pagesDict) { var pageCache = this.pageCache; var kids = pagesDict.get('Kids'); - assertWellFormed(IsArray(kids), + assertWellFormed(isArray(kids), 'page dictionary kids object is not an array'); for (var i = 0; i < kids.length; ++i) { var kid = kids[i]; - assertWellFormed(IsRef(kid), + assertWellFormed(isRef(kid), 'page dictionary kid is not a reference'); var obj = this.xref.fetch(kid); - if (IsDict(obj, 'Page') || (IsDict(obj) && !obj.has('Kids'))) { + if (isDict(obj, 'Page') || (isDict(obj) && !obj.has('Kids'))) { pageCache.push(new Page(this.xref, pageCache.length, obj, kid)); } else { // must be a child page dictionary assertWellFormed( - IsDict(obj), + isDict(obj), 'page dictionary kid reference points to wrong type of object' ); this.traverseKids(obj); @@ -3740,7 +3788,7 @@ var Catalog = (function catalogCatalog() { get destinations() { function fetchDestination(xref, ref) { var dest = xref.fetchIfRef(ref); - return IsDict(dest) ? dest.get('D') : dest; + return isDict(dest) ? dest.get('D') : dest; } var xref = this.xref; @@ -3800,8 +3848,21 @@ var Catalog = (function catalogCatalog() { })(); var PDFDoc = (function pdfDoc() { - function constructor(data) { - var stream = new Stream(data); + function constructor(arg, callback) { + // Stream argument + if (typeof arg.isStream !== 'undefined') { + init.call(this, arg); + } + // ArrayBuffer argument + else if (typeof arg.byteLength !== 'undefined') { + init.call(this, new Stream(arg)); + } + else { + error('Unknown argument type'); + } + } + + function init(stream) { assertWellFormed(stream.length > 0, 'stream must have data'); this.stream = stream; this.setup(); @@ -4312,8 +4373,8 @@ var PartialEvaluator = (function partialEvaluator() { var parser = new Parser(new Lexer(stream), false); var args = [], argsArray = [], fnArray = [], obj; - while (!IsEOF(obj = parser.getObj())) { - if (IsCmd(obj)) { + while (!isEOF(obj = parser.getObj())) { + if (isCmd(obj)) { var cmd = obj.cmd; var fn = OP_MAP[cmd]; assertWellFormed(fn, "Unknown command '" + cmd + "'"); @@ -4323,10 +4384,10 @@ var PartialEvaluator = (function partialEvaluator() { // compile tiling patterns var patternName = args[args.length - 1]; // SCN/scn applies patterns along with normal colors - if (IsName(patternName)) { + if (isName(patternName)) { var pattern = xref.fetchIfRef(patterns.get(patternName.name)); if (pattern) { - var dict = IsStream(pattern) ? pattern.dict : pattern; + var dict = isStream(pattern) ? pattern.dict : pattern; var typeNum = dict.get('PatternType'); if (typeNum == 1) { patternName.code = this.evaluate(pattern, xref, @@ -4341,11 +4402,11 @@ var PartialEvaluator = (function partialEvaluator() { var xobj = xobjs.get(name); if (xobj) { xobj = xref.fetchIfRef(xobj); - assertWellFormed(IsStream(xobj), 'XObject should be a stream'); + assertWellFormed(isStream(xobj), 'XObject should be a stream'); var type = xobj.dict.get('Subtype'); assertWellFormed( - IsName(type), + isName(type), 'XObject should have a Name subtype' ); @@ -4362,7 +4423,7 @@ var PartialEvaluator = (function partialEvaluator() { if (fontRes) { fontRes = xref.fetchIfRef(fontRes); var font = xref.fetchIfRef(fontRes.get(args[0].name)); - assertWellFormed(IsDict(font)); + assertWellFormed(isDict(font)); if (!font.translated) { font.translated = this.translateFont(font, xref, resources); if (fonts && font.translated) { @@ -4377,7 +4438,7 @@ var PartialEvaluator = (function partialEvaluator() { fnArray.push(fn); argsArray.push(args); args = []; - } else { + } else if (obj != null) { assertWellFormed(args.length <= 33, 'Too many arguments'); args.push(obj); } @@ -4404,7 +4465,7 @@ var PartialEvaluator = (function partialEvaluator() { var start = 0, end = 0; for (var i = 0; i < widths.length; i++) { var code = widths[i]; - if (IsArray(code)) { + if (isArray(code)) { for (var j = 0; j < code.length; j++) glyphsWidths[start++] = code[j]; start = 0; @@ -4421,7 +4482,7 @@ var PartialEvaluator = (function partialEvaluator() { properties.widths = glyphsWidths; var cidToGidMap = dict.get('CIDToGIDMap'); - if (!cidToGidMap || !IsRef(cidToGidMap)) { + if (!cidToGidMap || !isRef(cidToGidMap)) { return Object.create(GlyphsUnicode); } @@ -4443,11 +4504,11 @@ var PartialEvaluator = (function partialEvaluator() { var width = glyphsWidths[code]; encoding[code] = { unicode: glyphID, - width: IsNum(width) ? width : defaultWidth + width: isNum(width) ? width : defaultWidth }; } } else if (type == 'CIDFontType0') { - if (IsName(encoding)) { + if (isName(encoding)) { // Encoding is a predefined CMap if (encoding.name == 'Identity-H') { TODO('Need to create an identity cmap'); @@ -4468,7 +4529,7 @@ var PartialEvaluator = (function partialEvaluator() { var baseEncoding = null; if (dict.has('Encoding')) { encoding = xref.fetchIfRef(dict.get('Encoding')); - if (IsDict(encoding)) { + if (isDict(encoding)) { var baseName = encoding.get('BaseEncoding'); if (baseName) baseEncoding = Encodings[baseName.name].slice(); @@ -4479,13 +4540,13 @@ var PartialEvaluator = (function partialEvaluator() { var index = 0; for (var j = 0; j < diffEncoding.length; j++) { var data = diffEncoding[j]; - if (IsNum(data)) + if (isNum(data)) index = data; else differences[index++] = data.name; } } - } else if (IsName(encoding)) { + } else if (isName(encoding)) { baseEncoding = Encodings[encoding.name].slice(); } else { error('Encoding is not a Name nor a Dict'); @@ -4503,6 +4564,8 @@ var PartialEvaluator = (function partialEvaluator() { break; default: warn('Unknown type of font: ' + type); + baseEncoding = []; + break; } } @@ -4513,21 +4576,20 @@ var PartialEvaluator = (function partialEvaluator() { var glyphs = {}; for (var i = firstChar; i <= lastChar; i++) { var glyph = differences[i]; + var replaceGlyph = true; if (!glyph) { glyph = baseEncoding[i]; - // skipping already specified by difference glyphs - if (differences.indexOf(glyph) >= 0) - continue; + replaceGlyph = false; } var index = GlyphsUnicode[glyph] || i; var width = widths[i] || widths[glyph]; map[i] = { unicode: index, - width: IsNum(width) ? width : properties.defaultWidth + width: isNum(width) ? width : properties.defaultWidth }; - if (glyph) - glyphs[glyph] = map[i]; + if (glyph && (replaceGlyph || !glyphs[glyph])) + glyphs[glyph] = map[i]; // If there is no file, the character mapping can't be modified // but this is unlikely that there is any standard encoding with @@ -4541,12 +4603,12 @@ var PartialEvaluator = (function partialEvaluator() { if (type == 'TrueType' && dict.has('ToUnicode') && differences) { var cmapObj = dict.get('ToUnicode'); - if (IsRef(cmapObj)) { + if (isRef(cmapObj)) { cmapObj = xref.fetch(cmapObj); } - if (IsName(cmapObj)) { + if (isName(cmapObj)) { error('ToUnicode file cmap translation not implemented'); - } else if (IsStream(cmapObj)) { + } else if (isStream(cmapObj)) { var tokens = []; var token = ''; var beginArrayToken = {}; @@ -4647,7 +4709,7 @@ var PartialEvaluator = (function partialEvaluator() { var defaultWidth = 0; var widths = Metrics[stdFontMap[name] || name]; - if (IsNum(widths)) { + if (isNum(widths)) { defaultWidth = widths; widths = null; } @@ -4663,7 +4725,7 @@ var PartialEvaluator = (function partialEvaluator() { resources) { var baseDict = dict; var type = dict.get('Subtype'); - assertWellFormed(IsName(type), 'invalid font Subtype'); + assertWellFormed(isName(type), 'invalid font Subtype'); var composite = false; if (type.name == 'Type0') { @@ -4675,13 +4737,13 @@ var PartialEvaluator = (function partialEvaluator() { if (!df) return null; - if (IsRef(df)) + if (isRef(df)) df = xref.fetch(df); - dict = xref.fetch(IsRef(df) ? df : df[0]); + dict = xref.fetch(isRef(df) ? df : df[0]); type = dict.get('Subtype'); - assertWellFormed(IsName(type), 'invalid font Subtype'); + assertWellFormed(isName(type), 'invalid font Subtype'); composite = true; } @@ -4697,7 +4759,7 @@ var PartialEvaluator = (function partialEvaluator() { // FontDescriptor was not required. // This case is here for compatibility. var baseFontName = dict.get('BaseFont'); - if (!IsName(baseFontName)) + if (!isName(baseFontName)) return null; // Using base font name as a font name. @@ -4742,7 +4804,7 @@ var PartialEvaluator = (function partialEvaluator() { } else { // Trying get the BaseFont metrics (see comment above). var baseFontName = dict.get('BaseFont'); - if (IsName(baseFontName)) { + if (isName(baseFontName)) { var metricsAndMap = this.getBaseFontMetricsAndMap(baseFontName.name); glyphWidths = metricsAndMap.widths; @@ -4752,7 +4814,7 @@ var PartialEvaluator = (function partialEvaluator() { } var fontName = xref.fetchIfRef(descriptor.get('FontName')); - assertWellFormed(IsName(fontName), 'invalid font name'); + assertWellFormed(isName(fontName), 'invalid font name'); var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3'); if (fontFile) { @@ -4763,11 +4825,11 @@ var PartialEvaluator = (function partialEvaluator() { subtype = subtype.name; var length1 = fontFile.dict.get('Length1'); - if (!IsInt(length1)) + if (!isInt(length1)) length1 = xref.fetchIfRef(length1); var length2 = fontFile.dict.get('Length2'); - if (!IsInt(length2)) + if (!isInt(length2)) length2 = xref.fetchIfRef(length2); } } @@ -4847,6 +4909,9 @@ var CanvasExtraState = (function canvasExtraState() { this.strokeColorSpaceObj = null; this.fillColorObj = null; this.strokeColorObj = null; + // Default fore and background colors + this.fillColor = "#000000"; + this.strokeColor = "#000000"; this.old = old; } @@ -4956,10 +5021,10 @@ var CanvasGraphics = (function canvasGraphics() { }, setGState: function canvasGraphicsSetGState(dictName) { var extGState = this.xref.fetchIfRef(this.res.get('ExtGState')); - if (IsDict(extGState) && extGState.has(dictName.name)) { + if (isDict(extGState) && extGState.has(dictName.name)) { var gsState = this.xref.fetchIfRef(extGState.get(dictName.name)); var self = this; - gsState.forEach(function(key, value) { + gsState.forEach(function canvasGraphicsSetGStateForEach(key, value) { switch (key) { case 'Type': break; @@ -5182,13 +5247,13 @@ var CanvasGraphics = (function canvasGraphics() { setFont: function canvasGraphicsSetFont(fontRef, size) { var font; // the tf command uses a name, but graphics state uses a reference - if (IsName(fontRef)) { + if (isName(fontRef)) { font = this.xref.fetchIfRef(this.res.get('Font')); - if (!IsDict(font)) + if (!isDict(font)) return; font = font.get(fontRef.name); - } else if (IsRef(fontRef)) { + } else if (isRef(fontRef)) { font = fontRef; } font = this.xref.fetchIfRef(font); @@ -5321,13 +5386,13 @@ var CanvasGraphics = (function canvasGraphics() { var arrLength = arr.length; for (var i = 0; i < arrLength; ++i) { var e = arr[i]; - if (IsNum(e)) { + if (isNum(e)) { if (ctx.$addCurrentX) { ctx.$addCurrentX(-e * 0.001 * fontSize); } else { current.x -= e * 0.001 * fontSize * textHScale; } - } else if (IsString(e)) { + } else if (isString(e)) { this.showText(e); } else { malformed('TJ array element ' + e + ' is not string or num'); @@ -5503,7 +5568,7 @@ var CanvasGraphics = (function canvasGraphics() { if (!xobj) return; xobj = this.xref.fetchIfRef(xobj); - assertWellFormed(IsStream(xobj), 'XObject should be a stream'); + assertWellFormed(isStream(xobj), 'XObject should be a stream'); var oc = xobj.dict.get('OC'); if (oc) { @@ -5516,7 +5581,7 @@ var CanvasGraphics = (function canvasGraphics() { } var type = xobj.dict.get('Subtype'); - assertWellFormed(IsName(type), 'XObject should have a Name subtype'); + assertWellFormed(isName(type), 'XObject should have a Name subtype'); if ('Image' == type.name) { this.paintImageXObject(obj, xobj, false); } else if ('Form' == type.name) { @@ -5532,11 +5597,11 @@ var CanvasGraphics = (function canvasGraphics() { this.save(); var matrix = stream.dict.get('Matrix'); - if (matrix && IsArray(matrix) && 6 == matrix.length) + if (matrix && isArray(matrix) && 6 == matrix.length) this.transform.apply(this, matrix); var bbox = stream.dict.get('BBox'); - if (bbox && IsArray(bbox) && 4 == bbox.length) { + if (bbox && isArray(bbox) && 4 == bbox.length) { this.rectangle.apply(this, bbox); this.clip(); this.endPath(); @@ -5693,9 +5758,9 @@ var ColorSpace = (function colorSpaceColorSpace() { }; constructor.parse = function colorspace_parse(cs, xref, res) { - if (IsName(cs)) { + if (isName(cs)) { var colorSpaces = xref.fetchIfRef(res.get('ColorSpace')); - if (IsDict(colorSpaces)) { + if (isDict(colorSpaces)) { var refcs = colorSpaces.get(cs.name); if (refcs) cs = refcs; @@ -5704,7 +5769,7 @@ var ColorSpace = (function colorSpaceColorSpace() { cs = xref.fetchIfRef(cs); - if (IsName(cs)) { + if (isName(cs)) { var mode = cs.name; this.mode = mode; @@ -5723,7 +5788,7 @@ var ColorSpace = (function colorSpaceColorSpace() { default: error('unrecognized colorspace ' + mode); } - } else if (IsArray(cs)) { + } else if (isArray(cs)) { var mode = cs[0].name; this.mode = mode; @@ -5841,10 +5906,10 @@ var IndexedCS = (function indexedCS() { var length = baseNumComps * highVal; var lookupArray = new Uint8Array(length); - if (IsStream(lookup)) { + if (isStream(lookup)) { var bytes = lookup.getBytes(length); lookupArray.set(bytes); - } else if (IsString(lookup)) { + } else if (isString(lookup)) { for (var i = 0; i < length; ++i) lookupArray[i] = lookup.charCodeAt(i); } else { @@ -6040,7 +6105,7 @@ var Pattern = (function patternPattern() { var length = args.length; var patternName = args[length - 1]; - if (!IsName(patternName)) + if (!isName(patternName)) error('Bad args to getPattern: ' + patternName); var patternRes = xref.fetchIfRef(res.get('Pattern')); @@ -6048,7 +6113,7 @@ var Pattern = (function patternPattern() { error('Unable to find pattern resource'); var pattern = xref.fetchIfRef(patternRes.get(patternName.name)); - var dict = IsStream(pattern) ? pattern.dict : pattern; + var dict = isStream(pattern) ? pattern.dict : pattern; var typeNum = dict.get('PatternType'); switch (typeNum) { @@ -6079,7 +6144,7 @@ var Pattern = (function patternPattern() { constructor.parseShading = function pattern_shading(shading, matrix, xref, res, ctx) { - var dict = IsStream(shading) ? shading.dict : shading; + var dict = isStream(shading) ? shading.dict : shading; var type = dict.get('ShadingType'); switch (type) { @@ -6142,9 +6207,9 @@ var RadialAxialShading = (function radialAxialShading() { var fnObj = dict.get('Function'); fnObj = xref.fetchIfRef(fnObj); - if (IsArray(fnObj)) + if (isArray(fnObj)) error('No support for array of functions'); - else if (!IsPDFFunction(fnObj)) + else if (!isPDFFunction(fnObj)) error('Invalid function'); var fn = new PDFFunction(xref, fnObj); @@ -6291,7 +6356,7 @@ var TilingPattern = (function tilingPattern() { graphics.transform.apply(graphics, tmpScale); graphics.transform.apply(graphics, tmpTranslate); - if (bbox && IsArray(bbox) && 4 == bbox.length) { + if (bbox && isArray(bbox) && 4 == bbox.length) { graphics.rectangle.apply(graphics, bbox); graphics.clip(); graphics.endPath(); @@ -6687,7 +6752,7 @@ var PDFFunction = (function pdfFunction() { var c1 = dict.get('C1') || [1]; var n = dict.get('N'); - if (!IsArray(c0) || !IsArray(c1)) + if (!isArray(c0) || !isArray(c1)) error('Illegal dictionary for interpolated function'); var length = c0.length; diff --git a/test/driver.js b/test/driver.js index 144b97589..7162af6f3 100644 --- a/test/driver.js +++ b/test/driver.js @@ -73,26 +73,16 @@ function nextTask() { log('Loading file "' + task.file + '"\n'); - var r = new XMLHttpRequest(); - r.open('GET', task.file); - r.mozResponseType = r.responseType = 'arraybuffer'; - r.onreadystatechange = function nextTaskOnreadystatechange() { + getPdf(task.file, function nextTaskGetPdf(data) { var failure; - if (r.readyState == 4) { - var data = r.mozResponseArrayBuffer || r.mozResponse || - r.responseArrayBuffer || r.response; - - try { - task.pdfDoc = new PDFDoc(data); - } catch (e) { - failure = 'load PDF doc : ' + e.toString(); - } - - task.pageNum = 1; - nextPage(task, failure); + try { + task.pdfDoc = new PDFDoc(data); + } catch (e) { + failure = 'load PDF doc : ' + e.toString(); } - }; - r.send(null); + task.pageNum = 1; + nextPage(task, failure); + }); } function isLastPage(task) { diff --git a/test/pdfs/unix01.pdf.link b/test/pdfs/unix01.pdf.link new file mode 100644 index 000000000..f8ad05e49 --- /dev/null +++ b/test/pdfs/unix01.pdf.link @@ -0,0 +1 @@ +https://docs.rice.edu/confluence/download/attachments/4588376/unix01.pdf?version=1 diff --git a/test/test_manifest.json b/test/test_manifest.json index 1d3904ca1..b61e28dbc 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -128,6 +128,12 @@ "rounds": 1, "type": "eq" }, + { "id": "unix01", + "file": "pdfs/unix01.pdf", + "link": true, + "rounds": 1, + "type": "eq" + }, { "id": "fips197", "file": "pdfs/fips197.pdf", "link": true, diff --git a/utils/fonts_utils.js b/utils/fonts_utils.js index 550637fff..3b5644ab4 100644 --- a/utils/fonts_utils.js +++ b/utils/fonts_utils.js @@ -258,7 +258,7 @@ var Type2Parser = function(aFilePath) { var count = decoded.length; for (var i = 0; i < count; i++) { var token = decoded[i]; - if (IsNum(token)) { + if (isNum(token)) { stack.push(token); } else { switch (token.operand) { diff --git a/web/viewer.html b/web/viewer.html index e22cacbf6..d964bbee6 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -28,7 +28,7 @@
- + / -- diff --git a/web/viewer.js b/web/viewer.js index 482ac95e0..230464473 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -9,6 +9,8 @@ var kDefaultScaleDelta = 1.1; var kCacheSize = 20; var kCssUnits = 96.0 / 72.0; var kScrollbarPadding = 40; +var kMinScale = 0.25; +var kMaxScale = 4.0; var Cache = function(size) { @@ -21,11 +23,13 @@ var Cache = function(size) { }; var cache = new Cache(kCacheSize); +var currentPageNumber = 1; var PDFView = { pages: [], thumbnails: [], currentScale: kDefaultScale, + initialBookmark: document.location.hash.substring(1), setScale: function(val, resetAutoSettings) { var pages = this.pages; @@ -33,11 +37,8 @@ var PDFView = { pages[i].update(val * kCssUnits); this.currentScale = val; - if (document.location.hash == '#' + this.page) - this.pages[this.page - 1].draw(); - else - // Jump the scroll position to the correct page. - document.location.hash = this.page; + this.pages[this.page - 1].scrollIntoView(); + this.pages[this.page - 1].draw(); var event = document.createEvent('UIEvents'); event.initUIEvent('scalechange', false, false, window, 0); @@ -72,11 +73,13 @@ var PDFView = { }, zoomIn: function() { - this.setScale(this.currentScale * kDefaultScaleDelta, true); + var newScale = Math.min(kMaxScale, this.currentScale * kDefaultScaleDelta); + this.setScale(newScale, true); }, zoomOut: function() { - this.setScale(this.currentScale / kDefaultScaleDelta, true); + var newScale = Math.max(kMinScale, this.currentScale / kDefaultScaleDelta); + this.setScale(newScale, true); }, set page(val) { @@ -87,18 +90,18 @@ var PDFView = { return; } - document.location.hash = val; + currentPageNumber = val; document.getElementById('previous').disabled = (val == 1); document.getElementById('next').disabled = (val == pages.length); - if (input.value == val) - return; + if (input.value != val) { + input.value = val; + } - input.value = val; - pages[val - 1].draw(); + pages[val - 1].scrollIntoView(); }, get page() { - return parseInt(document.location.hash.substring(1), 10) || 1; + return currentPageNumber; }, open: function(url, scale) { @@ -107,28 +110,18 @@ var PDFView = { document.title = url; - var xhr = new XMLHttpRequest(); - 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'; + getPdf( + { + url: url, + progress: function getPdfProgress(evt) { + if (evt.lengthComputable) + PDFView.progress(evt.loaded / evt.total); + }, + error: PDFView.error + }, + function getPdfLoad(data) { PDFView.load(data, scale); - } - }; - - xhr.send(null); - }, - - progressLevel: function(evt) { - var p = Math.round((evt.loaded / evt.total) * 100); - document.getElementById('loading').innerHTML = 'Loading... ' + p + '%'; + }); }, navigateTo: function(dest) { @@ -147,7 +140,36 @@ var PDFView = { } }, + getDestinationHash: function(dest) { + if (typeof dest === 'string') + return '#' + escape(dest); + if (dest instanceof Array) { + var destRef = dest[0]; // see nevigateTo method for dest format + var pageNumber = destRef instanceof Object ? + this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : + (destRef + 1); + if (pageNumber) { + return '#page=' + pageNumber + '&dest=' + dest.slice(1).join(','); + } + } + return ''; + }, + + error: function() { + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.innerHTML = 'Error'; + }, + + progress: function(level) { + var percent = Math.round(level * 100); + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.innerHTML = 'Loading... ' + percent + '%'; + }, + load: function(data, scale) { + var loadingIndicator = document.getElementById('loading'); + loadingIndicator.style.display = 'none'; + var sidebar = document.getElementById('sidebarView'); sidebar.parentNode.scrollTop = 0; @@ -162,6 +184,7 @@ var PDFView = { var pdf = new PDFDoc(data); var pagesCount = pdf.numPages; document.getElementById('numPages').innerHTML = pagesCount; + document.getElementById('pageNumber').max = pagesCount; var pages = this.pages = []; var pagesRefMap = {}; @@ -177,7 +200,7 @@ var PDFView = { } this.setScale(scale || kDefaultScale, true); - this.page = parseInt(document.location.hash.substring(1), 10) || 1; + this.pagesRefMap = pagesRefMap; this.destinations = pdf.catalog.destinations; if (pdf.catalog.documentOutline) { @@ -186,6 +209,28 @@ var PDFView = { outlineSwitchButton.removeAttribute('disabled'); this.switchSidebarView('outline'); } + + if (this.initialBookmark) { + this.setHash(this.initialBookmark); + this.initialBookmark = null; + } + else + this.page = 1; + }, + + setHash: function(hash) { + if (!hash) + return; + + if (hash.indexOf('=') >= 0) { + // TODO more complex hashes, for now catching page=XX only + var m = /\bpage=(\d+)/.exec(hash); + if (m && m[1] > 0) + this.page = m[1]; + } else if (/^\d+$/.test(hash)) // page number + this.page = hash; + else // named destination + PDFView.navigateTo(unescape(hash)); }, switchSidebarView: function(view) { @@ -270,6 +315,7 @@ var PageView = function(container, content, id, pageWidth, pageHeight, function setupLinks(content, scale) { function bindLink(link, dest) { + link.href = PDFView.getDestinationHash(dest); link.onclick = function() { if (dest) PDFView.navigateTo(dest); @@ -292,6 +338,11 @@ var PageView = function(container, content, id, pageWidth, pageHeight, } this.scrollIntoView = function(dest) { + if (!dest) { + div.scrollIntoView(true); + return; + } + var x = 0, y = 0; var width = 0, height = 0, widthScale, heightScale; var scale = 0; @@ -401,6 +452,10 @@ var PageView = function(container, content, id, pageWidth, pageHeight, var ThumbnailView = function(container, page, id, pageRatio) { var anchor = document.createElement('a'); anchor.href = '#' + id; + anchor.onclick = function stopNivigation() { + PDFView.page = id; + return false; + }; var div = document.createElement('div'); div.id = 'thumbnailContainer' + id; @@ -448,7 +503,7 @@ var DocumentOutlineView = function(outline) { var outlineView = document.getElementById('outlineView'); function bindItemLink(domObj, item) { - domObj.href = ''; + domObj.href = PDFView.getDestinationHash(item.dest); domObj.onclick = function(e) { PDFView.navigateTo(item.dest); return false; @@ -495,10 +550,18 @@ window.addEventListener('load', function(evt) { document.getElementById('fileInput').value = null; }, true); -window.addEventListener('pdfloaded', function(evt) { +window.addEventListener('pdfload', function(evt) { PDFView.load(evt.detail); }, true); +window.addEventListener('pdfprogress', function(evt) { + PDFView.progress(evt.detail); +}, true); + +window.addEventListener('pdferror', function(evt) { + PDFView.error(); +}, true); + function updateViewarea() { var visiblePages = PDFView.getVisiblePages(); for (var i = 0; i < visiblePages.length; i++) { @@ -531,7 +594,7 @@ window.addEventListener('resize', function onscroll(evt) { }); window.addEventListener('hashchange', function(evt) { - PDFView.page = PDFView.page; + PDFView.setHash(document.location.hash.substring(1)); }); window.addEventListener('change', function(evt) { @@ -557,7 +620,6 @@ window.addEventListener('change', function(evt) { fileReader.readAsBinaryString(file); document.title = file.name; - document.location.hash = 1; }, true); window.addEventListener('transitionend', function(evt) { @@ -609,13 +671,19 @@ window.addEventListener('scalechange', function scalechange(evt) { window.addEventListener('pagechange', function pagechange(evt) { var page = evt.detail; - document.location.hash = page; document.getElementById('pageNumber').value = page; document.getElementById('previous').disabled = (page == 1); document.getElementById('next').disabled = (page == PDFView.pages.length); }, true); window.addEventListener('keydown', function keydown(evt) { + var curElement = document.activeElement; + var controlsElement = document.getElementById('controls'); + while (curElement) { + if (curElement === controlsElement) + return; // ignoring if the 'controls' element is focused + curElement = curElement.parentNode; + } switch (evt.keyCode) { case 61: // FF/Mac '=' case 107: // FF '+' and '='