author | Ryan VanderMeulen <ryanvm@gmail.com> |
Fri, 09 May 2014 09:31:32 -0400 | |
changeset 182339 | 970b90975e1c2bc78fd041bf6bb9530668b30790 |
parent 182338 | 2e95c8a3da918ad901239edaa0429bea6dd37f7b |
child 182340 | d773600f10bdc4e7b37e84ebf7546ac7f11bbc26 |
push id | 26758 |
push user | ryanvm@gmail.com |
push date | Fri, 09 May 2014 19:37:26 +0000 |
treeherder | mozilla-central@007380afa681 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | yury, Mossop |
bugs | 1007627 |
milestone | 32.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,4 +1,4 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.0.68 +Current extension version is: 1.0.162
--- a/browser/extensions/pdfjs/content/PdfJs.jsm +++ b/browser/extensions/pdfjs/content/PdfJs.jsm @@ -59,17 +59,17 @@ function getIntPref(aPref, aDefaultValue } } function initializeDefaultPreferences() { var DEFAULT_PREFERENCES = { showPreviousViewOnLoad: true, defaultZoomValue: '', - ifAvailableShowOutlineOnLoad: false, + sidebarViewOnLoad: 0, enableHandToolOnLoad: false, enableWebGL: false }; var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); var defaultValue; for (var key in DEFAULT_PREFERENCES) {
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm +++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm @@ -48,22 +48,38 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, 'PdfJsTelemetry', 'resource://pdf.js/PdfJsTelemetry.jsm'); var Svc = {}; XPCOMUtils.defineLazyServiceGetter(Svc, 'mime', '@mozilla.org/mime;1', 'nsIMIMEService'); +function getContainingBrowser(domWindow) { + return domWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .chromeEventHandler; +} + function getChromeWindow(domWindow) { - var containingBrowser = domWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .chromeEventHandler; - return containingBrowser.ownerDocument.defaultView; + return getContainingBrowser(domWindow).ownerDocument.defaultView; +} + +function getFindBar(domWindow) { + var browser = getContainingBrowser(domWindow); + try { + var tabbrowser = browser.getTabBrowser(); + var tab = tabbrowser._getTabForBrowser(browser); + return tabbrowser.getFindBar(tab); + } catch (e) { + // FF22 has no _getTabForBrowser, and FF24 has no getFindBar + var chromeWindow = browser.ownerDocument.defaultView; + return chromeWindow.gFindBar; + } } function setBoolPref(pref, value) { Services.prefs.setBoolPref(pref, value); } function getBoolPref(pref, def) { try { @@ -269,19 +285,20 @@ ChromeActions.prototype = { if ('nsIPrivateBrowsingChannel' in Ci && channel instanceof Ci.nsIPrivateBrowsingChannel) { channel.setPrivate(docIsPrivate); } var listener = { extListener: null, onStartRequest: function(aRequest, aContext) { - this.extListener = extHelperAppSvc.doContent((data.isAttachment ? '' : - 'application/pdf'), - aRequest, frontWindow, false); + this.extListener = extHelperAppSvc.doContent( + (data.isAttachment ? 'application/octet-stream' : + 'application/pdf'), + aRequest, frontWindow, false); this.extListener.onStartRequest(aRequest, aContext); }, onStopRequest: function(aRequest, aContext, aStatusCode) { if (this.extListener) this.extListener.onStopRequest(aRequest, aContext, aStatusCode); // Notify the content code we're done downloading. if (sendResponse) sendResponse(false); @@ -311,21 +328,23 @@ ChromeActions.prototype = { log('Unable to retrive localized strings: ' + e); return 'null'; } }, pdfBugEnabled: function() { return getBoolPref(PREF_PREFIX + '.pdfBugEnabled', false); }, supportsIntegratedFind: function() { - // Integrated find is only supported when we're not in a frame and when the - // new find events code exists. - return this.domWindow.frameElement === null && - getChromeWindow(this.domWindow).gFindBar && - 'updateControlState' in getChromeWindow(this.domWindow).gFindBar; + // Integrated find is only supported when we're not in a frame + if (this.domWindow.frameElement !== null) { + return false; + } + // ... and when the new find events code exists. + var findBar = getFindBar(this.domWindow); + return findBar && ('updateControlState' in findBar); }, supportsDocumentFonts: function() { var prefBrowser = getIntPref('browser.display.use_document_fonts', 1); var prefGfx = getBoolPref('gfx.downloadable_fonts.enabled', true); return (!!prefBrowser && prefGfx); }, supportsDocumentColors: function() { return getBoolPref('browser.display.use_document_colors', true); @@ -432,18 +451,17 @@ ChromeActions.prototype = { // Verify what we're sending to the findbar. var result = data.result; var findPrevious = data.findPrevious; var findPreviousType = typeof findPrevious; if ((typeof result !== 'number' || result < 0 || result > 3) || (findPreviousType !== 'undefined' && findPreviousType !== 'boolean')) { return; } - getChromeWindow(this.domWindow).gFindBar - .updateControlState(result, findPrevious); + getFindBar(this.domWindow).updateControlState(result, findPrevious); }, setPreferences: function(prefs, sendResponse) { var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); var numberOfPrefs = 0; var prefValue, prefName; for (var key in prefs) { if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) { log('setPreferences - Exceeded the maximum number of preferences ' + @@ -911,17 +929,18 @@ PdfStreamConverter.prototype = { domWindow, contentDispositionFilename, dataListener); } var requestListener = new RequestListener(actions); domWindow.addEventListener(PDFJS_EVENT_ID, function(event) { requestListener.receive(event); }, false, true); if (actions.supportsIntegratedFind()) { var chromeWindow = getChromeWindow(domWindow); - var findEventManager = new FindEventManager(chromeWindow.gFindBar, + var findBar = getFindBar(domWindow); + var findEventManager = new FindEventManager(findBar, domWindow, chromeWindow); findEventManager.bind(); } listener.onStopRequest(aRequest, context, statusCode); } };
--- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -16,18 +16,18 @@ */ /*jshint globalstrict: false */ // Initializing PDFJS global object (if still undefined) if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.0.68'; -PDFJS.build = 'ead4cbf'; +PDFJS.version = '1.0.162'; +PDFJS.build = 'abc924b'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it 'use strict'; /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation @@ -901,32 +901,39 @@ function isPDFFunction(v) { fnDict = v.dict; } else { return false; } return fnDict.has('FunctionType'); } /** - * Legacy support for PDFJS Promise implementation. - * TODO remove eventually - * @ignore + * Promise Capability object. + * + * @typedef {Object} PromiseCapability + * @property {Promise} promise - A promise object. + * @property {function} resolve - Fullfills the promise. + * @property {function} reject - Rejects the promise. */ -var LegacyPromise = PDFJS.LegacyPromise = (function LegacyPromiseClosure() { - return function LegacyPromise() { - var resolve, reject; - var promise = new Promise(function (resolve_, reject_) { - resolve = resolve_; - reject = reject_; - }); - promise.resolve = resolve; - promise.reject = reject; - return promise; - }; -})(); + +/** + * Creates a promise capability object. + * @return {PromiseCapability} A capability object contains: + * - a Promise, resolve and reject methods. + */ +function createPromiseCapability() { + var capability = {}; + capability.promise = new Promise(function (resolve, reject) { + capability.resolve = resolve; + capability.reject = reject; + }); + return capability; +} + +PDFJS.createPromiseCapability = createPromiseCapability; /** * Polyfill for Promises: * The following promise implementation tries to generally implment the * Promise/A+ spec. Some notable differences from other promise libaries are: * - There currently isn't a seperate deferred and promise object. * - Unhandled rejections eventually show an error if they aren't handled. * @@ -955,18 +962,25 @@ var LegacyPromise = PDFJS.LegacyPromise }); if (count === 0) { resolve(results); } return promise; }; } if (typeof globalScope.Promise.resolve !== 'function') { - globalScope.Promise.resolve = function (x) { - return new globalScope.Promise(function (resolve) { resolve(x); }); + globalScope.Promise.resolve = function (value) { + return new globalScope.Promise(function (resolve) { resolve(value); }); + }; + } + if (typeof globalScope.Promise.reject !== 'function') { + globalScope.Promise.reject = function (reason) { + return new globalScope.Promise(function (resolve, reject) { + reject(reason); + }); }; } return; } throw new Error('DOM Promise is not present'); })(); var StatTimer = (function StatTimerClosure() { @@ -1283,26 +1297,27 @@ var ColorSpace = (function ColorSpaceClo rgbBuf = new Uint8Array(count * 3); this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc, /* alpha01 = */ 0); } } if (rgbBuf) { if (needsResizing) { - rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth, - originalHeight, width, height); - } - rgbPos = 0; - destPos = 0; - for (i = 0, ii = width * actualHeight; i < ii; i++) { - dest[destPos++] = rgbBuf[rgbPos++]; - dest[destPos++] = rgbBuf[rgbPos++]; - dest[destPos++] = rgbBuf[rgbPos++]; - destPos += alpha01; + PDFImage.resize(rgbBuf, bpc, 3, originalWidth, originalHeight, width, + height, dest, alpha01); + } else { + rgbPos = 0; + destPos = 0; + for (i = 0, ii = width * actualHeight; i < ii; i++) { + dest[destPos++] = rgbBuf[rgbPos++]; + dest[destPos++] = rgbBuf[rgbPos++]; + dest[destPos++] = rgbBuf[rgbPos++]; + destPos += alpha01; + } } } }, /** * True if the colorspace has components in the default range of [0, 1]. * This should be true for all colorspaces except for lab color spaces * which are [0,100], [-128, 127], [-128, 127]. */ @@ -1884,35 +1899,25 @@ var CalGrayCS = (function CalGrayCSClosu } function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) { // A represents a gray component of a calibrated gray space. // A <---> AG in the spec var A = src[srcOffset] * scale; var AG = Math.pow(A, cs.G); - // Computes intermediate variables M, L, N as per spec. + // Computes L as per spec. ( = cs.YW * AG ) // Except if other than default BlackPoint values are used. - var M = cs.XW * AG; var L = cs.YW * AG; - var N = cs.ZW * AG; - - // Decode XYZ, as per spec. - var X = M; - var Y = L; - var Z = N; - // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html, Ch 4. - // This yields values in range [0, 100]. - var Lstar = Math.max(116 * Math.pow(Y, 1 / 3) - 16, 0); - // Convert values to rgb range [0, 255]. - dest[destOffset] = Lstar * 255 / 100; - dest[destOffset + 1] = Lstar * 255 / 100; - dest[destOffset + 2] = Lstar * 255 / 100; + var val = Math.max(295.8 * Math.pow(L, 0.333333333333333333) - 40.8, 0) | 0; + dest[destOffset] = val; + dest[destOffset + 1] = val; + dest[destOffset + 2] = val; } CalGrayCS.prototype = { getRgb: ColorSpace.prototype.getRgb, getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, dest, destOffset) { convertToRgb(this, src, srcOffset, dest, destOffset, 1); }, @@ -2719,24 +2724,24 @@ var PostScriptEvaluator = (function Post break; case 'ne': b = stack.pop(); a = stack.pop(); stack.push(a != b); break; case 'neg': a = stack.pop(); - stack.push(-b); + stack.push(-a); break; case 'not': a = stack.pop(); - if (isBool(a) && isBool(b)) { - stack.push(a && b); + if (isBool(a)) { + stack.push(!a); } else { - stack.push(a & b); + stack.push(~a); } break; case 'or': b = stack.pop(); a = stack.pop(); if (isBool(a) && isBool(b)) { stack.push(a || b); } else { @@ -2792,16 +2797,17 @@ var PostScriptEvaluator = (function Post } return stack.stack; } }; return PostScriptEvaluator; })(); +var DEFAULT_ICON_SIZE = 22; // px var HIGHLIGHT_OFFSET = 4; // px var SUPPORTED_TYPES = ['Link', 'Text', 'Widget']; var Annotation = (function AnnotationClosure() { // 12.5.5: Algorithm: Appearance streams function getTransformMatrix(rect, bbox, matrix) { var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); var minX = bounds[0]; @@ -2965,41 +2971,39 @@ var Annotation = (function AnnotationClo var data = this.data; return !!(!this.isInvisible() && data && data.annotationFlags && // Default: not printable data.annotationFlags & 0x4 && // Print data.rect); // rectangle is nessessary }, - loadResources: function(keys) { - var promise = new LegacyPromise(); - this.appearance.dict.getAsync('Resources').then(function(resources) { - if (!resources) { - promise.resolve(); - return; - } - var objectLoader = new ObjectLoader(resources.map, - keys, - resources.xref); - objectLoader.load().then(function() { - promise.resolve(resources); - }); + loadResources: function Annotation_loadResources(keys) { + return new Promise(function (resolve, reject) { + this.appearance.dict.getAsync('Resources').then(function (resources) { + if (!resources) { + resolve(); + return; + } + var objectLoader = new ObjectLoader(resources.map, + keys, + resources.xref); + objectLoader.load().then(function() { + resolve(resources); + }, reject); + }, reject); }.bind(this)); - - return promise; }, getOperatorList: function Annotation_getOperatorList(evaluator) { - - var promise = new LegacyPromise(); + var capability = createPromiseCapability(); if (!this.appearance) { - promise.resolve(new OperatorList()); - return promise; + capability.resolve(new OperatorList()); + return capability.promise; } var data = this.data; var appearanceDict = this.appearance.dict; var resourcesPromise = this.loadResources([ 'ExtGState', 'ColorSpace', @@ -3014,22 +3018,22 @@ var Annotation = (function AnnotationClo var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0]; var transform = getTransformMatrix(data.rect, bbox, matrix); resourcesPromise.then(function(resources) { var opList = new OperatorList(); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); evaluator.getOperatorList(this.appearance, resources, opList); opList.addOp(OPS.endAnnotation, []); - promise.resolve(opList); + capability.resolve(opList); this.appearance.reset(); - }.bind(this)); - - return promise; + }.bind(this), capability.reject); + + return capability.promise; } }; Annotation.getConstructor = function Annotation_getConstructor(subtype, fieldType) { if (!subtype) { return; @@ -3099,40 +3103,40 @@ var Annotation = (function AnnotationClo warn('unimplemented annotation type: ' + subtype); } }; Annotation.appendToOperatorList = function Annotation_appendToOperatorList( annotations, opList, pdfManager, partialEvaluator, intent) { function reject(e) { - annotationsReadyPromise.reject(e); - } - - var annotationsReadyPromise = new LegacyPromise(); + annotationsReadyCapability.reject(e); + } + + var annotationsReadyCapability = createPromiseCapability(); var annotationPromises = []; for (var i = 0, n = annotations.length; i < n; ++i) { if (intent === 'display' && annotations[i].isViewable() || intent === 'print' && annotations[i].isPrintable()) { annotationPromises.push( annotations[i].getOperatorList(partialEvaluator)); } } Promise.all(annotationPromises).then(function(datas) { opList.addOp(OPS.beginAnnotations, []); for (var i = 0, n = datas.length; i < n; ++i) { var annotOpList = datas[i]; opList.addOpList(annotOpList); } opList.addOp(OPS.endAnnotations, []); - annotationsReadyPromise.resolve(); + annotationsReadyCapability.resolve(); }, reject); - return annotationsReadyPromise; + return annotationsReadyCapability.promise; }; return Annotation; })(); PDFJS.Annotation = Annotation; var WidgetAnnotation = (function WidgetAnnotationClosure() { @@ -3268,27 +3272,25 @@ var TextWidgetAnnotation = (function Tex return element; }, getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) { if (this.appearance) { return Annotation.prototype.getOperatorList.call(this, evaluator); } - var promise = new LegacyPromise(); var opList = new OperatorList(); var data = this.data; // Even if there is an appearance stream, ignore it. This is the // behaviour used by Adobe Reader. var defaultAppearance = data.defaultAppearance; if (!defaultAppearance) { - promise.resolve(opList); - return promise; + return Promise.resolve(opList); } // Include any font resources found in the default appearance var stream = new Stream(stringToBytes(defaultAppearance)); evaluator.getOperatorList(stream, this.fieldResources, opList); var appearanceFnArray = opList.fnArray; var appearanceArgsArray = opList.argsArray; @@ -3313,18 +3315,17 @@ var TextWidgetAnnotation = (function Tex } } else if (fnId === OPS.setFillRGBColor) { data.rgb = args; } else if (fnId === OPS.setFillGray) { var rgbValue = args[0] * 255; data.rgb = [rgbValue, rgbValue, rgbValue]; } } - promise.resolve(opList); - return promise; + return Promise.resolve(opList); } }); return TextWidgetAnnotation; })(); var InteractiveAnnotation = (function InteractiveAnnotationClosure() { function InteractiveAnnotation(params) { @@ -3395,16 +3396,18 @@ var TextAnnotation = (function TextAnnot var content = dict.get('Contents'); var title = dict.get('T'); data.content = stringToPDFString(content || ''); data.title = stringToPDFString(title || ''); if (data.hasAppearance) { data.name = 'NoIcon'; } else { + data.rect[1] = data.rect[3] - DEFAULT_ICON_SIZE; + data.rect[2] = data.rect[0] + DEFAULT_ICON_SIZE; data.name = dict.has('Name') ? dict.get('Name').name : 'Note'; } if (dict.has('C')) { data.hasBgColor = true; } } @@ -3777,17 +3780,17 @@ PDFJS.verbosity = (PDFJS.verbosity === u * * @return {Promise} A promise that is resolved with {@link PDFDocumentProxy} * object. */ PDFJS.getDocument = function getDocument(source, pdfDataRangeTransport, passwordCallback, progressCallback) { - var workerInitializedPromise, workerReadyPromise, transport; + var workerInitializedCapability, workerReadyCapability, transport; if (typeof source === 'string') { source = { url: source }; } else if (isArrayBuffer(source)) { source = { data: source }; } else if (typeof source !== 'object') { error('Invalid parameter in getDocument, need either Uint8Array, ' + 'string or a parameter object'); @@ -3802,25 +3805,26 @@ PDFJS.getDocument = function getDocument for (var key in source) { if (key === 'url' && typeof window !== 'undefined') { params[key] = combineUrl(window.location.href, source[key]); continue; } params[key] = source[key]; } - workerInitializedPromise = new PDFJS.LegacyPromise(); - workerReadyPromise = new PDFJS.LegacyPromise(); - transport = new WorkerTransport(workerInitializedPromise, workerReadyPromise, - pdfDataRangeTransport, progressCallback); - workerInitializedPromise.then(function transportInitialized() { + workerInitializedCapability = createPromiseCapability(); + workerReadyCapability = createPromiseCapability(); + transport = new WorkerTransport(workerInitializedCapability, + workerReadyCapability, pdfDataRangeTransport, + progressCallback); + workerInitializedCapability.promise.then(function transportInitialized() { transport.passwordCallback = passwordCallback; transport.fetchDocument(params); }); - return workerReadyPromise; + return workerReadyCapability.promise; }; /** * Proxy to a PDFDocument in the worker thread. Also, contains commonly used * properties that can be read synchronously. * @class */ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { @@ -3873,20 +3877,17 @@ var PDFDocumentProxy = (function PDFDocu getAttachments: function PDFDocumentProxy_getAttachments() { return this.transport.getAttachments(); }, /** * @return {Promise} A promise that is resolved with an array of all the * JavaScript strings in the name tree. */ getJavaScript: function PDFDocumentProxy_getJavaScript() { - var promise = new PDFJS.LegacyPromise(); - var js = this.pdfInfo.javaScript; - promise.resolve(js); - return promise; + return this.transport.getJavaScript(); }, /** * @return {Promise} A promise that is resolved with an {Array} that is a * tree outline (if it has one) of the PDF. The tree is in the format of: * [ * { * title: string, * bold: boolean, @@ -3894,53 +3895,43 @@ var PDFDocumentProxy = (function PDFDocu * color: rgb array, * dest: dest obj, * items: array of more items like this * }, * ... * ]. */ getOutline: function PDFDocumentProxy_getOutline() { - var promise = new PDFJS.LegacyPromise(); - var outline = this.pdfInfo.outline; - promise.resolve(outline); - return promise; + return this.transport.getOutline(); }, /** * @return {Promise} A promise that is resolved with an {Object} that has * info and metadata properties. Info is an {Object} filled with anything * available in the information dictionary and similarly metadata is a * {Metadata} object with information from the metadata section of the PDF. */ getMetadata: function PDFDocumentProxy_getMetadata() { - var promise = new PDFJS.LegacyPromise(); - var info = this.pdfInfo.info; - var metadata = this.pdfInfo.metadata; - promise.resolve({ - info: info, - metadata: (metadata ? new PDFJS.Metadata(metadata) : null) - }); - return promise; + return this.transport.getMetadata(); }, /** * @return {Promise} A promise that is resolved with a TypedArray that has * the raw data from the PDF. */ getData: function PDFDocumentProxy_getData() { - var promise = new PDFJS.LegacyPromise(); - this.transport.getData(promise); - return promise; + var capability = createPromiseCapability(); + this.transport.getData(capability); + return capability.promise; }, /** * @return {Promise} A promise that is resolved when the document's data * is loaded. It is resolved with an {Object} that contains the length * property that indicates size of the PDF data in bytes. */ getDownloadInfo: function PDFDocumentProxy_getDownloadInfo() { - return this.transport.downloadInfoPromise; + return this.transport.downloadInfoCapability.promise; }, /** * Cleans up resources allocated by the document, e.g. created @font-face. */ cleanup: function PDFDocumentProxy_cleanup() { this.transport.startCleanup(); }, /** @@ -4057,24 +4048,24 @@ var PDFPageProxy = (function PDFPageProx } return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0); }, /** * @return {Promise} A promise that is resolved with an {Array} of the * annotation objects. */ getAnnotations: function PDFPageProxy_getAnnotations() { - if (this.annotationsPromise) { - return this.annotationsPromise; - } - - var promise = new PDFJS.LegacyPromise(); - this.annotationsPromise = promise; + if (this.annotationsCapability) { + return this.annotationsCapability.promise; + } + + var capability = createPromiseCapability(); + this.annotationsCapability = capability; this.transport.getAnnotations(this.pageInfo.pageIndex); - return promise; + return capability.promise; }, /** * Begins the process of rendering a page to the desired context. * @param {RenderParameters} params Page render parameters. * @return {RenderTask} An object that contains the promise, which * is resolved when the page finishes rendering. */ render: function PDFPageProxy_render(params) { @@ -4088,21 +4079,21 @@ var PDFPageProxy = (function PDFPageProx var renderingIntent = ('intent' in params ? (params.intent == 'print' ? 'print' : 'display') : 'display'); if (!this.intentStates[renderingIntent]) { this.intentStates[renderingIntent] = {}; } var intentState = this.intentStates[renderingIntent]; - // If there is no displayReadyPromise yet, then the operatorList was never - // requested before. Make the request and create the promise. - if (!intentState.displayReadyPromise) { + // If there's no displayReadyCapability yet, then the operatorList + // was never requested before. Make the request and create the promise. + if (!intentState.displayReadyCapability) { intentState.receivingOperatorList = true; - intentState.displayReadyPromise = new LegacyPromise(); + intentState.displayReadyCapability = createPromiseCapability(); intentState.operatorList = { fnArray: [], argsArray: [], lastChunk: false }; this.stats.time('Page Request'); this.transport.messageHandler.send('RenderPageRequest', { @@ -4118,17 +4109,17 @@ var PDFPageProxy = (function PDFPageProx this.pageNumber); if (!intentState.renderTasks) { intentState.renderTasks = []; } intentState.renderTasks.push(internalRenderTask); var renderTask = new RenderTask(internalRenderTask); var self = this; - intentState.displayReadyPromise.then( + intentState.displayReadyCapability.promise.then( function pageDisplayReadyPromise(transparency) { if (self.pendingDestroy) { complete(); return; } stats.time('Rendering'); internalRenderTask.initalizeGraphics(transparency); internalRenderTask.operatorListChanged(); @@ -4145,40 +4136,40 @@ var PDFPageProxy = (function PDFPageProx } if (self.cleanupAfterRender) { self.pendingDestroy = true; } self._tryDestroy(); if (error) { - renderTask.promise.reject(error); + internalRenderTask.capability.reject(error); } else { - renderTask.promise.resolve(); + internalRenderTask.capability.resolve(); } stats.timeEnd('Rendering'); stats.timeEnd('Overall'); } return renderTask; }, /** * @return {Promise} That is resolved a {@link TextContent} * object that represent the page text content. */ getTextContent: function PDFPageProxy_getTextContent() { - var promise = new PDFJS.LegacyPromise(); - this.transport.messageHandler.send('GetTextContent', { - pageIndex: this.pageNumber - 1 - }, - function textContentCallback(textContent) { - promise.resolve(textContent); - } - ); - return promise; + return new Promise(function (resolve) { + this.transport.messageHandler.send('GetTextContent', { + pageIndex: this.pageNumber - 1 + }, + function textContentCallback(textContent) { + resolve(textContent); + } + ); + }.bind(this)); }, /** * Destroys resources allocated by the page. */ destroy: function PDFPageProxy_destroy() { this.pendingDestroy = true; this._tryDestroy(); }, @@ -4205,17 +4196,17 @@ var PDFPageProxy = (function PDFPageProx }, /** * For internal use only. * @ignore */ _startRenderPage: function PDFPageProxy_startRenderPage(transparency, intent) { var intentState = this.intentStates[intent]; - intentState.displayReadyPromise.resolve(transparency); + intentState.displayReadyCapability.resolve(transparency); }, /** * For internal use only. * @ignore */ _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk, intent) { var intentState = this.intentStates[intent]; @@ -4242,27 +4233,27 @@ var PDFPageProxy = (function PDFPageProx return PDFPageProxy; })(); /** * For internal use only. * @ignore */ var WorkerTransport = (function WorkerTransportClosure() { - function WorkerTransport(workerInitializedPromise, workerReadyPromise, + function WorkerTransport(workerInitializedCapability, workerReadyCapability, pdfDataRangeTransport, progressCallback) { this.pdfDataRangeTransport = pdfDataRangeTransport; - this.workerReadyPromise = workerReadyPromise; + this.workerReadyCapability = workerReadyCapability; this.progressCallback = progressCallback; this.commonObjs = new PDFObjects(); this.pageCache = []; - this.pagePromises = []; - this.downloadInfoPromise = new PDFJS.LegacyPromise(); + this.pageCapabilities = []; + this.downloadInfoCapability = createPromiseCapability(); this.passwordCallback = null; // If worker support isn't disabled explicit and the browser has worker // support, create a new web worker and test if it/the browser fullfills // all requirements to run parts of pdf.js in a web worker. // Right now, the requirement is, that an Uint8Array is still an Uint8Array // as it arrives on the worker. Chrome added this with version 15. if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { @@ -4281,22 +4272,22 @@ var WorkerTransport = (function WorkerTr messageHandler.on('test', function transportTest(data) { var supportTypedArray = data && data.supportTypedArray; if (supportTypedArray) { this.worker = worker; if (!data.supportTransfers) { PDFJS.postMessageTransfers = false; } this.setupMessageHandler(messageHandler); - workerInitializedPromise.resolve(); + workerInitializedCapability.resolve(); } else { globalScope.PDFJS.disableWorker = true; this.loadFakeWorkerFiles().then(function() { this.setupFakeWorker(); - workerInitializedPromise.resolve(); + workerInitializedCapability.resolve(); }.bind(this)); } }.bind(this)); var testObj = new Uint8Array([PDFJS.postMessageTransfers ? 255 : 0]); // Some versions of Opera throw a DATA_CLONE_ERR on serializing the // typed array. Also, checking if we can use transfers. try { @@ -4311,43 +4302,43 @@ var WorkerTransport = (function WorkerTr info('The worker has been disabled.'); } } // Either workers are disabled, not supported or have thrown an exception. // Thus, we fallback to a faked worker. globalScope.PDFJS.disableWorker = true; this.loadFakeWorkerFiles().then(function() { this.setupFakeWorker(); - workerInitializedPromise.resolve(); + workerInitializedCapability.resolve(); }.bind(this)); } WorkerTransport.prototype = { destroy: function WorkerTransport_destroy() { this.pageCache = []; - this.pagePromises = []; + this.pageCapabilities = []; var self = this; this.messageHandler.send('Terminate', null, function () { FontLoader.clear(); if (self.worker) { self.worker.terminate(); } }); }, loadFakeWorkerFiles: function WorkerTransport_loadFakeWorkerFiles() { - if (!PDFJS.fakeWorkerFilesLoadedPromise) { - PDFJS.fakeWorkerFilesLoadedPromise = new LegacyPromise(); + if (!PDFJS.fakeWorkerFilesLoadedCapability) { + PDFJS.fakeWorkerFilesLoadedCapability = createPromiseCapability(); // In the developer build load worker_loader which in turn loads all the // other files and resolves the promise. In production only the // pdf.worker.js file is needed. Util.loadScript(PDFJS.workerSrc, function() { - PDFJS.fakeWorkerFilesLoadedPromise.resolve(); + PDFJS.fakeWorkerFilesLoadedCapability.resolve(); }); } - return PDFJS.fakeWorkerFilesLoadedPromise; + return PDFJS.fakeWorkerFilesLoadedCapability.promise; }, setupFakeWorker: function WorkerTransport_setupFakeWorker() { warn('Setting up fake worker.'); // If we don't use a worker, just post/sendMessage to the main thread. var fakeWorker = { postMessage: function WorkerTransport_postMessage(obj) { fakeWorker.onmessage({data: obj}); @@ -4392,62 +4383,66 @@ var WorkerTransport = (function WorkerTr }, this); } messageHandler.on('GetDoc', function transportDoc(data) { var pdfInfo = data.pdfInfo; this.numPages = data.pdfInfo.numPages; var pdfDocument = new PDFDocumentProxy(pdfInfo, this); this.pdfDocument = pdfDocument; - this.workerReadyPromise.resolve(pdfDocument); + this.workerReadyCapability.resolve(pdfDocument); }, this); messageHandler.on('NeedPassword', function transportPassword(data) { if (this.passwordCallback) { return this.passwordCallback(updatePassword, PasswordResponses.NEED_PASSWORD); } - this.workerReadyPromise.reject(data.exception.message, data.exception); + this.workerReadyCapability.reject(data.exception.message, + data.exception); }, this); messageHandler.on('IncorrectPassword', function transportBadPass(data) { if (this.passwordCallback) { return this.passwordCallback(updatePassword, PasswordResponses.INCORRECT_PASSWORD); } - this.workerReadyPromise.reject(data.exception.message, data.exception); + this.workerReadyCapability.reject(data.exception.message, + data.exception); }, this); messageHandler.on('InvalidPDF', function transportInvalidPDF(data) { - this.workerReadyPromise.reject(data.exception.name, data.exception); + this.workerReadyCapability.reject(data.exception.name, data.exception); }, this); messageHandler.on('MissingPDF', function transportMissingPDF(data) { - this.workerReadyPromise.reject(data.exception.message, data.exception); + this.workerReadyCapability.reject(data.exception.message, + data.exception); }, this); messageHandler.on('UnknownError', function transportUnknownError(data) { - this.workerReadyPromise.reject(data.exception.message, data.exception); + this.workerReadyCapability.reject(data.exception.message, + data.exception); }, this); messageHandler.on('DataLoaded', function transportPage(data) { - this.downloadInfoPromise.resolve(data); + this.downloadInfoCapability.resolve(data); }, this); messageHandler.on('GetPage', function transportPage(data) { var pageInfo = data.pageInfo; var page = new PDFPageProxy(pageInfo, this); this.pageCache[pageInfo.pageIndex] = page; - var promise = this.pagePromises[pageInfo.pageIndex]; + var promise = this.pageCapabilities[pageInfo.pageIndex]; promise.resolve(page); }, this); messageHandler.on('GetAnnotations', function transportAnnotations(data) { var annotations = data.annotations; - var promise = this.pageCache[data.pageIndex].annotationsPromise; + var promise = this.pageCache[data.pageIndex].annotationsCapability; promise.resolve(annotations); }, this); messageHandler.on('StartRenderPage', function transportRender(data) { var page = this.pageCache[data.pageIndex]; page.stats.timeEnd('Page Request'); page._startRenderPage(data.transparency, data.intent); @@ -4531,24 +4526,24 @@ var WorkerTransport = (function WorkerTr this.progressCallback({ loaded: data.loaded, total: data.total }); } }, this); messageHandler.on('DocError', function transportDocError(data) { - this.workerReadyPromise.reject(data); + this.workerReadyCapability.reject(data); }, this); messageHandler.on('PageError', function transportError(data, intent) { var page = this.pageCache[data.pageNum - 1]; var intentState = page.intentStates[intent]; - if (intentState.displayReadyPromise) { - intentState.displayReadyPromise.reject(data.error); + if (intentState.displayReadyCapability.promise) { + intentState.displayReadyCapability.reject(data.error); } else { error(data.error); } }, this); messageHandler.on('JpegDecode', function(data, deferred) { var imageUrl = data[0]; var components = data[1]; @@ -4596,73 +4591,104 @@ var WorkerTransport = (function WorkerTr cMapUrl: PDFJS.cMapUrl, cMapPacked: PDFJS.cMapPacked, disableFontFace: PDFJS.disableFontFace, disableCreateObjectURL: PDFJS.disableCreateObjectURL, verbosity: PDFJS.verbosity }); }, - getData: function WorkerTransport_getData(promise) { + getData: function WorkerTransport_getData(capability) { this.messageHandler.send('GetData', null, function(data) { - promise.resolve(data); + capability.resolve(data); }); }, - getPage: function WorkerTransport_getPage(pageNumber, promise) { + getPage: function WorkerTransport_getPage(pageNumber, capability) { if (pageNumber <= 0 || pageNumber > this.numPages || (pageNumber|0) !== pageNumber) { - var pagePromise = new PDFJS.LegacyPromise(); - pagePromise.reject(new Error('Invalid page request')); - return pagePromise; + return new Promise.reject(new Error('Invalid page request')); } var pageIndex = pageNumber - 1; - if (pageIndex in this.pagePromises) { - return this.pagePromises[pageIndex]; - } - promise = new PDFJS.LegacyPromise(); - this.pagePromises[pageIndex] = promise; + if (pageIndex in this.pageCapabilities) { + return this.pageCapabilities[pageIndex].promise; + } + capability = createPromiseCapability(); + this.pageCapabilities[pageIndex] = capability; this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex }); - return promise; + return capability.promise; }, getPageIndex: function WorkerTransport_getPageIndexByRef(ref) { - var promise = new PDFJS.LegacyPromise(); - this.messageHandler.send('GetPageIndex', { ref: ref }, - function (pageIndex) { - promise.resolve(pageIndex); - } - ); - return promise; + return new Promise(function (resolve) { + this.messageHandler.send('GetPageIndex', { ref: ref }, + function (pageIndex) { + resolve(pageIndex); + } + ); + }.bind(this)); }, getAnnotations: function WorkerTransport_getAnnotations(pageIndex) { this.messageHandler.send('GetAnnotationsRequest', { pageIndex: pageIndex }); }, getDestinations: function WorkerTransport_getDestinations() { - var promise = new PDFJS.LegacyPromise(); - this.messageHandler.send('GetDestinations', null, - function transportDestinations(destinations) { - promise.resolve(destinations); - } - ); - return promise; + return new Promise(function (resolve) { + this.messageHandler.send('GetDestinations', null, + function transportDestinations(destinations) { + resolve(destinations); + } + ); + }.bind(this)); }, getAttachments: function WorkerTransport_getAttachments() { - var promise = new PDFJS.LegacyPromise(); - this.messageHandler.send('GetAttachments', null, - function transportAttachments(attachments) { - promise.resolve(attachments); - } - ); - return promise; + return new Promise(function (resolve) { + this.messageHandler.send('GetAttachments', null, + function transportAttachments(attachments) { + resolve(attachments); + } + ); + }.bind(this)); + }, + + getJavaScript: function WorkerTransport_getJavaScript() { + return new Promise(function (resolve) { + this.messageHandler.send('GetJavaScript', null, + function transportJavaScript(js) { + resolve(js); + } + ); + }.bind(this)); + }, + + getOutline: function WorkerTransport_getOutline() { + return new Promise(function (resolve) { + this.messageHandler.send('GetOutline', null, + function transportOutline(outline) { + resolve(outline); + } + ); + }.bind(this)); + }, + + getMetadata: function WorkerTransport_getMetadata() { + return new Promise(function (resolve) { + this.messageHandler.send('GetMetadata', null, + function transportMetadata(results) { + resolve({ + info: results[0], + metadata: (results[1] ? new PDFJS.Metadata(results[1]) : null) + }); + } + ); + }.bind(this)); }, startCleanup: function WorkerTransport_startCleanup() { this.messageHandler.send('Cleanup', null, function endCleanup() { for (var i = 0, ii = this.pageCache.length; i < ii; i++) { var page = this.pageCache[i]; if (page) { @@ -4697,17 +4723,17 @@ var PDFObjects = (function PDFObjectsClo * Ensures there is an object defined for `objId`. */ ensureObj: function PDFObjects_ensureObj(objId) { if (this.objs[objId]) { return this.objs[objId]; } var obj = { - promise: new LegacyPromise(), + capability: createPromiseCapability(), data: null, resolved: false }; this.objs[objId] = obj; return obj; }, @@ -4719,17 +4745,17 @@ var PDFObjects = (function PDFObjectsClo * object once the object is resolved. That means, if you call this * function and the object is already resolved, the callback gets called * right away. */ get: function PDFObjects_get(objId, callback) { // If there is a callback, then the get can be async and the object is // not required to be resolved right now if (callback) { - this.ensureObj(objId).promise.then(callback); + this.ensureObj(objId).capability.promise.then(callback); return null; } // If there isn't a callback, the user expects to get the resolved data // directly. var obj = this.objs[objId]; // If there isn't an object yet or the object isn't resolved, then the @@ -4744,17 +4770,17 @@ var PDFObjects = (function PDFObjectsClo /** * Resolves the object `objId` with optional `data`. */ resolve: function PDFObjects_resolve(objId, data) { var obj = this.ensureObj(objId); obj.resolved = true; obj.data = data; - obj.promise.resolve(data); + obj.capability.resolve(data); }, isResolved: function PDFObjects_isResolved(objId) { var objs = this.objs; if (!objs[objId]) { return false; } else { @@ -4791,28 +4817,27 @@ var PDFObjects = (function PDFObjectsClo */ var RenderTask = (function RenderTaskClosure() { function RenderTask(internalRenderTask) { this.internalRenderTask = internalRenderTask; /** * Promise for rendering task completion. * @type {Promise} */ - this.promise = new PDFJS.LegacyPromise(); + this.promise = this.internalRenderTask.capability.promise; } RenderTask.prototype = /** @lends RenderTask.prototype */ { /** * Cancels the rendering task. If the task is currently rendering it will * not be cancelled until graphics pauses with a timeout. The promise that * this object extends will resolved when cancelled. */ cancel: function RenderTask_cancel() { this.internalRenderTask.cancel(); - this.promise.reject(new Error('Rendering is cancelled')); }, /** * Registers callback to indicate the rendering task completion. * * @param {function} onFulfilled The callback for the rendering completion. * @param {function} onRejected The callback for the rendering failure. * @return {Promise} A promise that is resolved after the onFulfilled or @@ -4840,16 +4865,17 @@ var InternalRenderTask = (function Inter this.commonObjs = commonObjs; this.operatorListIdx = null; this.operatorList = operatorList; this.pageNumber = pageNumber; this.running = false; this.graphicsReadyCallback = null; this.graphicsReady = false; this.cancelled = false; + this.capability = createPromiseCapability(); } InternalRenderTask.prototype = { initalizeGraphics: function InternalRenderTask_initalizeGraphics(transparency) { if (this.cancelled) { @@ -5499,34 +5525,43 @@ var CanvasGraphics = (function CanvasGra dest32[destPos++] = 0; } ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); } } else if (imgData.kind === ImageKind.RGBA_32BPP) { // RGBA, 32-bits per pixel. - for (i = 0; i < totalChunks; i++) { - thisChunkHeight = - (i < fullChunks) ? fullChunkHeight : partialChunkHeight; - elemsInThisChunk = imgData.width * thisChunkHeight * 4; - + j = 0; + elemsInThisChunk = width * fullChunkHeight * 4; + for (i = 0; i < fullChunks; i++) { dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); srcPos += elemsInThisChunk; - ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); - } + ctx.putImageData(chunkImgData, 0, j); + j += fullChunkHeight; + } + if (i < totalChunks) { + elemsInThisChunk = width * partialChunkHeight * 4; + dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); + ctx.putImageData(chunkImgData, 0, j); + } + } else if (imgData.kind === ImageKind.RGB_24BPP) { // RGB, 24-bits per pixel. + thisChunkHeight = fullChunkHeight; + elemsInThisChunk = width * thisChunkHeight; for (i = 0; i < totalChunks; i++) { - thisChunkHeight = - (i < fullChunks) ? fullChunkHeight : partialChunkHeight; - elemsInThisChunk = imgData.width * thisChunkHeight * 3; + if (i >= fullChunks) { + thisChunkHeight =partialChunkHeight; + elemsInThisChunk = width * thisChunkHeight; + } + destPos = 0; - for (j = 0; j < elemsInThisChunk; j += 3) { + for (j = elemsInThisChunk; j--;) { dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++]; dest[destPos++] = 255; } ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); } } else {
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -16,18 +16,18 @@ */ /*jshint globalstrict: false */ // Initializing PDFJS global object (if still undefined) if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.0.68'; -PDFJS.build = 'ead4cbf'; +PDFJS.version = '1.0.162'; +PDFJS.build = 'abc924b'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it 'use strict'; /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation @@ -901,32 +901,39 @@ function isPDFFunction(v) { fnDict = v.dict; } else { return false; } return fnDict.has('FunctionType'); } /** - * Legacy support for PDFJS Promise implementation. - * TODO remove eventually - * @ignore + * Promise Capability object. + * + * @typedef {Object} PromiseCapability + * @property {Promise} promise - A promise object. + * @property {function} resolve - Fullfills the promise. + * @property {function} reject - Rejects the promise. */ -var LegacyPromise = PDFJS.LegacyPromise = (function LegacyPromiseClosure() { - return function LegacyPromise() { - var resolve, reject; - var promise = new Promise(function (resolve_, reject_) { - resolve = resolve_; - reject = reject_; - }); - promise.resolve = resolve; - promise.reject = reject; - return promise; - }; -})(); + +/** + * Creates a promise capability object. + * @return {PromiseCapability} A capability object contains: + * - a Promise, resolve and reject methods. + */ +function createPromiseCapability() { + var capability = {}; + capability.promise = new Promise(function (resolve, reject) { + capability.resolve = resolve; + capability.reject = reject; + }); + return capability; +} + +PDFJS.createPromiseCapability = createPromiseCapability; /** * Polyfill for Promises: * The following promise implementation tries to generally implment the * Promise/A+ spec. Some notable differences from other promise libaries are: * - There currently isn't a seperate deferred and promise object. * - Unhandled rejections eventually show an error if they aren't handled. * @@ -955,18 +962,25 @@ var LegacyPromise = PDFJS.LegacyPromise }); if (count === 0) { resolve(results); } return promise; }; } if (typeof globalScope.Promise.resolve !== 'function') { - globalScope.Promise.resolve = function (x) { - return new globalScope.Promise(function (resolve) { resolve(x); }); + globalScope.Promise.resolve = function (value) { + return new globalScope.Promise(function (resolve) { resolve(value); }); + }; + } + if (typeof globalScope.Promise.reject !== 'function') { + globalScope.Promise.reject = function (reason) { + return new globalScope.Promise(function (resolve, reject) { + reject(reason); + }); }; } return; } throw new Error('DOM Promise is not present'); })(); var StatTimer = (function StatTimerClosure() { @@ -1283,26 +1297,27 @@ var ColorSpace = (function ColorSpaceClo rgbBuf = new Uint8Array(count * 3); this.getRgbBuffer(comps, 0, count, rgbBuf, 0, bpc, /* alpha01 = */ 0); } } if (rgbBuf) { if (needsResizing) { - rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth, - originalHeight, width, height); - } - rgbPos = 0; - destPos = 0; - for (i = 0, ii = width * actualHeight; i < ii; i++) { - dest[destPos++] = rgbBuf[rgbPos++]; - dest[destPos++] = rgbBuf[rgbPos++]; - dest[destPos++] = rgbBuf[rgbPos++]; - destPos += alpha01; + PDFImage.resize(rgbBuf, bpc, 3, originalWidth, originalHeight, width, + height, dest, alpha01); + } else { + rgbPos = 0; + destPos = 0; + for (i = 0, ii = width * actualHeight; i < ii; i++) { + dest[destPos++] = rgbBuf[rgbPos++]; + dest[destPos++] = rgbBuf[rgbPos++]; + dest[destPos++] = rgbBuf[rgbPos++]; + destPos += alpha01; + } } } }, /** * True if the colorspace has components in the default range of [0, 1]. * This should be true for all colorspaces except for lab color spaces * which are [0,100], [-128, 127], [-128, 127]. */ @@ -1884,35 +1899,25 @@ var CalGrayCS = (function CalGrayCSClosu } function convertToRgb(cs, src, srcOffset, dest, destOffset, scale) { // A represents a gray component of a calibrated gray space. // A <---> AG in the spec var A = src[srcOffset] * scale; var AG = Math.pow(A, cs.G); - // Computes intermediate variables M, L, N as per spec. + // Computes L as per spec. ( = cs.YW * AG ) // Except if other than default BlackPoint values are used. - var M = cs.XW * AG; var L = cs.YW * AG; - var N = cs.ZW * AG; - - // Decode XYZ, as per spec. - var X = M; - var Y = L; - var Z = N; - // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html, Ch 4. - // This yields values in range [0, 100]. - var Lstar = Math.max(116 * Math.pow(Y, 1 / 3) - 16, 0); - // Convert values to rgb range [0, 255]. - dest[destOffset] = Lstar * 255 / 100; - dest[destOffset + 1] = Lstar * 255 / 100; - dest[destOffset + 2] = Lstar * 255 / 100; + var val = Math.max(295.8 * Math.pow(L, 0.333333333333333333) - 40.8, 0) | 0; + dest[destOffset] = val; + dest[destOffset + 1] = val; + dest[destOffset + 2] = val; } CalGrayCS.prototype = { getRgb: ColorSpace.prototype.getRgb, getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, dest, destOffset) { convertToRgb(this, src, srcOffset, dest, destOffset, 1); }, @@ -2719,24 +2724,24 @@ var PostScriptEvaluator = (function Post break; case 'ne': b = stack.pop(); a = stack.pop(); stack.push(a != b); break; case 'neg': a = stack.pop(); - stack.push(-b); + stack.push(-a); break; case 'not': a = stack.pop(); - if (isBool(a) && isBool(b)) { - stack.push(a && b); + if (isBool(a)) { + stack.push(!a); } else { - stack.push(a & b); + stack.push(~a); } break; case 'or': b = stack.pop(); a = stack.pop(); if (isBool(a) && isBool(b)) { stack.push(a || b); } else { @@ -2792,16 +2797,17 @@ var PostScriptEvaluator = (function Post } return stack.stack; } }; return PostScriptEvaluator; })(); +var DEFAULT_ICON_SIZE = 22; // px var HIGHLIGHT_OFFSET = 4; // px var SUPPORTED_TYPES = ['Link', 'Text', 'Widget']; var Annotation = (function AnnotationClosure() { // 12.5.5: Algorithm: Appearance streams function getTransformMatrix(rect, bbox, matrix) { var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix); var minX = bounds[0]; @@ -2965,41 +2971,39 @@ var Annotation = (function AnnotationClo var data = this.data; return !!(!this.isInvisible() && data && data.annotationFlags && // Default: not printable data.annotationFlags & 0x4 && // Print data.rect); // rectangle is nessessary }, - loadResources: function(keys) { - var promise = new LegacyPromise(); - this.appearance.dict.getAsync('Resources').then(function(resources) { - if (!resources) { - promise.resolve(); - return; - } - var objectLoader = new ObjectLoader(resources.map, - keys, - resources.xref); - objectLoader.load().then(function() { - promise.resolve(resources); - }); + loadResources: function Annotation_loadResources(keys) { + return new Promise(function (resolve, reject) { + this.appearance.dict.getAsync('Resources').then(function (resources) { + if (!resources) { + resolve(); + return; + } + var objectLoader = new ObjectLoader(resources.map, + keys, + resources.xref); + objectLoader.load().then(function() { + resolve(resources); + }, reject); + }, reject); }.bind(this)); - - return promise; }, getOperatorList: function Annotation_getOperatorList(evaluator) { - - var promise = new LegacyPromise(); + var capability = createPromiseCapability(); if (!this.appearance) { - promise.resolve(new OperatorList()); - return promise; + capability.resolve(new OperatorList()); + return capability.promise; } var data = this.data; var appearanceDict = this.appearance.dict; var resourcesPromise = this.loadResources([ 'ExtGState', 'ColorSpace', @@ -3014,22 +3018,22 @@ var Annotation = (function AnnotationClo var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0]; var transform = getTransformMatrix(data.rect, bbox, matrix); resourcesPromise.then(function(resources) { var opList = new OperatorList(); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); evaluator.getOperatorList(this.appearance, resources, opList); opList.addOp(OPS.endAnnotation, []); - promise.resolve(opList); + capability.resolve(opList); this.appearance.reset(); - }.bind(this)); - - return promise; + }.bind(this), capability.reject); + + return capability.promise; } }; Annotation.getConstructor = function Annotation_getConstructor(subtype, fieldType) { if (!subtype) { return; @@ -3099,40 +3103,40 @@ var Annotation = (function AnnotationClo warn('unimplemented annotation type: ' + subtype); } }; Annotation.appendToOperatorList = function Annotation_appendToOperatorList( annotations, opList, pdfManager, partialEvaluator, intent) { function reject(e) { - annotationsReadyPromise.reject(e); - } - - var annotationsReadyPromise = new LegacyPromise(); + annotationsReadyCapability.reject(e); + } + + var annotationsReadyCapability = createPromiseCapability(); var annotationPromises = []; for (var i = 0, n = annotations.length; i < n; ++i) { if (intent === 'display' && annotations[i].isViewable() || intent === 'print' && annotations[i].isPrintable()) { annotationPromises.push( annotations[i].getOperatorList(partialEvaluator)); } } Promise.all(annotationPromises).then(function(datas) { opList.addOp(OPS.beginAnnotations, []); for (var i = 0, n = datas.length; i < n; ++i) { var annotOpList = datas[i]; opList.addOpList(annotOpList); } opList.addOp(OPS.endAnnotations, []); - annotationsReadyPromise.resolve(); + annotationsReadyCapability.resolve(); }, reject); - return annotationsReadyPromise; + return annotationsReadyCapability.promise; }; return Annotation; })(); PDFJS.Annotation = Annotation; var WidgetAnnotation = (function WidgetAnnotationClosure() { @@ -3268,27 +3272,25 @@ var TextWidgetAnnotation = (function Tex return element; }, getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) { if (this.appearance) { return Annotation.prototype.getOperatorList.call(this, evaluator); } - var promise = new LegacyPromise(); var opList = new OperatorList(); var data = this.data; // Even if there is an appearance stream, ignore it. This is the // behaviour used by Adobe Reader. var defaultAppearance = data.defaultAppearance; if (!defaultAppearance) { - promise.resolve(opList); - return promise; + return Promise.resolve(opList); } // Include any font resources found in the default appearance var stream = new Stream(stringToBytes(defaultAppearance)); evaluator.getOperatorList(stream, this.fieldResources, opList); var appearanceFnArray = opList.fnArray; var appearanceArgsArray = opList.argsArray; @@ -3313,18 +3315,17 @@ var TextWidgetAnnotation = (function Tex } } else if (fnId === OPS.setFillRGBColor) { data.rgb = args; } else if (fnId === OPS.setFillGray) { var rgbValue = args[0] * 255; data.rgb = [rgbValue, rgbValue, rgbValue]; } } - promise.resolve(opList); - return promise; + return Promise.resolve(opList); } }); return TextWidgetAnnotation; })(); var InteractiveAnnotation = (function InteractiveAnnotationClosure() { function InteractiveAnnotation(params) { @@ -3395,16 +3396,18 @@ var TextAnnotation = (function TextAnnot var content = dict.get('Contents'); var title = dict.get('T'); data.content = stringToPDFString(content || ''); data.title = stringToPDFString(title || ''); if (data.hasAppearance) { data.name = 'NoIcon'; } else { + data.rect[1] = data.rect[3] - DEFAULT_ICON_SIZE; + data.rect[2] = data.rect[0] + DEFAULT_ICON_SIZE; data.name = dict.has('Name') ? dict.get('Name').name : 'Note'; } if (dict.has('C')) { data.hasBgColor = true; } } @@ -3879,46 +3882,47 @@ var ChunkedStreamManager = (function Chu } this.currRequestId = 0; this.chunksNeededByRequest = {}; this.requestsByChunk = {}; this.callbacksByRequest = {}; - this.loadedStream = new LegacyPromise(); + this._loadedStreamCapability = createPromiseCapability(); + if (args.initialData) { this.setInitialData(args.initialData); } } ChunkedStreamManager.prototype = { setInitialData: function ChunkedStreamManager_setInitialData(data) { this.stream.onReceiveInitialData(data); if (this.stream.allChunksLoaded()) { - this.loadedStream.resolve(this.stream); + this._loadedStreamCapability.resolve(this.stream); } else if (this.msgHandler) { this.msgHandler.send('DocProgress', { loaded: data.length, total: this.length }); } }, onLoadedStream: function ChunkedStreamManager_getLoadedStream() { - return this.loadedStream; + return this._loadedStreamCapability.promise; }, // Get all the chunks that are not yet loaded and groups them into // contiguous ranges to load in as few requests as possible requestAllChunks: function ChunkedStreamManager_requestAllChunks() { var missingChunks = this.stream.getMissingChunks(); this.requestChunks(missingChunks); - return this.loadedStream; + return this._loadedStreamCapability.promise; }, requestChunks: function ChunkedStreamManager_requestChunks(chunks, callback) { var requestId = this.currRequestId++; var chunksNeeded; var i, ii; @@ -4044,17 +4048,17 @@ var ChunkedStreamManager = (function Chu var begin = args.begin; var end = begin + chunk.byteLength; var beginChunk = this.getBeginChunk(begin); var endChunk = this.getEndChunk(end); this.stream.onReceiveData(begin, chunk); if (this.stream.allChunksLoaded()) { - this.loadedStream.resolve(this.stream); + this._loadedStreamCapability.resolve(this.stream); } var loadedRequests = []; var i, requestId; for (chunk = beginChunk; chunk < endChunk; ++chunk) { // The server might return more chunks than requested var requestIds = this.requestsByChunk[chunk] || []; delete this.requestsByChunk[chunk]; @@ -4104,16 +4108,20 @@ var ChunkedStreamManager = (function Chu } this.msgHandler.send('DocProgress', { loaded: this.stream.numChunksLoaded * this.chunkSize, total: this.length }); }, + onError: function ChunkedStreamManager_onError(err) { + this._loadedStreamCapability.reject(err); + }, + getBeginChunk: function ChunkedStreamManager_getBeginChunk(begin) { var chunk = Math.floor(begin / this.chunkSize); return chunk; }, getEndChunk: function ChunkedStreamManager_getEndChunk(end) { if (end % this.chunkSize === 0) { return end / this.chunkSize; @@ -4177,73 +4185,75 @@ var BasePdfManager = (function BasePdfMa }, requestLoadedStream: function BasePdfManager_requestLoadedStream() { return new NotImplementedException(); }, updatePassword: function BasePdfManager_updatePassword(password) { this.pdfDocument.xref.password = this.password = password; - if (this.passwordChangedPromise) { - this.passwordChangedPromise.resolve(); - } + if (this._passwordChangedCapability) { + this._passwordChangedCapability.resolve(); + } + }, + + passwordChanged: function BasePdfManager_passwordChanged() { + this._passwordChangedCapability = createPromiseCapability(); + return this._passwordChangedCapability.promise; }, terminate: function BasePdfManager_terminate() { return new NotImplementedException(); } }; return BasePdfManager; })(); var LocalPdfManager = (function LocalPdfManagerClosure() { function LocalPdfManager(data, password) { var stream = new Stream(data); this.pdfDocument = new PDFDocument(this, stream, password); - this.loadedStream = new LegacyPromise(); - this.loadedStream.resolve(stream); + this._loadedStreamCapability = createPromiseCapability(); + this._loadedStreamCapability.resolve(stream); } LocalPdfManager.prototype = Object.create(BasePdfManager.prototype); LocalPdfManager.prototype.constructor = LocalPdfManager; LocalPdfManager.prototype.ensure = function LocalPdfManager_ensure(obj, prop, args) { - var promise = new LegacyPromise(); - try { - var value = obj[prop]; - var result; - if (typeof(value) === 'function') { - result = value.apply(obj, args); - } else { - result = value; - } - promise.resolve(result); - } catch (e) { - console.log(e.stack); - promise.reject(e); - } - return promise; + return new Promise(function (resolve, reject) { + try { + var value = obj[prop]; + var result; + if (typeof value === 'function') { + result = value.apply(obj, args); + } else { + result = value; + } + resolve(result); + } catch (e) { + reject(e); + } + }); }; LocalPdfManager.prototype.requestRange = function LocalPdfManager_requestRange(begin, end) { - var promise = new LegacyPromise(); - promise.resolve(); - return promise; + return Promise.resolve(); }; LocalPdfManager.prototype.requestLoadedStream = function LocalPdfManager_requestLoadedStream() { }; LocalPdfManager.prototype.onLoadedStream = function LocalPdfManager_getLoadedStream() { - return this.loadedStream; + return this._loadedStreamCapability.promise; }; LocalPdfManager.prototype.terminate = function LocalPdfManager_terminate() { return; }; return LocalPdfManager; @@ -4269,52 +4279,49 @@ var NetworkPdfManager = (function Networ args.password); } NetworkPdfManager.prototype = Object.create(BasePdfManager.prototype); NetworkPdfManager.prototype.constructor = NetworkPdfManager; NetworkPdfManager.prototype.ensure = function NetworkPdfManager_ensure(obj, prop, args) { - var promise = new LegacyPromise(); - this.ensureHelper(promise, obj, prop, args); - return promise; - }; - - NetworkPdfManager.prototype.ensureHelper = - function NetworkPdfManager_ensureHelper(promise, obj, prop, args) { - try { - var result; - var value = obj[prop]; - if (typeof(value) === 'function') { - result = value.apply(obj, args); - } else { - result = value; - } - promise.resolve(result); - } catch(e) { - if (!(e instanceof MissingDataException)) { - console.log(e.stack); - promise.reject(e); - return; - } - - this.streamManager.requestRange(e.begin, e.end, function() { - this.ensureHelper(promise, obj, prop, args); - }.bind(this)); - } + var pdfManager = this; + + return new Promise(function (resolve, reject) { + function ensureHelper() { + try { + var result; + var value = obj[prop]; + if (typeof value === 'function') { + result = value.apply(obj, args); + } else { + result = value; + } + resolve(result); + } catch(e) { + if (!(e instanceof MissingDataException)) { + reject(e); + return; + } + pdfManager.streamManager.requestRange(e.begin, e.end, ensureHelper); + } + } + + ensureHelper(); + }); }; NetworkPdfManager.prototype.requestRange = function NetworkPdfManager_requestRange(begin, end) { - var promise = new LegacyPromise(); - this.streamManager.requestRange(begin, end, function() { - promise.resolve(); - }); - return promise; + return new Promise(function (resolve) { + this.streamManager.requestRange(begin, end, function() { + resolve(); + }); + }.bind(this)); }; NetworkPdfManager.prototype.requestLoadedStream = function NetworkPdfManager_requestLoadedStream() { this.streamManager.requestAllChunks(); }; NetworkPdfManager.prototype.onLoadedStream = @@ -4445,42 +4452,38 @@ var Page = (function PageClosure() { stream = content; } else { // replacing non-existent page content with empty one stream = new NullStream(); } return stream; }, - loadResources: function(keys) { + loadResources: function Page_loadResources(keys) { if (!this.resourcesPromise) { // TODO: add async getInheritedPageProp and remove this. this.resourcesPromise = this.pdfManager.ensure(this, 'resources'); } - var promise = new LegacyPromise(); - this.resourcesPromise.then(function resourceSuccess() { + return this.resourcesPromise.then(function resourceSuccess() { var objectLoader = new ObjectLoader(this.resources.map, keys, this.xref); - objectLoader.load().then(function objectLoaderSuccess() { - promise.resolve(); - }); + return objectLoader.load(); }.bind(this)); - return promise; }, getOperatorList: function Page_getOperatorList(handler, intent) { var self = this; - var promise = new LegacyPromise(); + var capability = createPromiseCapability(); function reject(e) { - promise.reject(e); - } - - var pageListPromise = new LegacyPromise(); + capability.reject(e); + } + + var pageListCapability = createPromiseCapability(); var pdfManager = this.pdfManager; var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', []); var resourcesPromise = this.loadResources([ 'ExtGState', 'ColorSpace', 'Pattern', @@ -4504,39 +4507,40 @@ var Page = (function PageClosure() { var opList = new OperatorList(intent, handler, self.pageIndex); handler.send('StartRenderPage', { transparency: partialEvaluator.hasBlendModes(self.resources), pageIndex: self.pageIndex, intent: intent }); partialEvaluator.getOperatorList(contentStream, self.resources, opList); - pageListPromise.resolve(opList); + pageListCapability.resolve(opList); }); var annotationsPromise = pdfManager.ensure(this, 'annotations'); - Promise.all([pageListPromise, annotationsPromise]).then(function(datas) { + Promise.all([pageListCapability.promise, annotationsPromise]).then( + function(datas) { var pageOpList = datas[0]; var annotations = datas[1]; if (annotations.length === 0) { pageOpList.flush(true); - promise.resolve(pageOpList); + capability.resolve(pageOpList); return; } var annotationsReadyPromise = Annotation.appendToOperatorList( annotations, pageOpList, pdfManager, partialEvaluator, intent); annotationsReadyPromise.then(function () { pageOpList.flush(true); - promise.resolve(pageOpList); + capability.resolve(pageOpList); }, reject); }, reject); - return promise; + return capability.promise; }, extractTextContent: function Page_extractTextContent() { var handler = { on: function nullHandlerOn() {}, send: function nullHandlerSend() {} }; @@ -4933,43 +4937,36 @@ var Dict = (function DictClosure() { } value = this.map[key3] || null; return xref ? xref.fetchIfRef(value) : value; }, // Same as get(), but returns a promise and uses fetchIfRefAsync(). getAsync: function Dict_getAsync(key1, key2, key3) { var value; - var promise; var xref = this.xref; if (typeof (value = this.map[key1]) !== undefined || key1 in this.map || typeof key2 === undefined) { if (xref) { return xref.fetchIfRefAsync(value); } - promise = new LegacyPromise(); - promise.resolve(value); - return promise; + return Promise.resolve(value); } if (typeof (value = this.map[key2]) !== undefined || key2 in this.map || typeof key3 === undefined) { if (xref) { return xref.fetchIfRefAsync(value); } - promise = new LegacyPromise(); - promise.resolve(value); - return promise; + return Promise.resolve(value); } value = this.map[key3] || null; if (xref) { return xref.fetchIfRefAsync(value); } - promise = new LegacyPromise(); - promise.resolve(value); - return promise; + return Promise.resolve(value); }, // no dereferencing getRaw: function Dict_getRaw(key) { return this.map[key]; }, // creates new map and dereferences all Refs @@ -5356,39 +5353,39 @@ var Catalog = (function CatalogClosure() this.fontCache); }.bind(this) ); } return this.pagePromises[pageIndex]; }, getPageDict: function Catalog_getPageDict(pageIndex) { - var promise = new LegacyPromise(); + var capability = createPromiseCapability(); var nodesToVisit = [this.catDict.getRaw('Pages')]; var currentPageIndex = 0; var xref = this.xref; function next() { while (nodesToVisit.length) { var currentNode = nodesToVisit.pop(); if (isRef(currentNode)) { xref.fetchAsync(currentNode).then(function (obj) { if ((isDict(obj, 'Page') || (isDict(obj) && !obj.has('Kids')))) { if (pageIndex === currentPageIndex) { - promise.resolve([obj, currentNode]); + capability.resolve([obj, currentNode]); } else { currentPageIndex++; next(); } return; } nodesToVisit.push(obj); next(); - }.bind(this), promise.reject.bind(promise)); + }.bind(this), capability.reject.bind(capability)); return; } // must be a child page dictionary assert( isDict(currentNode), 'page dictionary kid reference points to wrong type of object' ); @@ -5413,20 +5410,20 @@ var Catalog = (function CatalogClosure() currentPageIndex = pageIndex; continue; } else { for (var last = kids.length - 1; last >= 0; last--) { nodesToVisit.push(kids[last]); } } } - promise.reject('Page index ' + pageIndex + ' not found.'); + capability.reject('Page index ' + pageIndex + ' not found.'); } next(); - return promise; + return capability.promise; }, getPageIndex: function Catalog_getPageIndex(ref) { // The page tree nodes have the count of all the leaves below them. To get // how many pages are before we just have to walk up the tree and keep // adding the count of siblings to the left of the node. var xref = this.xref; function pagesBeforeRef(kidRef) { @@ -5558,16 +5555,21 @@ var XRef = (function XRefClosure() { // << /Size 22 // /Root 20R // /Info 10R // /ID [ <81b14aafa313db63dbd6f981e49f94f4> ] // >> // The parser goes through the entire stream << ... >> and provides // a getter interface for the key-value table var dict = parser.getObj(); + + // The pdflib PDF generator can generate a nested trailer dictionary + if (!isDict(dict) && dict.dict) { + dict = dict.dict; + } if (!isDict(dict)) { error('Invalid XRef table: could not parse trailer dictionary'); } delete this.tableState; return dict; }, @@ -6074,39 +6076,37 @@ var XRef = (function XRefClosure() { if (xrefEntry === undefined) { error('bad XRef entry for compressed object'); } return xrefEntry; }, fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) { if (!isRef(obj)) { - var promise = new LegacyPromise(); - promise.resolve(obj); - return promise; + return Promise.resolve(obj); } return this.fetchAsync(obj); }, fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) { - var promise = new LegacyPromise(); - var tryFetch = function (promise) { - try { - promise.resolve(this.fetch(ref, suppressEncryption)); - } catch (e) { - if (e instanceof MissingDataException) { - this.stream.manager.requestRange(e.begin, e.end, tryFetch); - return; - } - promise.reject(e); - } - }.bind(this, promise); - tryFetch(); - return promise; - }, + return new Promise(function (resolve, reject) { + var tryFetch = function () { + try { + resolve(this.fetch(ref, suppressEncryption)); + } catch (e) { + if (e instanceof MissingDataException) { + this.stream.manager.requestRange(e.begin, e.end, tryFetch); + return; + } + reject(e); + } + }.bind(this); + tryFetch(); + }.bind(this)); + }, getCatalogObj: function XRef_getCatalogObj() { return this.root; } }; return XRef; })(); @@ -6301,33 +6301,33 @@ var ObjectLoader = (function() { this.keys = keys; this.xref = xref; this.refSet = null; } ObjectLoader.prototype = { load: function ObjectLoader_load() { var keys = this.keys; - this.promise = new LegacyPromise(); + this.capability = createPromiseCapability(); // Don't walk the graph if all the data is already loaded. if (!(this.xref.stream instanceof ChunkedStream) || this.xref.stream.getMissingChunks().length === 0) { - this.promise.resolve(); - return this.promise; + this.capability.resolve(); + return this.capability.promise; } this.refSet = new RefSet(); // Setup the initial nodes to visit. var nodesToVisit = []; for (var i = 0; i < keys.length; i++) { nodesToVisit.push(this.obj[keys[i]]); } this.walk(nodesToVisit); - return this.promise; + return this.capability.promise; }, walk: function ObjectLoader_walk(nodesToVisit) { var nodesToRevisit = []; var pendingRequests = []; // DFS walk of the object graph. while (nodesToVisit.length) { var currentNode = nodesToVisit.pop(); @@ -6384,17 +6384,17 @@ var ObjectLoader = (function() { } } this.walk(nodesToVisit); }.bind(this)); return; } // Everything is loaded. this.refSet = null; - this.promise.resolve(); + this.capability.resolve(); } }; return ObjectLoader; })(); var ISOAdobeCharset = [ @@ -18355,17 +18355,17 @@ var symbolsFonts = { 'Dingbats': true, 'Symbol': true, 'ZapfDingbats': true }; // Glyph map for well-known standard fonts. Sometimes Ghostscript uses CID fonts // but does not embed the CID to GID mapping. The mapping is incomplete for all // glyphs, but common for some set of the standard fonts. var GlyphMapForStandardFonts = { '2': 10, '3': 32, '4': 33, '5': 34, '6': 35, '7': 36, '8': 37, '9': 38, - '10': 39, '11': 40, '12': 41, '13': 42, '14': 43, '15': 44, '16': 173, + '10': 39, '11': 40, '12': 41, '13': 42, '14': 43, '15': 44, '16': 45, '17': 46, '18': 47, '19': 48, '20': 49, '21': 50, '22': 51, '23': 52, '24': 53, '25': 54, '26': 55, '27': 56, '28': 57, '29': 58, '30': 894, '31': 60, '32': 61, '33': 62, '34': 63, '35': 64, '36': 65, '37': 66, '38': 67, '39': 68, '40': 69, '41': 70, '42': 71, '43': 72, '44': 73, '45': 74, '46': 75, '47': 76, '48': 77, '49': 78, '50': 79, '51': 80, '52': 81, '53': 82, '54': 83, '55': 84, '56': 85, '57': 86, '58': 87, '59': 88, '60': 89, '61': 90, '62': 91, '63': 92, '64': 93, '65': 94, '66': 95, '67': 96, '68': 97, '69': 98, '70': 99, '71': 100, '72': 101, @@ -18379,17 +18379,19 @@ var GlyphMapForStandardFonts = { '119': 239, '120': 241, '121': 243, '122': 242, '123': 244, '124': 246, '125': 245, '126': 250, '127': 249, '128': 251, '129': 252, '130': 8224, '131': 176, '132': 162, '133': 163, '134': 167, '135': 8226, '136': 182, '137': 223, '138': 174, '139': 169, '140': 8482, '141': 180, '142': 168, '143': 8800, '144': 198, '145': 216, '146': 8734, '147': 177, '148': 8804, '149': 8805, '150': 165, '151': 181, '152': 8706, '153': 8721, '154': 8719, '156': 8747, '157': 170, '158': 186, '159': 8486, '160': 230, '161': 248, '162': 191, '163': 161, '164': 172, '165': 8730, '166': 402, '167': 8776, - '168': 8710, '169': 171, '170': 187, '171': 8230, '210': 218, '305': 963, + '168': 8710, '169': 171, '170': 187, '171': 8230, '210': 218, '223': 711, + '227': 353, '229': 382, '234': 253, '253': 268, '254': 269, '258': 258, + '268': 283, '269': 313, '278': 328, '284': 345, '292': 367, '305': 963, '306': 964, '307': 966, '308': 8215, '309': 8252, '310': 8319, '311': 8359, '312': 8592, '313': 8593, '337': 9552, '493': 1039, '494': 1040, '705': 1524, '706': 8362, '710': 64288, '711': 64298, '759': 1617, '761': 1776, '763': 1778, '775': 1652, '777': 1764, '778': 1780, '779': 1781, '780': 1782, '782': 771, '783': 64726, '786': 8363, '788': 8532, '790': 768, '791': 769, '792': 768, '795': 803, '797': 64336, '798': 64337, '799': 64342, '800': 64343, '801': 64344, '802': 64345, '803': 64362, '804': 64363, '805': 64364, '2424': 7821, '2425': 7822, '2426': 7823, '2427': 7824, @@ -20183,17 +20185,22 @@ var Font = (function FontClosure() { this.loadedName = fontName.split('-')[0]; this.loading = false; return; } // Some fonts might use wrong font types for Type1C or CIDFontType0C var subtype = properties.subtype; if (subtype == 'Type1C' && (type != 'Type1' && type != 'MMType1')) { - type = 'Type1'; + // Some TrueType fonts by mistake claim Type1C + if (isTrueTypeFile(file)) { + subtype = 'TrueType'; + } else { + type = 'Type1'; + } } if (subtype == 'CIDFontType0C' && type != 'CIDFontType0') { type = 'CIDFontType0'; } // XXX: Temporarily change the type for open type so we trigger a warning. // This should be removed when we add support for open type. if (subtype === 'OpenType') { type = 'OpenType'; @@ -20331,16 +20338,21 @@ var Font = (function FontClosure() { } var tableEntry = (tag + string32(checksum) + string32(offset) + string32(length)); file.file += tableEntry; file.virtualOffset += data.length; } + function isTrueTypeFile(file) { + var header = file.peekBytes(4); + return readUint32(header, 0) === 0x00010000; + } + /** * Rebuilds the char code to glyph ID map by trying to replace the char codes * with their unicode value. It also moves char codes that are in known * problematic locations. * @return {Object} Two properties: * 'toFontChar' - maps original char codes(the value that will be read * from commands such as show text) to the char codes that will be used in the * font that we build @@ -20373,16 +20385,18 @@ var Font = (function FontClosure() { // font was symbolic and there is only an identity unicode map since the // characters probably aren't in the correct position (fixes an issue // with firefox and thuluthfont). if ((fontCharCode in usedFontCharCodes || fontCharCode <= 0x1f || // Control chars fontCharCode === 0x7F || // Control char fontCharCode === 0xAD || // Soft hyphen (fontCharCode >= 0x80 && fontCharCode <= 0x9F) || // Control chars + // Prevent drawing characters in the specials unicode block. + (fontCharCode >= 0xFFF0 && fontCharCode <= 0xFFFF) || (isSymbolic && isIdentityUnicode)) && nextAvailableFontCharCode <= PRIVATE_USE_OFFSET_END) { // Room left. // Loop to try and find a free spot in the private use area. do { fontCharCode = nextAvailableFontCharCode++; if (SKIP_PRIVATE_USE_RANGE_F000_TO_F01F && fontCharCode === 0xF000) { fontCharCode = 0xF020; @@ -29825,25 +29839,27 @@ var PDFImage = (function PDFImageClosure }); return new Promise(function (resolve) { resolvePromise = resolve; }); } else { return Promise.resolve(image); } } + /** * Decode and clamp a value. The formula is different from the spec because we * don't decode to float range [0,1], we decode it in the [0,max] range. */ function decodeAndClamp(value, addend, coefficient, max) { value = addend + value * coefficient; // Clamp the value to the range return (value < 0 ? 0 : (value > max ? max : value)); } + function PDFImage(xref, res, image, inline, smask, mask, isMask) { this.image = image; var dict = image.dict; if (dict.has('Filter')) { var filter = dict.get('Filter').name; if (filter === 'JPXDecode') { var jpxImage = new JpxImage(); jpxImage.parseImageProperties(image.stream); @@ -29979,40 +29995,60 @@ var PDFImage = (function PDFImageClosure * supports one and three component images. * @param {TypedArray} pixels The original image with one component. * @param {Number} bpc Number of bits per component. * @param {Number} components Number of color components, 1 or 3 is supported. * @param {Number} w1 Original width. * @param {Number} h1 Original height. * @param {Number} w2 New width. * @param {Number} h2 New height. + * @param {TypedArray} dest (Optional) The destination buffer. + * @param {Number} alpha01 (Optional) Size reserved for the alpha channel. * @return {TypedArray} Resized image data. */ PDFImage.resize = function PDFImage_resize(pixels, bpc, components, - w1, h1, w2, h2) { + w1, h1, w2, h2, dest, alpha01) { + + if (components !== 1 && components !== 3) { + error('Unsupported component count for resizing.'); + } + var length = w2 * h2 * components; - var temp = (bpc <= 8 ? new Uint8Array(length) : + var temp = dest ? dest : (bpc <= 8 ? new Uint8Array(length) : (bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length))); var xRatio = w1 / w2; var yRatio = h1 / h2; - var px, py, newIndex, oldIndex; - for (var i = 0; i < h2; i++) { - for (var j = 0; j < w2; j++) { - px = Math.floor(j * xRatio); - py = Math.floor(i * yRatio); - newIndex = (i * w2) + j; - oldIndex = ((py * w1) + px); - if (components === 1) { - temp[newIndex] = pixels[oldIndex]; - } else if (components === 3) { - newIndex *= 3; - oldIndex *= 3; - temp[newIndex] = pixels[oldIndex]; - temp[newIndex + 1] = pixels[oldIndex + 1]; - temp[newIndex + 2] = pixels[oldIndex + 2]; + var i, j, py, newIndex = 0, oldIndex; + var xScaled = new Uint16Array(w2); + var w1Scanline = w1 * components; + if (alpha01 !== 1) { + alpha01 = 0; + } + + for (j = 0; j < w2; j++) { + xScaled[j] = Math.floor(j * xRatio) * components; + } + + if (components === 1) { + for (i = 0; i < h2; i++) { + py = Math.floor(i * yRatio) * w1Scanline; + for (j = 0; j < w2; j++) { + oldIndex = py + xScaled[j]; + temp[newIndex++] = pixels[oldIndex]; + } + } + } else if (components === 3) { + for (i = 0; i < h2; i++) { + py = Math.floor(i * yRatio) * w1Scanline; + for (j = 0; j < w2; j++) { + oldIndex = py + xScaled[j]; + temp[newIndex++] = pixels[oldIndex++]; + temp[newIndex++] = pixels[oldIndex++]; + temp[newIndex++] = pixels[oldIndex++]; + newIndex += alpha01; } } } return temp; }; PDFImage.createMask = function PDFImage_createMask(imgArray, width, height, canTransfer, @@ -30040,21 +30076,23 @@ var PDFImage = (function PDFImageClosure }; PDFImage.prototype = { get drawWidth() { return Math.max(this.width, this.smask && this.smask.width || 0, this.mask && this.mask.width || 0); }, + get drawHeight() { return Math.max(this.height, this.smask && this.smask.height || 0, this.mask && this.mask.height || 0); }, + decodeBuffer: function PDFImage_decodeBuffer(buffer) { var bpc = this.bpc; var numComps = this.numComps; var decodeAddends = this.decodeAddends; var decodeCoefficients = this.decodeCoefficients; var max = (1 << bpc) - 1; var i, ii; @@ -30070,16 +30108,17 @@ var PDFImage = (function PDFImageClosure for (i = 0, ii = this.width * this.height; i < ii; i++) { for (var j = 0; j < numComps; j++) { buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], decodeCoefficients[j], max); index++; } } }, + getComponents: function PDFImage_getComponents(buffer) { var bpc = this.bpc; // This image doesn't require any extra work. if (bpc === 8) { return buffer; } @@ -30146,16 +30185,17 @@ var PDFImage = (function PDFImageClosure var value = buf >> remainingBits; output[i] = (value < 0 ? 0 : (value > max ? max : value)); buf = buf & ((1 << remainingBits) - 1); bits = remainingBits; } } return output; }, + fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height, actualHeight, image) { var smask = this.smask; var mask = this.mask; var alphaBuf, sw, sh, i, ii, j; if (smask) { sw = smask.width; @@ -30212,60 +30252,64 @@ var PDFImage = (function PDFImageClosure } } else { // No mask. for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { rgbaBuf[j] = 255; } } }, + undoPreblend: function PDFImage_undoPreblend(buffer, width, height) { var matte = this.smask && this.smask.matte; if (!matte) { return; } - - function clamp(value) { - return (value < 0 ? 0 : (value > 255 ? 255 : value)) | 0; - } - var matteRgb = this.colorSpace.getRgb(matte, 0); + var matteR = matteRgb[0]; + var matteG = matteRgb[1]; + var matteB = matteRgb[2]; var length = width * height * 4; + var r, g, b; for (var i = 0; i < length; i += 4) { var alpha = buffer[i + 3]; if (alpha === 0) { // according formula we have to get Infinity in all components - // making it as white (tipical paper color) should be okay + // making it white (typical paper color) should be okay buffer[i] = 255; buffer[i + 1] = 255; buffer[i + 2] = 255; continue; } var k = 255 / alpha; - buffer[i] = clamp((buffer[i] - matteRgb[0]) * k + matteRgb[0]); - buffer[i + 1] = clamp((buffer[i + 1] - matteRgb[1]) * k + matteRgb[1]); - buffer[i + 2] = clamp((buffer[i + 2] - matteRgb[2]) * k + matteRgb[2]); - } - }, + r = (buffer[i] - matteR) * k + matteR; + g = (buffer[i + 1] - matteG) * k + matteG; + b = (buffer[i + 2] - matteB) * k + matteB; + buffer[i] = r <= 0 ? 0 : r >= 255 ? 255 : r | 0; + buffer[i + 1] = g <= 0 ? 0 : g >= 255 ? 255 : g | 0; + buffer[i + 2] = b <= 0 ? 0 : b >= 255 ? 255 : b | 0; + } + }, + createImageData: function PDFImage_createImageData(forceRGBA) { var drawWidth = this.drawWidth; var drawHeight = this.drawHeight; var imgData = { // other fields are filled in below width: drawWidth, height: drawHeight }; var numComps = this.numComps; var originalWidth = this.width; var originalHeight = this.height; var bpc = this.bpc; // Rows start at byte boundary. var rowBytes = (originalWidth * numComps * bpc + 7) >> 3; - var imgArray = this.getImageBytes(originalHeight * rowBytes); + var imgArray; if (!forceRGBA) { // If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image // without any complications, we pass a same-sized copy to the main // thread rather than expanding by 32x to RGBA form. This saves *lots* // of memory for many scanned documents. It's also much faster. // // Similarly, if it is a 24-bit-per pixel RGB image without any @@ -30275,32 +30319,40 @@ var PDFImage = (function PDFImageClosure kind = ImageKind.GRAYSCALE_1BPP; } else if (this.colorSpace.name === 'DeviceRGB' && bpc === 8) { kind = ImageKind.RGB_24BPP; } if (kind && !this.smask && !this.mask && !this.needsDecode && drawWidth === originalWidth && drawHeight === originalHeight) { imgData.kind = kind; + imgArray = this.getImageBytes(originalHeight * rowBytes); // If imgArray came from a DecodeStream, we're safe to transfer it // (and thus neuter it) because it will constitute the entire // DecodeStream's data. But if it came from a Stream, we need to // copy it because it'll only be a portion of the Stream's data, and // the rest will be read later on. if (this.image instanceof DecodeStream) { imgData.data = imgArray; } else { var newArray = new Uint8Array(imgArray.length); newArray.set(imgArray); imgData.data = newArray; } return imgData; } } - + if (this.image instanceof JpegStream) { + imgData.kind = ImageKind.RGB_24BPP; + imgData.data = this.getImageBytes(originalHeight * rowBytes, + drawWidth, drawHeight); + return imgData; + } + + imgArray = this.getImageBytes(originalHeight * rowBytes); // imgArray can be incomplete (e.g. after CCITT fax encoding). var actualHeight = 0 | (imgArray.length / rowBytes * drawHeight / originalHeight); var comps = this.getComponents(imgArray); // If opacity data is present, use RGBA_32BPP form. Otherwise, use the // more compact RGB_24BPP form if allowable. @@ -30328,16 +30380,17 @@ var PDFImage = (function PDFImageClosure drawWidth, drawHeight, actualHeight, bpc, comps, alpha01); if (maybeUndoPreblend) { this.undoPreblend(imgData.data, drawWidth, actualHeight); } return imgData; }, + fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) { var numComps = this.numComps; if (numComps != 1) { error('Reading gray scale from a color image: ' + numComps); } var width = this.width; var height = this.height; @@ -30349,17 +30402,17 @@ var PDFImage = (function PDFImageClosure var comps = this.getComponents(imgArray); var i, length; if (bpc === 1) { // inline decoding (= inversion) for 1 bpc images length = width * height; if (this.needsDecode) { - // invert and scale to {0, 255} + // invert and scale to {0, 255} for (i = 0; i < length; ++i) { buffer[i] = (comps[i] - 1) & 255; } } else { // scale to {0, 255} for (i = 0; i < length; ++i) { buffer[i] = (-comps[i]) & 255; } @@ -30372,18 +30425,22 @@ var PDFImage = (function PDFImageClosure } length = width * height; // we aren't using a colorspace so we need to scale the value var scale = 255 / ((1 << bpc) - 1); for (i = 0; i < length; ++i) { buffer[i] = (scale * comps[i]) | 0; } }, - getImageBytes: function PDFImage_getImageBytes(length) { + + getImageBytes: function PDFImage_getImageBytes(length, + drawWidth, drawHeight) { this.image.reset(); + this.image.drawWidth = drawWidth; + this.image.drawHeight = drawHeight; return this.image.getBytes(length); } }; return PDFImage; })(); // The Metrics object contains glyph widths (in glyph space units). @@ -35227,26 +35284,31 @@ var JpegStream = (function JpegStreamClo return; } try { var jpegImage = new JpegImage(); if (this.colorTransform != -1) { jpegImage.colorTransform = this.colorTransform; } jpegImage.parse(this.bytes); - var width = jpegImage.width; - var height = jpegImage.height; - var data = jpegImage.getData(width, height); + var data = jpegImage.getData(this.drawWidth, this.drawHeight, + /* forceRGBoutput = */true); this.buffer = data; this.bufferLength = data.length; this.eof = true; } catch (e) { error('JPEG error: ' + e); } }; + + JpegStream.prototype.getBytes = function JpegStream_getBytes(length) { + this.ensureBuffer(); + return this.buffer; + }; + JpegStream.prototype.getIR = function JpegStream_getIR() { return PDFJS.createObjectURL(this.bytes, 'image/jpeg'); }; /** * Checks if the image can be decoded and displayed by the browser without any * further processing such as color space conversions. */ JpegStream.prototype.isNativelySupported = @@ -36769,79 +36831,69 @@ var NullStream = (function NullStreamClo })(); var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { setup: function wphSetup(handler) { var pdfManager; function loadDocument(recoveryMode) { - var loadDocumentPromise = new LegacyPromise(); + var loadDocumentCapability = createPromiseCapability(); var parseSuccess = function parseSuccess() { var numPagesPromise = pdfManager.ensureDoc('numPages'); var fingerprintPromise = pdfManager.ensureDoc('fingerprint'); - var outlinePromise = pdfManager.ensureCatalog('documentOutline'); - var infoPromise = pdfManager.ensureDoc('documentInfo'); - var metadataPromise = pdfManager.ensureCatalog('metadata'); var encryptedPromise = pdfManager.ensureXRef('encrypt'); - var javaScriptPromise = pdfManager.ensureCatalog('javaScript'); - Promise.all([numPagesPromise, fingerprintPromise, outlinePromise, - infoPromise, metadataPromise, encryptedPromise, - javaScriptPromise]).then(function onDocReady(results) { - + Promise.all([numPagesPromise, fingerprintPromise, + encryptedPromise]).then(function onDocReady(results) { var doc = { numPages: results[0], fingerprint: results[1], - outline: results[2], - info: results[3], - metadata: results[4], - encrypted: !!results[5], - javaScript: results[6] + encrypted: !!results[2], }; - loadDocumentPromise.resolve(doc); + loadDocumentCapability.resolve(doc); }, parseFailure); }; var parseFailure = function parseFailure(e) { - loadDocumentPromise.reject(e); + loadDocumentCapability.reject(e); }; pdfManager.ensureDoc('checkHeader', []).then(function() { pdfManager.ensureDoc('parseStartXRef', []).then(function() { pdfManager.ensureDoc('parse', [recoveryMode]).then( parseSuccess, parseFailure); }, parseFailure); }, parseFailure); - return loadDocumentPromise; + return loadDocumentCapability.promise; } function getPdfManager(data) { - var pdfManagerPromise = new LegacyPromise(); + var pdfManagerCapability = createPromiseCapability(); var source = data.source; var disableRange = data.disableRange; if (source.data) { try { pdfManager = new LocalPdfManager(source.data, source.password); - pdfManagerPromise.resolve(); + pdfManagerCapability.resolve(); } catch (ex) { - pdfManagerPromise.reject(ex); - } - return pdfManagerPromise; + pdfManagerCapability.reject(ex); + } + return pdfManagerCapability.promise; } else if (source.chunkedViewerLoading) { try { pdfManager = new NetworkPdfManager(source, handler); - pdfManagerPromise.resolve(); + pdfManagerCapability.resolve(); } catch (ex) { - pdfManagerPromise.reject(ex); - } - return pdfManagerPromise; + pdfManagerCapability.reject(ex); + } + return pdfManagerCapability.promise; } var networkManager = new NetworkManager(source.url, { httpHeaders: source.httpHeaders, withCredentials: source.withCredentials }); var fullRequestXhrId = networkManager.requestFull({ onHeadersReceived: function onHeadersReceived() { @@ -36876,29 +36928,29 @@ var WorkerMessageHandler = PDFJS.WorkerM // NOTE: by cancelling the full request, and then issuing range // requests, there will be an issue for sites where you can only // request the pdf once. However, if this is the case, then the // server should not be returning that it can support range requests. networkManager.abortRequest(fullRequestXhrId); try { pdfManager = new NetworkPdfManager(source, handler); - pdfManagerPromise.resolve(pdfManager); + pdfManagerCapability.resolve(pdfManager); } catch (ex) { - pdfManagerPromise.reject(ex); + pdfManagerCapability.reject(ex); } }, onDone: function onDone(args) { // the data is array, instantiating directly from it try { pdfManager = new LocalPdfManager(args.chunk, source.password); - pdfManagerPromise.resolve(); + pdfManagerCapability.resolve(); } catch (ex) { - pdfManagerPromise.reject(ex); + pdfManagerCapability.reject(ex); } }, onError: function onError(status) { if (status == 404) { var exception = new MissingPDFException('Missing PDF "' + source.url + '".'); handler.send('MissingPDF', { exception: exception }); @@ -36912,17 +36964,17 @@ var WorkerMessageHandler = PDFJS.WorkerM onProgress: function onProgress(evt) { handler.send('DocProgress', { loaded: evt.loaded, total: evt.lengthComputable ? evt.total : source.length }); } }); - return pdfManagerPromise; + return pdfManagerCapability.promise; } handler.on('test', function wphSetupTest(data) { // check if Uint8Array can be sent to worker if (!(data instanceof Uint8Array)) { handler.send('test', false); return; } @@ -36995,18 +37047,17 @@ var WorkerMessageHandler = PDFJS.WorkerM }); }).then(function pdfManagerReady() { loadDocument(false).then(onSuccess, function loadFailure(ex) { // Try again with recoveryMode == true if (!(ex instanceof XRefParseException)) { if (ex instanceof PasswordException) { // after password exception prepare to receive a new password // to repeat loading - pdfManager.passwordChangedPromise = new LegacyPromise(); - pdfManager.passwordChangedPromise.then(pdfManagerReady); + pdfManager.passwordChanged().then(pdfManagerReady); } onFailure(ex); return; } pdfManager.requestLoadedStream(); pdfManager.onLoadedStream().then(function() { @@ -37056,16 +37107,42 @@ var WorkerMessageHandler = PDFJS.WorkerM handler.on('GetAttachments', function wphSetupGetAttachments(data, deferred) { pdfManager.ensureCatalog('attachments').then(function(attachments) { deferred.resolve(attachments); }, deferred.reject); } ); + handler.on('GetJavaScript', + function wphSetupGetJavaScript(data, deferred) { + pdfManager.ensureCatalog('javaScript').then(function (js) { + deferred.resolve(js); + }, deferred.reject); + } + ); + + handler.on('GetOutline', + function wphSetupGetOutline(data, deferred) { + pdfManager.ensureCatalog('documentOutline').then(function (outline) { + deferred.resolve(outline); + }, deferred.reject); + } + ); + + handler.on('GetMetadata', + function wphSetupGetMetadata(data, deferred) { + Promise.all([pdfManager.ensureDoc('documentInfo'), + pdfManager.ensureCatalog('metadata')]).then( + function (results) { + deferred.resolve(results); + }, deferred.reject); + } + ); + handler.on('GetData', function wphSetupGetData(data, deferred) { pdfManager.requestLoadedStream(); pdfManager.onLoadedStream().then(function(stream) { deferred.resolve(stream.bytes); }); }); handler.on('UpdatePassword', function wphSetupUpdatePassword(data) { @@ -37373,16 +37450,1000 @@ var ArithmeticDecoder = (function Arithm return d; } }; return ArithmeticDecoder; })(); +var JpegImage = (function jpegImage() { + var dctZigZag = new Int32Array([ + 0, + 1, 8, + 16, 9, 2, + 3, 10, 17, 24, + 32, 25, 18, 11, 4, + 5, 12, 19, 26, 33, 40, + 48, 41, 34, 27, 20, 13, 6, + 7, 14, 21, 28, 35, 42, 49, 56, + 57, 50, 43, 36, 29, 22, 15, + 23, 30, 37, 44, 51, 58, + 59, 52, 45, 38, 31, + 39, 46, 53, 60, + 61, 54, 47, + 55, 62, + 63 + ]); + + var dctCos1 = 4017; // cos(pi/16) + var dctSin1 = 799; // sin(pi/16) + var dctCos3 = 3406; // cos(3*pi/16) + var dctSin3 = 2276; // sin(3*pi/16) + var dctCos6 = 1567; // cos(6*pi/16) + var dctSin6 = 3784; // sin(6*pi/16) + var dctSqrt2 = 5793; // sqrt(2) + var dctSqrt1d2 = 2896; // sqrt(2) / 2 + + function constructor() { + } + + function buildHuffmanTable(codeLengths, values) { + var k = 0, code = [], i, j, length = 16; + while (length > 0 && !codeLengths[length - 1]) { + length--; + } + code.push({children: [], index: 0}); + var p = code[0], q; + for (i = 0; i < length; i++) { + for (j = 0; j < codeLengths[i]; j++) { + p = code.pop(); + p.children[p.index] = values[k]; + while (p.index > 0) { + p = code.pop(); + } + p.index++; + code.push(p); + while (code.length <= i) { + code.push(q = {children: [], index: 0}); + p.children[p.index] = q.children; + p = q; + } + k++; + } + if (i + 1 < length) { + // p here points to last code + code.push(q = {children: [], index: 0}); + p.children[p.index] = q.children; + p = q; + } + } + return code[0].children; + } + + function getBlockBufferOffset(component, row, col) { + return 64 * ((component.blocksPerLine + 1) * row + col); + } + + function decodeScan(data, offset, + frame, components, resetInterval, + spectralStart, spectralEnd, + successivePrev, successive) { + var precision = frame.precision; + var samplesPerLine = frame.samplesPerLine; + var scanLines = frame.scanLines; + var mcusPerLine = frame.mcusPerLine; + var progressive = frame.progressive; + var maxH = frame.maxH, maxV = frame.maxV; + + var startOffset = offset, bitsData = 0, bitsCount = 0; + + function readBit() { + if (bitsCount > 0) { + bitsCount--; + return (bitsData >> bitsCount) & 1; + } + bitsData = data[offset++]; + if (bitsData == 0xFF) { + var nextByte = data[offset++]; + if (nextByte) { + throw 'unexpected marker: ' + + ((bitsData << 8) | nextByte).toString(16); + } + // unstuff 0 + } + bitsCount = 7; + return bitsData >>> 7; + } + + function decodeHuffman(tree) { + var node = tree; + var bit; + while ((bit = readBit()) !== null) { + node = node[bit]; + if (typeof node === 'number') { + return node; + } + if (typeof node !== 'object') { + throw 'invalid huffman sequence'; + } + } + return null; + } + + function receive(length) { + var n = 0; + while (length > 0) { + var bit = readBit(); + if (bit === null) { + return; + } + n = (n << 1) | bit; + length--; + } + return n; + } + + function receiveAndExtend(length) { + var n = receive(length); + if (n >= 1 << (length - 1)) { + return n; + } + return n + (-1 << length) + 1; + } + + function decodeBaseline(component, offset) { + var t = decodeHuffman(component.huffmanTableDC); + var diff = t === 0 ? 0 : receiveAndExtend(t); + component.blockData[offset] = (component.pred += diff); + var k = 1; + while (k < 64) { + var rs = decodeHuffman(component.huffmanTableAC); + var s = rs & 15, r = rs >> 4; + if (s === 0) { + if (r < 15) { + break; + } + k += 16; + continue; + } + k += r; + var z = dctZigZag[k]; + component.blockData[offset + z] = receiveAndExtend(s); + k++; + } + } + + function decodeDCFirst(component, offset) { + var t = decodeHuffman(component.huffmanTableDC); + var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive); + component.blockData[offset] = (component.pred += diff); + } + + function decodeDCSuccessive(component, offset) { + component.blockData[offset] |= readBit() << successive; + } + + var eobrun = 0; + function decodeACFirst(component, offset) { + if (eobrun > 0) { + eobrun--; + return; + } + var k = spectralStart, e = spectralEnd; + while (k <= e) { + var rs = decodeHuffman(component.huffmanTableAC); + var s = rs & 15, r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r) - 1; + break; + } + k += 16; + continue; + } + k += r; + var z = dctZigZag[k]; + component.blockData[offset + z] = + receiveAndExtend(s) * (1 << successive); + k++; + } + } + + var successiveACState = 0, successiveACNextValue; + function decodeACSuccessive(component, offset) { + var k = spectralStart; + var e = spectralEnd; + var r = 0; + var s; + var rs; + while (k <= e) { + var z = dctZigZag[k]; + switch (successiveACState) { + case 0: // initial state + rs = decodeHuffman(component.huffmanTableAC); + s = rs & 15; + r = rs >> 4; + if (s === 0) { + if (r < 15) { + eobrun = receive(r) + (1 << r); + successiveACState = 4; + } else { + r = 16; + successiveACState = 1; + } + } else { + if (s !== 1) { + throw 'invalid ACn encoding'; + } + successiveACNextValue = receiveAndExtend(s); + successiveACState = r ? 2 : 3; + } + continue; + case 1: // skipping r zero items + case 2: + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } else { + r--; + if (r === 0) { + successiveACState = successiveACState == 2 ? 3 : 0; + } + } + break; + case 3: // set value for a zero item + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } else { + component.blockData[offset + z] = + successiveACNextValue << successive; + successiveACState = 0; + } + break; + case 4: // eob + if (component.blockData[offset + z]) { + component.blockData[offset + z] += (readBit() << successive); + } + break; + } + k++; + } + if (successiveACState === 4) { + eobrun--; + if (eobrun === 0) { + successiveACState = 0; + } + } + } + + function decodeMcu(component, decode, mcu, row, col) { + var mcuRow = (mcu / mcusPerLine) | 0; + var mcuCol = mcu % mcusPerLine; + var blockRow = mcuRow * component.v + row; + var blockCol = mcuCol * component.h + col; + var offset = getBlockBufferOffset(component, blockRow, blockCol); + decode(component, offset); + } + + function decodeBlock(component, decode, mcu) { + var blockRow = (mcu / component.blocksPerLine) | 0; + var blockCol = mcu % component.blocksPerLine; + var offset = getBlockBufferOffset(component, blockRow, blockCol); + decode(component, offset); + } + + var componentsLength = components.length; + var component, i, j, k, n; + var decodeFn; + if (progressive) { + if (spectralStart === 0) { + decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive; + } else { + decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive; + } + } else { + decodeFn = decodeBaseline; + } + + var mcu = 0, marker; + var mcuExpected; + if (componentsLength == 1) { + mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn; + } else { + mcuExpected = mcusPerLine * frame.mcusPerColumn; + } + if (!resetInterval) { + resetInterval = mcuExpected; + } + + var h, v; + while (mcu < mcuExpected) { + // reset interval stuff + for (i = 0; i < componentsLength; i++) { + components[i].pred = 0; + } + eobrun = 0; + + if (componentsLength == 1) { + component = components[0]; + for (n = 0; n < resetInterval; n++) { + decodeBlock(component, decodeFn, mcu); + mcu++; + } + } else { + for (n = 0; n < resetInterval; n++) { + for (i = 0; i < componentsLength; i++) { + component = components[i]; + h = component.h; + v = component.v; + for (j = 0; j < v; j++) { + for (k = 0; k < h; k++) { + decodeMcu(component, decodeFn, mcu, j, k); + } + } + } + mcu++; + } + } + + // find marker + bitsCount = 0; + marker = (data[offset] << 8) | data[offset + 1]; + if (marker <= 0xFF00) { + throw 'marker was not found'; + } + + if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx + offset += 2; + } else { + break; + } + } + + return offset - startOffset; + } + + // A port of poppler's IDCT method which in turn is taken from: + // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, + // 'Practical Fast 1-D DCT Algorithms with 11 Multiplications', + // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, + // 988-991. + function quantizeAndInverse(component, blockBufferOffset, p) { + var qt = component.quantizationTable; + var v0, v1, v2, v3, v4, v5, v6, v7, t; + var i; + + // dequant + for (i = 0; i < 64; i++) { + p[i] = component.blockData[blockBufferOffset + i] * qt[i]; + } + + // inverse DCT on rows + for (i = 0; i < 8; ++i) { + var row = 8 * i; + + // check for all-zero AC coefficients + if (p[1 + row] === 0 && p[2 + row] === 0 && p[3 + row] === 0 && + p[4 + row] === 0 && p[5 + row] === 0 && p[6 + row] === 0 && + p[7 + row] === 0) { + t = (dctSqrt2 * p[0 + row] + 512) >> 10; + p[0 + row] = t; + p[1 + row] = t; + p[2 + row] = t; + p[3 + row] = t; + p[4 + row] = t; + p[5 + row] = t; + p[6 + row] = t; + p[7 + row] = t; + continue; + } + + // stage 4 + v0 = (dctSqrt2 * p[0 + row] + 128) >> 8; + v1 = (dctSqrt2 * p[4 + row] + 128) >> 8; + v2 = p[2 + row]; + v3 = p[6 + row]; + v4 = (dctSqrt1d2 * (p[1 + row] - p[7 + row]) + 128) >> 8; + v7 = (dctSqrt1d2 * (p[1 + row] + p[7 + row]) + 128) >> 8; + v5 = p[3 + row] << 4; + v6 = p[5 + row] << 4; + + // stage 3 + t = (v0 - v1+ 1) >> 1; + v0 = (v0 + v1 + 1) >> 1; + v1 = t; + t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8; + v3 = t; + t = (v4 - v6 + 1) >> 1; + v4 = (v4 + v6 + 1) >> 1; + v6 = t; + t = (v7 + v5 + 1) >> 1; + v5 = (v7 - v5 + 1) >> 1; + v7 = t; + + // stage 2 + t = (v0 - v3 + 1) >> 1; + v0 = (v0 + v3 + 1) >> 1; + v3 = t; + t = (v1 - v2 + 1) >> 1; + v1 = (v1 + v2 + 1) >> 1; + v2 = t; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p[0 + row] = v0 + v7; + p[7 + row] = v0 - v7; + p[1 + row] = v1 + v6; + p[6 + row] = v1 - v6; + p[2 + row] = v2 + v5; + p[5 + row] = v2 - v5; + p[3 + row] = v3 + v4; + p[4 + row] = v3 - v4; + } + + // inverse DCT on columns + for (i = 0; i < 8; ++i) { + var col = i; + + // check for all-zero AC coefficients + if (p[1*8 + col] === 0 && p[2*8 + col] === 0 && p[3*8 + col] === 0 && + p[4*8 + col] === 0 && p[5*8 + col] === 0 && p[6*8 + col] === 0 && + p[7*8 + col] === 0) { + t = (dctSqrt2 * p[i+0] + 8192) >> 14; + p[0*8 + col] = t; + p[1*8 + col] = t; + p[2*8 + col] = t; + p[3*8 + col] = t; + p[4*8 + col] = t; + p[5*8 + col] = t; + p[6*8 + col] = t; + p[7*8 + col] = t; + continue; + } + + // stage 4 + v0 = (dctSqrt2 * p[0*8 + col] + 2048) >> 12; + v1 = (dctSqrt2 * p[4*8 + col] + 2048) >> 12; + v2 = p[2*8 + col]; + v3 = p[6*8 + col]; + v4 = (dctSqrt1d2 * (p[1*8 + col] - p[7*8 + col]) + 2048) >> 12; + v7 = (dctSqrt1d2 * (p[1*8 + col] + p[7*8 + col]) + 2048) >> 12; + v5 = p[3*8 + col]; + v6 = p[5*8 + col]; + + // stage 3 + t = (v0 - v1 + 1) >> 1; + v0 = (v0 + v1 + 1) >> 1; + v1 = t; + t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12; + v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12; + v3 = t; + t = (v4 - v6 + 1) >> 1; + v4 = (v4 + v6 + 1) >> 1; + v6 = t; + t = (v7 + v5 + 1) >> 1; + v5 = (v7 - v5 + 1) >> 1; + v7 = t; + + // stage 2 + t = (v0 - v3 + 1) >> 1; + v0 = (v0 + v3 + 1) >> 1; + v3 = t; + t = (v1 - v2 + 1) >> 1; + v1 = (v1 + v2 + 1) >> 1; + v2 = t; + t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; + v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; + v7 = t; + t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; + v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; + v6 = t; + + // stage 1 + p[0*8 + col] = v0 + v7; + p[7*8 + col] = v0 - v7; + p[1*8 + col] = v1 + v6; + p[6*8 + col] = v1 - v6; + p[2*8 + col] = v2 + v5; + p[5*8 + col] = v2 - v5; + p[3*8 + col] = v3 + v4; + p[4*8 + col] = v3 - v4; + } + + // convert to 8-bit integers + for (i = 0; i < 64; ++i) { + var index = blockBufferOffset + i; + var q = p[i]; + q = (q <= -2056) ? 0 : (q >= 2024) ? 255 : (q + 2056) >> 4; + component.blockData[index] = q; + } + } + + function buildComponentData(frame, component) { + var lines = []; + var blocksPerLine = component.blocksPerLine; + var blocksPerColumn = component.blocksPerColumn; + var samplesPerLine = blocksPerLine << 3; + var computationBuffer = new Int32Array(64); + + var i, j, ll = 0; + for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) { + for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) { + var offset = getBlockBufferOffset(component, blockRow, blockCol); + quantizeAndInverse(component, offset, computationBuffer); + } + } + return component.blockData; + } + + function clamp0to255(a) { + return a <= 0 ? 0 : a >= 255 ? 255 : a; + } + + constructor.prototype = { + parse: function parse(data) { + + function readUint16() { + var value = (data[offset] << 8) | data[offset + 1]; + offset += 2; + return value; + } + + function readDataBlock() { + var length = readUint16(); + var array = data.subarray(offset, offset + length - 2); + offset += array.length; + return array; + } + + function prepareComponents(frame) { + var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH); + var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV); + for (var i = 0; i < frame.components.length; i++) { + component = frame.components[i]; + var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * + component.h / frame.maxH); + var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * + component.v / frame.maxV); + var blocksPerLineForMcu = mcusPerLine * component.h; + var blocksPerColumnForMcu = mcusPerColumn * component.v; + + var blocksBufferSize = 64 * blocksPerColumnForMcu * + (blocksPerLineForMcu + 1); + component.blockData = new Int16Array(blocksBufferSize); + component.blocksPerLine = blocksPerLine; + component.blocksPerColumn = blocksPerColumn; + } + frame.mcusPerLine = mcusPerLine; + frame.mcusPerColumn = mcusPerColumn; + } + + var offset = 0, length = data.length; + var jfif = null; + var adobe = null; + var pixels = null; + var frame, resetInterval; + var quantizationTables = []; + var huffmanTablesAC = [], huffmanTablesDC = []; + var fileMarker = readUint16(); + if (fileMarker != 0xFFD8) { // SOI (Start of Image) + throw 'SOI not found'; + } + + fileMarker = readUint16(); + while (fileMarker != 0xFFD9) { // EOI (End of image) + var i, j, l; + switch(fileMarker) { + case 0xFFE0: // APP0 (Application Specific) + case 0xFFE1: // APP1 + case 0xFFE2: // APP2 + case 0xFFE3: // APP3 + case 0xFFE4: // APP4 + case 0xFFE5: // APP5 + case 0xFFE6: // APP6 + case 0xFFE7: // APP7 + case 0xFFE8: // APP8 + case 0xFFE9: // APP9 + case 0xFFEA: // APP10 + case 0xFFEB: // APP11 + case 0xFFEC: // APP12 + case 0xFFED: // APP13 + case 0xFFEE: // APP14 + case 0xFFEF: // APP15 + case 0xFFFE: // COM (Comment) + var appData = readDataBlock(); + + if (fileMarker === 0xFFE0) { + if (appData[0] === 0x4A && appData[1] === 0x46 && + appData[2] === 0x49 && appData[3] === 0x46 && + appData[4] === 0) { // 'JFIF\x00' + jfif = { + version: { major: appData[5], minor: appData[6] }, + densityUnits: appData[7], + xDensity: (appData[8] << 8) | appData[9], + yDensity: (appData[10] << 8) | appData[11], + thumbWidth: appData[12], + thumbHeight: appData[13], + thumbData: appData.subarray(14, 14 + + 3 * appData[12] * appData[13]) + }; + } + } + // TODO APP1 - Exif + if (fileMarker === 0xFFEE) { + if (appData[0] === 0x41 && appData[1] === 0x64 && + appData[2] === 0x6F && appData[3] === 0x62 && + appData[4] === 0x65 && appData[5] === 0) { // 'Adobe\x00' + adobe = { + version: appData[6], + flags0: (appData[7] << 8) | appData[8], + flags1: (appData[9] << 8) | appData[10], + transformCode: appData[11] + }; + } + } + break; + + case 0xFFDB: // DQT (Define Quantization Tables) + var quantizationTablesLength = readUint16(); + var quantizationTablesEnd = quantizationTablesLength + offset - 2; + var z; + while (offset < quantizationTablesEnd) { + var quantizationTableSpec = data[offset++]; + var tableData = new Int32Array(64); + if ((quantizationTableSpec >> 4) === 0) { // 8 bit values + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = data[offset++]; + } + } else if ((quantizationTableSpec >> 4) === 1) { //16 bit + for (j = 0; j < 64; j++) { + z = dctZigZag[j]; + tableData[z] = readUint16(); + } + } else { + throw 'DQT: invalid table spec'; + } + quantizationTables[quantizationTableSpec & 15] = tableData; + } + break; + + case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT) + case 0xFFC1: // SOF1 (Start of Frame, Extended DCT) + case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT) + if (frame) { + throw 'Only single frame JPEGs supported'; + } + readUint16(); // skip data length + frame = {}; + frame.extended = (fileMarker === 0xFFC1); + frame.progressive = (fileMarker === 0xFFC2); + frame.precision = data[offset++]; + frame.scanLines = readUint16(); + frame.samplesPerLine = readUint16(); + frame.components = []; + frame.componentIds = {}; + var componentsCount = data[offset++], componentId; + var maxH = 0, maxV = 0; + for (i = 0; i < componentsCount; i++) { + componentId = data[offset]; + var h = data[offset + 1] >> 4; + var v = data[offset + 1] & 15; + if (maxH < h) { + maxH = h; + } + if (maxV < v) { + maxV = v; + } + var qId = data[offset + 2]; + l = frame.components.push({ + h: h, + v: v, + quantizationTable: quantizationTables[qId] + }); + frame.componentIds[componentId] = l - 1; + offset += 3; + } + frame.maxH = maxH; + frame.maxV = maxV; + prepareComponents(frame); + break; + + case 0xFFC4: // DHT (Define Huffman Tables) + var huffmanLength = readUint16(); + for (i = 2; i < huffmanLength;) { + var huffmanTableSpec = data[offset++]; + var codeLengths = new Uint8Array(16); + var codeLengthSum = 0; + for (j = 0; j < 16; j++, offset++) { + codeLengthSum += (codeLengths[j] = data[offset]); + } + var huffmanValues = new Uint8Array(codeLengthSum); + for (j = 0; j < codeLengthSum; j++, offset++) { + huffmanValues[j] = data[offset]; + } + i += 17 + codeLengthSum; + + ((huffmanTableSpec >> 4) === 0 ? + huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = + buildHuffmanTable(codeLengths, huffmanValues); + } + break; + + case 0xFFDD: // DRI (Define Restart Interval) + readUint16(); // skip data length + resetInterval = readUint16(); + break; + + case 0xFFDA: // SOS (Start of Scan) + var scanLength = readUint16(); + var selectorsCount = data[offset++]; + var components = [], component; + for (i = 0; i < selectorsCount; i++) { + var componentIndex = frame.componentIds[data[offset++]]; + component = frame.components[componentIndex]; + var tableSpec = data[offset++]; + component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4]; + component.huffmanTableAC = huffmanTablesAC[tableSpec & 15]; + components.push(component); + } + var spectralStart = data[offset++]; + var spectralEnd = data[offset++]; + var successiveApproximation = data[offset++]; + var processed = decodeScan(data, offset, + frame, components, resetInterval, + spectralStart, spectralEnd, + successiveApproximation >> 4, successiveApproximation & 15); + offset += processed; + break; + default: + if (data[offset - 3] == 0xFF && + data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) { + // could be incorrect encoding -- last 0xFF byte of the previous + // block was eaten by the encoder + offset -= 3; + break; + } + throw 'unknown JPEG marker ' + fileMarker.toString(16); + } + fileMarker = readUint16(); + } + + this.width = frame.samplesPerLine; + this.height = frame.scanLines; + this.jfif = jfif; + this.adobe = adobe; + this.components = []; + for (i = 0; i < frame.components.length; i++) { + component = frame.components[i]; + this.components.push({ + output: buildComponentData(frame, component), + scaleX: component.h / frame.maxH, + scaleY: component.v / frame.maxV, + blocksPerLine: component.blocksPerLine, + blocksPerColumn: component.blocksPerColumn + }); + } + this.numComponents = this.components.length; + }, + + _getLinearizedBlockData: function getLinearizedBlockData(width, height) { + var scaleX = this.width / width, scaleY = this.height / height; + + var component, componentScaleX, componentScaleY, blocksPerScanline; + var x, y, i, j; + var index; + var offset = 0; + var output; + var numComponents = this.components.length; + var dataLength = width * height * numComponents; + var data = new Uint8Array(dataLength); + var xScaleBlockOffset = new Uint32Array(width); + var mask3LSB = 0xfffffff8; // used to clear the 3 LSBs + + for (i = 0; i < numComponents; i++) { + component = this.components[i]; + componentScaleX = component.scaleX * scaleX; + componentScaleY = component.scaleY * scaleY; + offset = i; + output = component.output; + blocksPerScanline = (component.blocksPerLine + 1) << 3; + // precalculate the xScaleBlockOffset + for (x = 0; x < width; x++) { + j = 0 | (x * componentScaleX); + xScaleBlockOffset[x] = ((j & mask3LSB) << 3) | (j & 7); + } + // linearize the blocks of the component + for (y = 0; y < height; y++) { + j = 0 | (y * componentScaleY); + index = blocksPerScanline * (j & mask3LSB) | ((j & 7) << 3); + for (x = 0; x < width; x++) { + data[offset] = output[index + xScaleBlockOffset[x]]; + offset += numComponents; + } + } + } + return data; + }, + + _isColorConversionNeeded: function isColorConversionNeeded() { + if (this.adobe && this.adobe.transformCode) { + // The adobe transform marker overrides any previous setting + return true; + } else if (this.numComponents == 3) { + return true; + } else if (typeof this.colorTransform !== 'undefined') { + return !!this.colorTransform; + } else { + return false; + } + }, + + _convertYccToRgb: function convertYccToRgb(data) { + var Y, Cb, Cr; + for (var i = 0; i < data.length; i += this.numComponents) { + Y = data[i ]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i ] = clamp0to255(Y - 179.456 + 1.402 * Cr); + data[i + 1] = clamp0to255(Y + 135.459 - 0.344 * Cb - 0.714 * Cr); + data[i + 2] = clamp0to255(Y - 226.816 + 1.772 * Cb); + } + return data; + }, + + _convertYcckToRgb: function convertYcckToRgb(data) { + var Y, Cb, Cr, k, CbCb, CbCr, CbY, Cbk, CrCr, Crk, CrY, YY, Yk, kk; + var offset = 0; + for (var i = 0; i < data.length; i += this.numComponents) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + k = data[i + 3]; + + CbCb = Cb * Cb; + CbCr = Cb * Cr; + CbY = Cb * Y; + Cbk = Cb * k; + CrCr = Cr * Cr; + Crk = Cr * k; + CrY = Cr * Y; + YY = Y * Y; + Yk = Y * k; + kk = k * k; + + var r = - 122.67195406894 - + 6.60635669420364e-5 * CbCb + 0.000437130475926232 * CbCr - + 5.4080610064599e-5* CbY + 0.00048449797120281* Cbk - + 0.154362151871126 * Cb - 0.000957964378445773 * CrCr + + 0.000817076911346625 * CrY - 0.00477271405408747 * Crk + + 1.53380253221734 * Cr + 0.000961250184130688 * YY - + 0.00266257332283933 * Yk + 0.48357088451265 * Y - + 0.000336197177618394 * kk + 0.484791561490776 * k; + + var g = 107.268039397724 + + 2.19927104525741e-5 * CbCb - 0.000640992018297945 * CbCr + + 0.000659397001245577* CbY + 0.000426105652938837* Cbk - + 0.176491792462875 * Cb - 0.000778269941513683 * CrCr + + 0.00130872261408275 * CrY + 0.000770482631801132 * Crk - + 0.151051492775562 * Cr + 0.00126935368114843 * YY - + 0.00265090189010898 * Yk + 0.25802910206845 * Y - + 0.000318913117588328 * kk - 0.213742400323665 * k; + + var b = - 20.810012546947 - + 0.000570115196973677 * CbCb - 2.63409051004589e-5 * CbCr + + 0.0020741088115012* CbY - 0.00288260236853442* Cbk + + 0.814272968359295 * Cb - 1.53496057440975e-5 * CrCr - + 0.000132689043961446 * CrY + 0.000560833691242812 * Crk - + 0.195152027534049 * Cr + 0.00174418132927582 * YY - + 0.00255243321439347 * Yk + 0.116935020465145 * Y - + 0.000343531996510555 * kk + 0.24165260232407 * k; + + data[offset++] = clamp0to255(r); + data[offset++] = clamp0to255(g); + data[offset++] = clamp0to255(b); + } + return data; + }, + + _convertYcckToCmyk: function convertYcckToCmyk(data) { + var Y, Cb, Cr; + for (var i = 0; i < data.length; i += this.numComponents) { + Y = data[i]; + Cb = data[i + 1]; + Cr = data[i + 2]; + data[i ] = clamp0to255(434.456 - Y - 1.402 * Cr); + data[i + 1] = clamp0to255(119.541 - Y + 0.344 * Cb + 0.714 * Cr); + data[i + 2] = clamp0to255(481.816 - Y - 1.772 * Cb); + // K in data[i + 3] is unchanged + } + return data; + }, + + _convertCmykToRgb: function convertCmykToRgb(data) { + var c, m, y, k; + var offset = 0; + var length = data.length; + var min = -255 * 255 * 255; + var scale = 1 / 255 / 255; + for (var i = 0; i < length;) { + c = data[i++]; + m = data[i++]; + y = data[i++]; + k = data[i++]; + + var r = + c * (-4.387332384609988 * c + 54.48615194189176 * m + + 18.82290502165302 * y + 212.25662451639585 * k - + 72734.4411664936) + + m * (1.7149763477362134 * m - 5.6096736904047315 * y - + 17.873870861415444 * k - 1401.7366389350734) + + y * (-2.5217340131683033 * y - 21.248923337353073 * k + + 4465.541406466231) - + k * (21.86122147463605 * k + 48317.86113160301); + var g = + c * (8.841041422036149 * c + 60.118027045597366 * m + + 6.871425592049007 * y + 31.159100130055922 * k - + 20220.756542821975) + + m * (-15.310361306967817 * m + 17.575251261109482 * y + + 131.35250912493976 * k - 48691.05921601825) + + y * (4.444339102852739 * y + 9.8632861493405 * k - + 6341.191035517494) - + k * (20.737325471181034 * k + 47890.15695978492); + var b = + c * (0.8842522430003296 * c + 8.078677503112928 * m + + 30.89978309703729 * y - 0.23883238689178934 * k - + 3616.812083916688) + + m * (10.49593273432072 * m + 63.02378494754052 * y + + 50.606957656360734 * k - 28620.90484698408) + + y * (0.03296041114873217 * y + 115.60384449646641 * k - + 49363.43385999684) - + k * (22.33816807309886 * k + 45932.16563550634); + + data[offset++] = r >= 0 ? 255 : r <= min ? 0 : 255 + r * scale | 0; + data[offset++] = g >= 0 ? 255 : g <= min ? 0 : 255 + g * scale | 0; + data[offset++] = b >= 0 ? 255 : b <= min ? 0 : 255 + b * scale | 0; + } + return data; + }, + + getData: function getData(width, height, forceRGBoutput) { + if (this.numComponents > 4) { + throw 'Unsupported color mode'; + } + // type of data: Uint8Array(width * height * numComponents) + var data = this._getLinearizedBlockData(width, height); + + if (this.numComponents === 3) { + return this._convertYccToRgb(data); + } else if (this.numComponents === 4) { + if (this._isColorConversionNeeded()) { + if (forceRGBoutput) { + return this._convertYcckToRgb(data); + } else { + return this._convertYcckToCmyk(data); + } + } else { + return this._convertCmykToRgb(data); + } + } + return data; + } + }; + + return constructor; +})(); + + var JpxImage = (function JpxImageClosure() { // Table E.1 var SubbandsGainLog2 = { 'LL': 0, 'LH': 1, 'HL': 1, 'HH': 2 }; @@ -38430,69 +39491,68 @@ var JpxImage = (function JpxImageClosure left: tile0.left, top: tile0.top, width: tile0.width, height: tile0.height, items: out }; // Section G.2.2 Inverse multi component transform - var shift, offset, max, min; + var shift, offset, max, min, maxK; var pos = 0, j, jj, y0, y1, y2, r, g, b, k, val; if (tile.codingStyleDefaultParameters.multipleComponentTransform) { var fourComponents = componentsCount === 4; var y0items = transformedTiles[0].items; var y1items = transformedTiles[1].items; var y2items = transformedTiles[2].items; var y3items = fourComponents ? transformedTiles[3].items : null; // HACK: The multiple component transform formulas below assume that // all components have the same precision. With this in mind, we // compute shift and offset only once. shift = components[0].precision - 8; offset = (128 << shift) + 0.5; - max = (127.5 * (1 << shift)); - min = -max; + max = 255 * (1 << shift); + maxK = max * 0.5; + min = -maxK; var component0 = tile.components[0]; + var alpha01 = componentsCount - 3; + jj = y0items.length; if (!component0.codingStyleParameters.reversibleTransformation) { // inverse irreversible multiple component transform - for (j = 0, jj = y0items.length; j < jj; ++j) { - y0 = y0items[j]; + for (j = 0; j < jj; j++, pos += alpha01) { + y0 = y0items[j] + offset; y1 = y1items[j]; y2 = y2items[j]; r = y0 + 1.402 * y2; g = y0 - 0.34413 * y1 - 0.71414 * y2; b = y0 + 1.772 * y1; - out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift; - out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift; - out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift; - if (fourComponents) { - k = y3items[j]; - out[pos++] = - k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift; - } + out[pos++] = r <= 0 ? 0 : r >= max ? 255 : r >> shift; + out[pos++] = g <= 0 ? 0 : g >= max ? 255 : g >> shift; + out[pos++] = b <= 0 ? 0 : b >= max ? 255 : b >> shift; } } else { // inverse reversible multiple component transform - for (j = 0, jj = y0items.length; j < jj; ++j) { - y0 = y0items[j]; + for (j = 0; j < jj; j++, pos += alpha01) { + y0 = y0items[j] + offset; y1 = y1items[j]; y2 = y2items[j]; g = y0 - ((y2 + y1) >> 2); r = g + y2; b = g + y1; - out[pos++] = r <= min ? 0 : r >= max ? 255 : (r + offset) >> shift; - out[pos++] = g <= min ? 0 : g >= max ? 255 : (g + offset) >> shift; - out[pos++] = b <= min ? 0 : b >= max ? 255 : (b + offset) >> shift; - if (fourComponents) { - k = y3items[j]; - out[pos++] = - k <= min ? 0 : k >= max ? 255 : (k + offset) >> shift; - } + out[pos++] = r <= 0 ? 0 : r >= max ? 255 : r >> shift; + out[pos++] = g <= 0 ? 0 : g >= max ? 255 : g >> shift; + out[pos++] = b <= 0 ? 0 : b >= max ? 255 : b >> shift; + } + } + if (fourComponents) { + for (j = 0, pos = 3; j < jj; j++, pos += 4) { + k = y3items[j]; + out[pos] = k <= min ? 0 : k >= maxK ? 255 : (k + offset) >> shift; } } } else { // no multi-component transform for (c = 0; c < componentsCount; c++) { var items = transformedTiles[c].items; shift = components[c].precision - 8; offset = (128 << shift) + 0.5; max = (127.5 * (1 << shift)); @@ -40884,975 +41944,14 @@ var MurmurHash3_64 = (function MurmurHas } return str; } }; return MurmurHash3_64; })(); -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* - Copyright 2011 notmasteryet - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -// - The JPEG specification can be found in the ITU CCITT Recommendation T.81 -// (www.w3.org/Graphics/JPEG/itu-t81.pdf) -// - The JFIF specification can be found in the JPEG File Interchange Format -// (www.w3.org/Graphics/JPEG/jfif3.pdf) -// - The Adobe Application-Specific JPEG markers in the Supporting the DCT Filters -// in PostScript Level 2, Technical Note #5116 -// (partners.adobe.com/public/developer/en/ps/sdk/5116.DCT_Filter.pdf) - -var JpegImage = (function jpegImage() { - "use strict"; - var dctZigZag = new Int32Array([ - 0, - 1, 8, - 16, 9, 2, - 3, 10, 17, 24, - 32, 25, 18, 11, 4, - 5, 12, 19, 26, 33, 40, - 48, 41, 34, 27, 20, 13, 6, - 7, 14, 21, 28, 35, 42, 49, 56, - 57, 50, 43, 36, 29, 22, 15, - 23, 30, 37, 44, 51, 58, - 59, 52, 45, 38, 31, - 39, 46, 53, 60, - 61, 54, 47, - 55, 62, - 63 - ]); - - var dctCos1 = 4017 // cos(pi/16) - var dctSin1 = 799 // sin(pi/16) - var dctCos3 = 3406 // cos(3*pi/16) - var dctSin3 = 2276 // sin(3*pi/16) - var dctCos6 = 1567 // cos(6*pi/16) - var dctSin6 = 3784 // sin(6*pi/16) - var dctSqrt2 = 5793 // sqrt(2) - var dctSqrt1d2 = 2896 // sqrt(2) / 2 - - function constructor() { - } - - function buildHuffmanTable(codeLengths, values) { - var k = 0, code = [], i, j, length = 16; - while (length > 0 && !codeLengths[length - 1]) - length--; - code.push({children: [], index: 0}); - var p = code[0], q; - for (i = 0; i < length; i++) { - for (j = 0; j < codeLengths[i]; j++) { - p = code.pop(); - p.children[p.index] = values[k]; - while (p.index > 0) { - p = code.pop(); - } - p.index++; - code.push(p); - while (code.length <= i) { - code.push(q = {children: [], index: 0}); - p.children[p.index] = q.children; - p = q; - } - k++; - } - if (i + 1 < length) { - // p here points to last code - code.push(q = {children: [], index: 0}); - p.children[p.index] = q.children; - p = q; - } - } - return code[0].children; - } - - function getBlockBufferOffset(component, row, col) { - return 64 * ((component.blocksPerLine + 1) * row + col); - } - - function decodeScan(data, offset, - frame, components, resetInterval, - spectralStart, spectralEnd, - successivePrev, successive) { - var precision = frame.precision; - var samplesPerLine = frame.samplesPerLine; - var scanLines = frame.scanLines; - var mcusPerLine = frame.mcusPerLine; - var progressive = frame.progressive; - var maxH = frame.maxH, maxV = frame.maxV; - - var startOffset = offset, bitsData = 0, bitsCount = 0; - - function readBit() { - if (bitsCount > 0) { - bitsCount--; - return (bitsData >> bitsCount) & 1; - } - bitsData = data[offset++]; - if (bitsData == 0xFF) { - var nextByte = data[offset++]; - if (nextByte) { - throw "unexpected marker: " + ((bitsData << 8) | nextByte).toString(16); - } - // unstuff 0 - } - bitsCount = 7; - return bitsData >>> 7; - } - - function decodeHuffman(tree) { - var node = tree; - var bit; - while ((bit = readBit()) !== null) { - node = node[bit]; - if (typeof node === 'number') - return node; - if (typeof node !== 'object') - throw "invalid huffman sequence"; - } - return null; - } - - function receive(length) { - var n = 0; - while (length > 0) { - var bit = readBit(); - if (bit === null) return; - n = (n << 1) | bit; - length--; - } - return n; - } - - function receiveAndExtend(length) { - var n = receive(length); - if (n >= 1 << (length - 1)) - return n; - return n + (-1 << length) + 1; - } - - function decodeBaseline(component, offset) { - var t = decodeHuffman(component.huffmanTableDC); - var diff = t === 0 ? 0 : receiveAndExtend(t); - component.blockData[offset] = (component.pred += diff); - var k = 1; - while (k < 64) { - var rs = decodeHuffman(component.huffmanTableAC); - var s = rs & 15, r = rs >> 4; - if (s === 0) { - if (r < 15) - break; - k += 16; - continue; - } - k += r; - var z = dctZigZag[k]; - component.blockData[offset + z] = receiveAndExtend(s); - k++; - } - } - - function decodeDCFirst(component, offset) { - var t = decodeHuffman(component.huffmanTableDC); - var diff = t === 0 ? 0 : (receiveAndExtend(t) << successive); - component.blockData[offset] = (component.pred += diff); - } - - function decodeDCSuccessive(component, offset) { - component.blockData[offset] |= readBit() << successive; - } - - var eobrun = 0; - function decodeACFirst(component, offset) { - if (eobrun > 0) { - eobrun--; - return; - } - var k = spectralStart, e = spectralEnd; - while (k <= e) { - var rs = decodeHuffman(component.huffmanTableAC); - var s = rs & 15, r = rs >> 4; - if (s === 0) { - if (r < 15) { - eobrun = receive(r) + (1 << r) - 1; - break; - } - k += 16; - continue; - } - k += r; - var z = dctZigZag[k]; - component.blockData[offset + z] = receiveAndExtend(s) * (1 << successive); - k++; - } - } - - var successiveACState = 0, successiveACNextValue; - function decodeACSuccessive(component, offset) { - var k = spectralStart, e = spectralEnd, r = 0; - while (k <= e) { - var z = dctZigZag[k]; - switch (successiveACState) { - case 0: // initial state - var rs = decodeHuffman(component.huffmanTableAC); - var s = rs & 15, r = rs >> 4; - if (s === 0) { - if (r < 15) { - eobrun = receive(r) + (1 << r); - successiveACState = 4; - } else { - r = 16; - successiveACState = 1; - } - } else { - if (s !== 1) - throw "invalid ACn encoding"; - successiveACNextValue = receiveAndExtend(s); - successiveACState = r ? 2 : 3; - } - continue; - case 1: // skipping r zero items - case 2: - if (component.blockData[offset + z]) { - component.blockData[offset + z] += (readBit() << successive); - } else { - r--; - if (r === 0) - successiveACState = successiveACState == 2 ? 3 : 0; - } - break; - case 3: // set value for a zero item - if (component.blockData[offset + z]) { - component.blockData[offset + z] += (readBit() << successive); - } else { - component.blockData[offset + z] = successiveACNextValue << successive; - successiveACState = 0; - } - break; - case 4: // eob - if (component.blockData[offset + z]) { - component.blockData[offset + z] += (readBit() << successive); - } - break; - } - k++; - } - if (successiveACState === 4) { - eobrun--; - if (eobrun === 0) - successiveACState = 0; - } - } - - function decodeMcu(component, decode, mcu, row, col) { - var mcuRow = (mcu / mcusPerLine) | 0; - var mcuCol = mcu % mcusPerLine; - var blockRow = mcuRow * component.v + row; - var blockCol = mcuCol * component.h + col; - var offset = getBlockBufferOffset(component, blockRow, blockCol); - decode(component, offset); - } - - function decodeBlock(component, decode, mcu) { - var blockRow = (mcu / component.blocksPerLine) | 0; - var blockCol = mcu % component.blocksPerLine; - var offset = getBlockBufferOffset(component, blockRow, blockCol); - decode(component, offset); - } - - var componentsLength = components.length; - var component, i, j, k, n; - var decodeFn; - if (progressive) { - if (spectralStart === 0) - decodeFn = successivePrev === 0 ? decodeDCFirst : decodeDCSuccessive; - else - decodeFn = successivePrev === 0 ? decodeACFirst : decodeACSuccessive; - } else { - decodeFn = decodeBaseline; - } - - var mcu = 0, marker; - var mcuExpected; - if (componentsLength == 1) { - mcuExpected = components[0].blocksPerLine * components[0].blocksPerColumn; - } else { - mcuExpected = mcusPerLine * frame.mcusPerColumn; - } - if (!resetInterval) { - resetInterval = mcuExpected; - } - - var h, v; - while (mcu < mcuExpected) { - // reset interval stuff - for (i = 0; i < componentsLength; i++) { - components[i].pred = 0; - } - eobrun = 0; - - if (componentsLength == 1) { - component = components[0]; - for (n = 0; n < resetInterval; n++) { - decodeBlock(component, decodeFn, mcu); - mcu++; - } - } else { - for (n = 0; n < resetInterval; n++) { - for (i = 0; i < componentsLength; i++) { - component = components[i]; - h = component.h; - v = component.v; - for (j = 0; j < v; j++) { - for (k = 0; k < h; k++) { - decodeMcu(component, decodeFn, mcu, j, k); - } - } - } - mcu++; - } - } - - // find marker - bitsCount = 0; - marker = (data[offset] << 8) | data[offset + 1]; - if (marker <= 0xFF00) { - throw "marker was not found"; - } - - if (marker >= 0xFFD0 && marker <= 0xFFD7) { // RSTx - offset += 2; - } else { - break; - } - } - - return offset - startOffset; - } - - // A port of poppler's IDCT method which in turn is taken from: - // Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz, - // "Practical Fast 1-D DCT Algorithms with 11 Multiplications", - // IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989, - // 988-991. - function quantizeAndInverse(component, blockBufferOffset, p) { - var qt = component.quantizationTable; - var v0, v1, v2, v3, v4, v5, v6, v7, t; - var i; - - // dequant - for (i = 0; i < 64; i++) { - p[i] = component.blockData[blockBufferOffset + i] * qt[i]; - } - - // inverse DCT on rows - for (i = 0; i < 8; ++i) { - var row = 8 * i; - - // check for all-zero AC coefficients - if (p[1 + row] == 0 && p[2 + row] == 0 && p[3 + row] == 0 && - p[4 + row] == 0 && p[5 + row] == 0 && p[6 + row] == 0 && - p[7 + row] == 0) { - t = (dctSqrt2 * p[0 + row] + 512) >> 10; - p[0 + row] = t; - p[1 + row] = t; - p[2 + row] = t; - p[3 + row] = t; - p[4 + row] = t; - p[5 + row] = t; - p[6 + row] = t; - p[7 + row] = t; - continue; - } - - // stage 4 - v0 = (dctSqrt2 * p[0 + row] + 128) >> 8; - v1 = (dctSqrt2 * p[4 + row] + 128) >> 8; - v2 = p[2 + row]; - v3 = p[6 + row]; - v4 = (dctSqrt1d2 * (p[1 + row] - p[7 + row]) + 128) >> 8; - v7 = (dctSqrt1d2 * (p[1 + row] + p[7 + row]) + 128) >> 8; - v5 = p[3 + row] << 4; - v6 = p[5 + row] << 4; - - // stage 3 - t = (v0 - v1+ 1) >> 1; - v0 = (v0 + v1 + 1) >> 1; - v1 = t; - t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8; - v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8; - v3 = t; - t = (v4 - v6 + 1) >> 1; - v4 = (v4 + v6 + 1) >> 1; - v6 = t; - t = (v7 + v5 + 1) >> 1; - v5 = (v7 - v5 + 1) >> 1; - v7 = t; - - // stage 2 - t = (v0 - v3 + 1) >> 1; - v0 = (v0 + v3 + 1) >> 1; - v3 = t; - t = (v1 - v2 + 1) >> 1; - v1 = (v1 + v2 + 1) >> 1; - v2 = t; - t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; - v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; - v7 = t; - t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; - v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; - v6 = t; - - // stage 1 - p[0 + row] = v0 + v7; - p[7 + row] = v0 - v7; - p[1 + row] = v1 + v6; - p[6 + row] = v1 - v6; - p[2 + row] = v2 + v5; - p[5 + row] = v2 - v5; - p[3 + row] = v3 + v4; - p[4 + row] = v3 - v4; - } - - // inverse DCT on columns - for (i = 0; i < 8; ++i) { - var col = i; - - // check for all-zero AC coefficients - if (p[1*8 + col] == 0 && p[2*8 + col] == 0 && p[3*8 + col] == 0 && - p[4*8 + col] == 0 && p[5*8 + col] == 0 && p[6*8 + col] == 0 && - p[7*8 + col] == 0) { - t = (dctSqrt2 * p[i+0] + 8192) >> 14; - p[0*8 + col] = t; - p[1*8 + col] = t; - p[2*8 + col] = t; - p[3*8 + col] = t; - p[4*8 + col] = t; - p[5*8 + col] = t; - p[6*8 + col] = t; - p[7*8 + col] = t; - continue; - } - - // stage 4 - v0 = (dctSqrt2 * p[0*8 + col] + 2048) >> 12; - v1 = (dctSqrt2 * p[4*8 + col] + 2048) >> 12; - v2 = p[2*8 + col]; - v3 = p[6*8 + col]; - v4 = (dctSqrt1d2 * (p[1*8 + col] - p[7*8 + col]) + 2048) >> 12; - v7 = (dctSqrt1d2 * (p[1*8 + col] + p[7*8 + col]) + 2048) >> 12; - v5 = p[3*8 + col]; - v6 = p[5*8 + col]; - - // stage 3 - t = (v0 - v1 + 1) >> 1; - v0 = (v0 + v1 + 1) >> 1; - v1 = t; - t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12; - v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12; - v3 = t; - t = (v4 - v6 + 1) >> 1; - v4 = (v4 + v6 + 1) >> 1; - v6 = t; - t = (v7 + v5 + 1) >> 1; - v5 = (v7 - v5 + 1) >> 1; - v7 = t; - - // stage 2 - t = (v0 - v3 + 1) >> 1; - v0 = (v0 + v3 + 1) >> 1; - v3 = t; - t = (v1 - v2 + 1) >> 1; - v1 = (v1 + v2 + 1) >> 1; - v2 = t; - t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12; - v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12; - v7 = t; - t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12; - v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12; - v6 = t; - - // stage 1 - p[0*8 + col] = v0 + v7; - p[7*8 + col] = v0 - v7; - p[1*8 + col] = v1 + v6; - p[6*8 + col] = v1 - v6; - p[2*8 + col] = v2 + v5; - p[5*8 + col] = v2 - v5; - p[3*8 + col] = v3 + v4; - p[4*8 + col] = v3 - v4; - } - - // convert to 8-bit integers - for (i = 0; i < 64; ++i) { - var index = blockBufferOffset + i; - component.blockData[index] = clampTo8bitInt((p[i] + 2056) >> 4); - } - } - - function buildComponentData(frame, component) { - var lines = []; - var blocksPerLine = component.blocksPerLine; - var blocksPerColumn = component.blocksPerColumn; - var samplesPerLine = blocksPerLine << 3; - var computationBuffer = new Int32Array(64); - - var i, j, ll = 0; - for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) { - for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) { - var offset = getBlockBufferOffset(component, blockRow, blockCol) - quantizeAndInverse(component, offset, computationBuffer); - } - } - return component.blockData; - } - - function clampTo8bitInt(a) { - return a <= 0 ? 0 : a >= 255 ? 255 : a | 0; - } - - function clamp0to255(a) { - return a <= 0 ? 0 : a >= 255 ? 255 : a; - } - - constructor.prototype = { - load: function load(path) { - var xhr = new XMLHttpRequest(); - xhr.open("GET", path, true); - xhr.responseType = "arraybuffer"; - xhr.onload = (function() { - // TODO catch parse error - var data = new Uint8Array(xhr.response || xhr.mozResponseArrayBuffer); - this.parse(data); - if (this.onload) - this.onload(); - }).bind(this); - xhr.send(null); - }, - - parse: function parse(data) { - - function readUint16() { - var value = (data[offset] << 8) | data[offset + 1]; - offset += 2; - return value; - } - - function readDataBlock() { - var length = readUint16(); - var array = data.subarray(offset, offset + length - 2); - offset += array.length; - return array; - } - - function prepareComponents(frame) { - var mcusPerLine = Math.ceil(frame.samplesPerLine / 8 / frame.maxH); - var mcusPerColumn = Math.ceil(frame.scanLines / 8 / frame.maxV); - for (var i = 0; i < frame.components.length; i++) { - component = frame.components[i]; - var blocksPerLine = Math.ceil(Math.ceil(frame.samplesPerLine / 8) * component.h / frame.maxH); - var blocksPerColumn = Math.ceil(Math.ceil(frame.scanLines / 8) * component.v / frame.maxV); - var blocksPerLineForMcu = mcusPerLine * component.h; - var blocksPerColumnForMcu = mcusPerColumn * component.v; - - var blocksBufferSize = 64 * blocksPerColumnForMcu - * (blocksPerLineForMcu + 1); - component.blockData = new Int16Array(blocksBufferSize); - component.blocksPerLine = blocksPerLine; - component.blocksPerColumn = blocksPerColumn; - } - frame.mcusPerLine = mcusPerLine; - frame.mcusPerColumn = mcusPerColumn; - } - - var offset = 0, length = data.length; - var jfif = null; - var adobe = null; - var pixels = null; - var frame, resetInterval; - var quantizationTables = []; - var huffmanTablesAC = [], huffmanTablesDC = []; - var fileMarker = readUint16(); - if (fileMarker != 0xFFD8) { // SOI (Start of Image) - throw "SOI not found"; - } - - fileMarker = readUint16(); - while (fileMarker != 0xFFD9) { // EOI (End of image) - var i, j, l; - switch(fileMarker) { - case 0xFFE0: // APP0 (Application Specific) - case 0xFFE1: // APP1 - case 0xFFE2: // APP2 - case 0xFFE3: // APP3 - case 0xFFE4: // APP4 - case 0xFFE5: // APP5 - case 0xFFE6: // APP6 - case 0xFFE7: // APP7 - case 0xFFE8: // APP8 - case 0xFFE9: // APP9 - case 0xFFEA: // APP10 - case 0xFFEB: // APP11 - case 0xFFEC: // APP12 - case 0xFFED: // APP13 - case 0xFFEE: // APP14 - case 0xFFEF: // APP15 - case 0xFFFE: // COM (Comment) - var appData = readDataBlock(); - - if (fileMarker === 0xFFE0) { - if (appData[0] === 0x4A && appData[1] === 0x46 && appData[2] === 0x49 && - appData[3] === 0x46 && appData[4] === 0) { // 'JFIF\x00' - jfif = { - version: { major: appData[5], minor: appData[6] }, - densityUnits: appData[7], - xDensity: (appData[8] << 8) | appData[9], - yDensity: (appData[10] << 8) | appData[11], - thumbWidth: appData[12], - thumbHeight: appData[13], - thumbData: appData.subarray(14, 14 + 3 * appData[12] * appData[13]) - }; - } - } - // TODO APP1 - Exif - if (fileMarker === 0xFFEE) { - if (appData[0] === 0x41 && appData[1] === 0x64 && appData[2] === 0x6F && - appData[3] === 0x62 && appData[4] === 0x65 && appData[5] === 0) { // 'Adobe\x00' - adobe = { - version: appData[6], - flags0: (appData[7] << 8) | appData[8], - flags1: (appData[9] << 8) | appData[10], - transformCode: appData[11] - }; - } - } - break; - - case 0xFFDB: // DQT (Define Quantization Tables) - var quantizationTablesLength = readUint16(); - var quantizationTablesEnd = quantizationTablesLength + offset - 2; - while (offset < quantizationTablesEnd) { - var quantizationTableSpec = data[offset++]; - var tableData = new Int32Array(64); - if ((quantizationTableSpec >> 4) === 0) { // 8 bit values - for (j = 0; j < 64; j++) { - var z = dctZigZag[j]; - tableData[z] = data[offset++]; - } - } else if ((quantizationTableSpec >> 4) === 1) { //16 bit - for (j = 0; j < 64; j++) { - var z = dctZigZag[j]; - tableData[z] = readUint16(); - } - } else - throw "DQT: invalid table spec"; - quantizationTables[quantizationTableSpec & 15] = tableData; - } - break; - - case 0xFFC0: // SOF0 (Start of Frame, Baseline DCT) - case 0xFFC1: // SOF1 (Start of Frame, Extended DCT) - case 0xFFC2: // SOF2 (Start of Frame, Progressive DCT) - if (frame) { - throw "Only single frame JPEGs supported"; - } - readUint16(); // skip data length - frame = {}; - frame.extended = (fileMarker === 0xFFC1); - frame.progressive = (fileMarker === 0xFFC2); - frame.precision = data[offset++]; - frame.scanLines = readUint16(); - frame.samplesPerLine = readUint16(); - frame.components = []; - frame.componentIds = {}; - var componentsCount = data[offset++], componentId; - var maxH = 0, maxV = 0; - for (i = 0; i < componentsCount; i++) { - componentId = data[offset]; - var h = data[offset + 1] >> 4; - var v = data[offset + 1] & 15; - if (maxH < h) maxH = h; - if (maxV < v) maxV = v; - var qId = data[offset + 2]; - var l = frame.components.push({ - h: h, - v: v, - quantizationTable: quantizationTables[qId] - }); - frame.componentIds[componentId] = l - 1; - offset += 3; - } - frame.maxH = maxH; - frame.maxV = maxV; - prepareComponents(frame); - break; - - case 0xFFC4: // DHT (Define Huffman Tables) - var huffmanLength = readUint16(); - for (i = 2; i < huffmanLength;) { - var huffmanTableSpec = data[offset++]; - var codeLengths = new Uint8Array(16); - var codeLengthSum = 0; - for (j = 0; j < 16; j++, offset++) - codeLengthSum += (codeLengths[j] = data[offset]); - var huffmanValues = new Uint8Array(codeLengthSum); - for (j = 0; j < codeLengthSum; j++, offset++) - huffmanValues[j] = data[offset]; - i += 17 + codeLengthSum; - - ((huffmanTableSpec >> 4) === 0 ? - huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = - buildHuffmanTable(codeLengths, huffmanValues); - } - break; - - case 0xFFDD: // DRI (Define Restart Interval) - readUint16(); // skip data length - resetInterval = readUint16(); - break; - - case 0xFFDA: // SOS (Start of Scan) - var scanLength = readUint16(); - var selectorsCount = data[offset++]; - var components = [], component; - for (i = 0; i < selectorsCount; i++) { - var componentIndex = frame.componentIds[data[offset++]]; - component = frame.components[componentIndex]; - var tableSpec = data[offset++]; - component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4]; - component.huffmanTableAC = huffmanTablesAC[tableSpec & 15]; - components.push(component); - } - var spectralStart = data[offset++]; - var spectralEnd = data[offset++]; - var successiveApproximation = data[offset++]; - var processed = decodeScan(data, offset, - frame, components, resetInterval, - spectralStart, spectralEnd, - successiveApproximation >> 4, successiveApproximation & 15); - offset += processed; - break; - default: - if (data[offset - 3] == 0xFF && - data[offset - 2] >= 0xC0 && data[offset - 2] <= 0xFE) { - // could be incorrect encoding -- last 0xFF byte of the previous - // block was eaten by the encoder - offset -= 3; - break; - } - throw "unknown JPEG marker " + fileMarker.toString(16); - } - fileMarker = readUint16(); - } - - this.width = frame.samplesPerLine; - this.height = frame.scanLines; - this.jfif = jfif; - this.adobe = adobe; - this.components = []; - for (var i = 0; i < frame.components.length; i++) { - var component = frame.components[i]; - this.components.push({ - output: buildComponentData(frame, component), - scaleX: component.h / frame.maxH, - scaleY: component.v / frame.maxV, - blocksPerLine: component.blocksPerLine, - blocksPerColumn: component.blocksPerColumn - }); - } - }, - - getData: function getData(width, height) { - var scaleX = this.width / width, scaleY = this.height / height; - - var component, componentScaleX, componentScaleY; - var x, y, i; - var offset = 0; - var Y, Cb, Cr, K, C, M, Ye, R, G, B; - var colorTransform; - var numComponents = this.components.length; - var dataLength = width * height * numComponents; - var data = new Uint8Array(dataLength); - var componentLine; - - // lineData is reused for all components. Assume first component is - // the biggest - var lineData = new Uint8Array((this.components[0].blocksPerLine << 3) * - this.components[0].blocksPerColumn * 8); - - // First construct image data ... - for (i = 0; i < numComponents; i++) { - component = this.components[i]; - var blocksPerLine = component.blocksPerLine; - var blocksPerColumn = component.blocksPerColumn; - var samplesPerLine = blocksPerLine << 3; - - var j, k, ll = 0; - var lineOffset = 0; - for (var blockRow = 0; blockRow < blocksPerColumn; blockRow++) { - var scanLine = blockRow << 3; - for (var blockCol = 0; blockCol < blocksPerLine; blockCol++) { - var bufferOffset = getBlockBufferOffset(component, blockRow, blockCol); - var offset = 0, sample = blockCol << 3; - for (j = 0; j < 8; j++) { - var lineOffset = (scanLine + j) * samplesPerLine; - for (k = 0; k < 8; k++) { - lineData[lineOffset + sample + k] = - component.output[bufferOffset + offset++]; - } - } - } - } - - componentScaleX = component.scaleX * scaleX; - componentScaleY = component.scaleY * scaleY; - offset = i; - - var cx, cy; - var index; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - cy = 0 | (y * componentScaleY); - cx = 0 | (x * componentScaleX); - index = cy * samplesPerLine + cx; - data[offset] = lineData[index]; - offset += numComponents; - } - } - } - - // ... then transform colors, if necessary - switch (numComponents) { - case 1: case 2: break; - // no color conversion for one or two compoenents - - case 3: - // The default transform for three components is true - colorTransform = true; - // The adobe transform marker overrides any previous setting - if (this.adobe && this.adobe.transformCode) - colorTransform = true; - else if (typeof this.colorTransform !== 'undefined') - colorTransform = !!this.colorTransform; - - if (colorTransform) { - for (i = 0; i < dataLength; i += numComponents) { - Y = data[i ]; - Cb = data[i + 1]; - Cr = data[i + 2]; - - R = clamp0to255(Y + 1.402 * (Cr - 128)); - G = clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); - B = clamp0to255(Y + 1.772 * (Cb - 128)); - - data[i ] = R; - data[i + 1] = G; - data[i + 2] = B; - } - } - break; - case 4: - // The default transform for four components is false - colorTransform = false; - // The adobe transform marker overrides any previous setting - if (this.adobe && this.adobe.transformCode) - colorTransform = true; - else if (typeof this.colorTransform !== 'undefined') - colorTransform = !!this.colorTransform; - - if (colorTransform) { - for (i = 0; i < dataLength; i += numComponents) { - Y = data[i]; - Cb = data[i + 1]; - Cr = data[i + 2]; - - C = 255 - clamp0to255(Y + 1.402 * (Cr - 128)); - M = 255 - clamp0to255(Y - 0.3441363 * (Cb - 128) - 0.71413636 * (Cr - 128)); - Ye = 255 - clamp0to255(Y + 1.772 * (Cb - 128)); - - data[i ] = C; - data[i + 1] = M; - data[i + 2] = Ye; - // K is unchanged - } - } - break; - default: - throw 'Unsupported color mode'; - } - return data; - }, - copyToImageData: function copyToImageData(imageData) { - var width = imageData.width, height = imageData.height; - var imageDataBytes = width * height * 4; - var imageDataArray = imageData.data; - var data = this.getData(width, height); - var i = 0, j = 0; - var Y, K, C, M, R, G, B; - switch (this.components.length) { - case 1: - while (j < imageDataBytes) { - Y = data[i++]; - - imageDataArray[j++] = Y; - imageDataArray[j++] = Y; - imageDataArray[j++] = Y; - imageDataArray[j++] = 255; - } - break; - case 3: - while (j < imageDataBytes) { - R = data[i++]; - G = data[i++]; - B = data[i++]; - - imageDataArray[j++] = R; - imageDataArray[j++] = G; - imageDataArray[j++] = B; - imageDataArray[j++] = 255; - } - break; - case 4: - while (j < imageDataBytes) { - C = data[i++]; - M = data[i++]; - Y = data[i++]; - K = data[i++]; - - R = 255 - clamp0to255(C * (1 - K / 255) + K); - G = 255 - clamp0to255(M * (1 - K / 255) + K); - B = 255 - clamp0to255(Y * (1 - K / 255) + K); - - imageDataArray[j++] = R; - imageDataArray[j++] = G; - imageDataArray[j++] = B; - imageDataArray[j++] = 255; - } - break; - default: - throw 'Unsupported color mode'; - } - } - }; - - return constructor; -})(); }).call((typeof window === 'undefined') ? this : window);
--- a/browser/extensions/pdfjs/content/web/viewer.css +++ b/browser/extensions/pdfjs/content/web/viewer.css @@ -1078,56 +1078,75 @@ a:focus > .thumbnail > .thumbnailSelecti } #outlineView, #attachmentsView { position: absolute; width: 192px; top: 0; bottom: 0; - padding: 4px 4px 0; overflow: auto; -moz-user-select: none; } +#outlineView { + padding: 4px 4px 0; +} +#attachmentsView { + padding: 3px 4px 0; +} + html[dir='ltr'] .outlineItem > .outlineItems { margin-left: 20px; } html[dir='rtl'] .outlineItem > .outlineItems { margin-right: 20px; } .outlineItem > a, -.attachmentsItem > a { +.attachmentsItem > button { text-decoration: none; display: inline-block; min-width: 95%; height: auto; margin-bottom: 1px; border-radius: 2px; color: hsla(0,0%,100%,.8); font-size: 13px; line-height: 15px; -moz-user-select: none; white-space: normal; } -html[dir='ltr'] .outlineItem > a, -html[dir='ltr'] .attachmentsItem > a { +.attachmentsItem > button { + border: 0 none; + background: none; + cursor: pointer; + width: 100%; +} + +html[dir='ltr'] .outlineItem > a { padding: 2px 0 5px 10px; } +html[dir='ltr'] .attachmentsItem > button { + padding: 2px 0 3px 7px; + text-align: left; +} -html[dir='rtl'] .outlineItem > a, -html[dir='rtl'] .attachmentsItem > a { +html[dir='rtl'] .outlineItem > a { padding: 2px 10px 5px 0; } +html[dir='rtl'] .attachmentsItem > button { + padding: 2px 7px 3px 0; + text-align: right; +} .outlineItem > a:hover, -.attachmentsItem > a:hover { +.attachmentsItem > button:hover { background-color: hsla(0,0%,100%,.02); background-image: linear-gradient(hsla(0,0%,100%,.05), hsla(0,0%,100%,0)); background-clip: padding-box; box-shadow: 0 1px 0 hsla(0,0%,100%,.05) inset, 0 0 1px hsla(0,0%,100%,.2) inset, 0 0 1px hsla(0,0%,0%,.2); color: hsla(0,0%,100%,.9); }
--- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -12,19 +12,20 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle, PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager, getFileName, scrollIntoView, getPDFFileNameFromURL, PDFHistory, - Preferences, ViewHistory, PageView, ThumbnailView, URL, + Preferences, SidebarView, ViewHistory, PageView, ThumbnailView, URL, noContextMenuHandler, SecondaryToolbar, PasswordPrompt, - PresentationMode, HandTool, Promise, DocumentProperties */ + PresentationMode, HandTool, Promise, DocumentProperties, + DocumentOutlineView, DocumentAttachmentsView */ 'use strict'; var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf'; var DEFAULT_SCALE = 'auto'; var DEFAULT_SCALE_DELTA = 1.1; var UNKNOWN_SCALE = 0; var CACHE_SIZE = 20; @@ -304,22 +305,29 @@ var Cache = function cacheCache(size) { }; var DEFAULT_PREFERENCES = { showPreviousViewOnLoad: true, defaultZoomValue: '', - ifAvailableShowOutlineOnLoad: false, + sidebarViewOnLoad: 0, enableHandToolOnLoad: false, enableWebGL: false }; +var SidebarView = { + NONE: 0, + THUMBS: 1, + OUTLINE: 2, + ATTACHMENTS: 3 +}; + /** * Preferences - Utility for storing persistent settings. * Used for settings that should be applied to all opened documents, * or every time the viewer is loaded. */ var Preferences = { prefs: Object.create(DEFAULT_PREFERENCES), isInitializedPromiseResolved: false, @@ -582,36 +590,22 @@ var currentPageNumber = 1; * for 'node make <flag>' the following cases exist: * - FIREFOX or MOZCENTRAL - uses sessionStorage. * - B2G - uses asyncStorage. * - GENERIC or CHROME - uses localStorage, if it is available. */ var ViewHistory = (function ViewHistoryClosure() { function ViewHistory(fingerprint) { this.fingerprint = fingerprint; - var initializedPromiseResolve; this.isInitializedPromiseResolved = false; - this.initializedPromise = new Promise(function (resolve) { - initializedPromiseResolve = resolve; - }); - - var resolvePromise = (function ViewHistoryResolvePromise(db) { + this.initializedPromise = + this._readFromStorage().then(function (databaseStr) { this.isInitializedPromiseResolved = true; - this.initialize(db || '{}'); - initializedPromiseResolve(); - }).bind(this); - - - resolvePromise(sessionStorage.getItem('pdfjsHistory')); - - } - - ViewHistory.prototype = { - initialize: function ViewHistory_initialize(database) { - database = JSON.parse(database); + + var database = JSON.parse(databaseStr || '{}'); if (!('files' in database)) { database.files = []; } if (database.files.length >= VIEW_HISTORY_MEMORY) { database.files.shift(); } var index; for (var i = 0, length = database.files.length; i < length; i++) { @@ -621,29 +615,55 @@ var ViewHistory = (function ViewHistoryC break; } } if (typeof index !== 'number') { index = database.files.push({fingerprint: this.fingerprint}) - 1; } this.file = database.files[index]; this.database = database; + }.bind(this)); + } + + ViewHistory.prototype = { + _writeToStorage: function ViewHistory_writeToStorage() { + return new Promise(function (resolve) { + var databaseStr = JSON.stringify(this.database); + + + sessionStorage.setItem('pdfjsHistory', databaseStr); + resolve(); + + }.bind(this)); + }, + + _readFromStorage: function ViewHistory_readFromStorage() { + return new Promise(function (resolve) { + + resolve(sessionStorage.getItem('pdfjsHistory')); + + }); }, set: function ViewHistory_set(name, val) { if (!this.isInitializedPromiseResolved) { return; } - var file = this.file; - file[name] = val; - var database = JSON.stringify(this.database); - - - sessionStorage.setItem('pdfjsHistory',database); - + this.file[name] = val; + this._writeToStorage(); + }, + + setMultiple: function ViewHistory_setMultiple(properties) { + if (!this.isInitializedPromiseResolved) { + return; + } + for (var name in properties) { + this.file[name] = properties[name]; + } + this._writeToStorage(); }, get: function ViewHistory_get(name, defaultValue) { if (!this.isInitializedPromiseResolved) { return defaultValue; } return this.file[name] || defaultValue; } @@ -2173,21 +2193,21 @@ var HandTool = { mozL10n.get('hand_tool_enable_label', null, 'Enable hand tool'); } } }); if (toggleHandTool) { toggleHandTool.addEventListener('click', this.toggle.bind(this), false); window.addEventListener('localized', function (evt) { - Preferences.get('enableHandToolOnLoad').then(function (prefValue) { - if (prefValue) { + Preferences.get('enableHandToolOnLoad').then(function resolved(value) { + if (value) { this.handTool.activate(); } - }.bind(this)); + }.bind(this), function rejected(reason) {}); }.bind(this)); } }, toggle: function handToolToggle() { this.handTool.toggle(); SecondaryToolbar.close(); }, @@ -2510,19 +2530,22 @@ var PDFView = { pageCountField: document.getElementById('pageCountField') }); container.addEventListener('scroll', function() { self.lastScroll = Date.now(); }, false); var initializedPromise = Promise.all([ - Preferences.get('enableWebGL').then(function (value) { + Preferences.get('enableWebGL').then(function resolved(value) { PDFJS.disableWebGL = !value; - }) + }, function rejected(reason) {}), + Preferences.get('sidebarViewOnLoad').then(function resolved(value) { + self.preferenceSidebarViewOnLoad = value; + }, function rejected(reason) {}) // TODO move more preferences and other async stuff here ]); return initializedPromise.then(function () { PDFView.initialized = true; }); }, @@ -2531,16 +2554,19 @@ var PDFView = { }, // Helper function to keep track whether a div was scrolled up or down and // then call a callback. watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) { state.down = true; state.lastY = viewAreaElement.scrollTop; viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) { + if (!PDFView.pdfDocument) { + return; + } var currentY = viewAreaElement.scrollTop; var lastY = state.lastY; if (currentY > lastY) { state.down = true; } else if (currentY < lastY) { state.down = false; } // else do nothing and use previous value @@ -3104,17 +3130,17 @@ var PDFView = { }, progress: function pdfViewProgress(level) { var percent = Math.round(level * 100); // When we transition from full request to range requests, it's possible // that we discard some of the loaded data. This can cause the loading // bar to move backwards. So prevent this by only updating the bar if it // increases. - if (percent > PDFView.loadingBar.percent) { + if (percent > PDFView.loadingBar.percent || isNaN(percent)) { PDFView.loadingBar.percent = percent; } }, load: function pdfViewLoad(pdfDocument, scale) { var self = this; var isOnePageRenderedResolved = false; var resolveOnePageRendered = null; @@ -3257,18 +3283,18 @@ var PDFView = { self.setInitialView(storedHash, scale); // Make all navigation keys work on document load, // unless the viewer is embedded in a web page. if (!self.isViewerEmbedded) { self.container.focus(); self.container.blur(); } - }, function rejected(errorMsg) { - console.error(errorMsg); + }, function rejected(reason) { + console.error(reason); firstPagePromise.then(function () { self.setInitialView(null, scale); }); }); pagesPromise.then(function() { if (PDFView.supportsPrinting) { @@ -3301,34 +3327,38 @@ var PDFView = { // outline depends on destinations and pagesRefMap var promises = [pagesPromise, destinationsPromise, PDFView.animationStartedPromise]; Promise.all(promises).then(function() { pdfDocument.getOutline().then(function(outline) { self.outline = new DocumentOutlineView(outline); document.getElementById('viewOutline').disabled = !outline; - if (outline) { - Preferences.get('ifAvailableShowOutlineOnLoad').then( - function (prefValue) { - if (prefValue) { - if (!self.sidebarOpen) { - document.getElementById('sidebarToggle').click(); - } - self.switchSidebarView('outline'); - } - }); + if (outline && + self.preferenceSidebarViewOnLoad === SidebarView.OUTLINE) { + self.switchSidebarView('outline', true); } }); pdfDocument.getAttachments().then(function(attachments) { self.attachments = new DocumentAttachmentsView(attachments); document.getElementById('viewAttachments').disabled = !attachments; + + if (attachments && + self.preferenceSidebarViewOnLoad === SidebarView.ATTACHMENTS) { + self.switchSidebarView('attachments', true); + } }); }); + if (self.preferenceSidebarViewOnLoad === SidebarView.THUMBS) { + Promise.all([firstPagePromise, onePageRendered]).then(function () { + self.switchSidebarView('thumbs', true); + }); + } + pdfDocument.getMetadata().then(function(data) { var info = data.info, metadata = data.metadata; self.documentInfo = info; self.metadata = metadata; // Provides some basic debug information console.log('PDF ' + pdfDocument.fingerprint + ' [' + info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() + @@ -3557,38 +3587,36 @@ var PDFView = { } if (dest) { var currentPage = this.pages[(pageNumber || this.page) - 1]; currentPage.scrollIntoView(dest); } else if (pageNumber) { this.page = pageNumber; // simple page } if ('pagemode' in params) { - var toggle = document.getElementById('sidebarToggle'); if (params.pagemode === 'thumbs' || params.pagemode === 'bookmarks' || params.pagemode === 'attachments') { - if (!this.sidebarOpen) { - toggle.click(); - } - this.switchSidebarView(params.pagemode === 'bookmarks' ? - 'outline' : - params.pagemode); + this.switchSidebarView((params.pagemode === 'bookmarks' ? + 'outline' : params.pagemode), true); } else if (params.pagemode === 'none' && this.sidebarOpen) { - toggle.click(); + document.getElementById('sidebarToggle').click(); } } } else if (/^\d+$/.test(hash)) { // page number this.page = hash; } else { // named destination PDFHistory.updateNextHashParam(unescape(hash)); PDFView.navigateTo(unescape(hash)); } }, - switchSidebarView: function pdfViewSwitchSidebarView(view) { + switchSidebarView: function pdfViewSwitchSidebarView(view, openSidebar) { + if (openSidebar && !this.sidebarOpen) { + document.getElementById('sidebarToggle').click(); + } var thumbsView = document.getElementById('thumbnailView'); var outlineView = document.getElementById('outlineView'); var attachmentsView = document.getElementById('attachmentsView'); var thumbsButton = document.getElementById('viewThumbnail'); var outlineButton = document.getElementById('viewOutline'); var attachmentsButton = document.getElementById('viewAttachments'); @@ -5064,17 +5092,16 @@ var DocumentOutlineView = function docum function bindItemLink(domObj, item) { domObj.href = PDFView.getDestinationHash(item.dest); domObj.onclick = function documentOutlineViewOnclick(e) { PDFView.navigateTo(item.dest); return false; }; } - var queue = [{parent: outlineView, items: outline}]; while (queue.length > 0) { var levelData = queue.shift(); var i, n = levelData.items.length; for (i = 0; i < n; i++) { var item = levelData.items[i]; var div = document.createElement('div'); div.className = 'outlineItem'; @@ -5090,55 +5117,56 @@ var DocumentOutlineView = function docum queue.push({parent: itemsDiv, items: item.items}); } levelData.parent.appendChild(div); } } }; + var DocumentAttachmentsView = function documentAttachmentsView(attachments) { var attachmentsView = document.getElementById('attachmentsView'); while (attachmentsView.firstChild) { attachmentsView.removeChild(attachmentsView.firstChild); } if (!attachments) { if (!attachmentsView.classList.contains('hidden')) { PDFView.switchSidebarView('thumbs'); } return; } function bindItemLink(domObj, item) { - domObj.href = '#'; domObj.onclick = function documentAttachmentsViewOnclick(e) { var downloadManager = new DownloadManager(); downloadManager.downloadData(item.content, getFileName(item.filename), ''); return false; }; } var names = Object.keys(attachments).sort(function(a,b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); for (var i = 0, ii = names.length; i < ii; i++) { var item = attachments[names[i]]; var div = document.createElement('div'); div.className = 'attachmentsItem'; - var a = document.createElement('a'); - bindItemLink(a, item); - a.textContent = getFileName(item.filename); - div.appendChild(a); + var button = document.createElement('button'); + bindItemLink(button, item); + button.textContent = getFileName(item.filename); + div.appendChild(button); attachmentsView.appendChild(div); } }; + function webViewerLoad(evt) { PDFView.initialize().then(webViewerInitialized); } function webViewerInitialized() { var file = window.location.href.split('#')[0]; document.getElementById('openFile').setAttribute('hidden', 'true'); @@ -5390,23 +5418,24 @@ function updateViewarea() { pdfOpenParams += ',' + intLeft + ',' + intTop; if (PresentationMode.active || PresentationMode.switchInProgress) { PDFView.currentPosition = null; } else { PDFView.currentPosition = { page: pageNumber, left: intLeft, top: intTop }; } - var store = PDFView.store; - store.initializedPromise.then(function() { - store.set('exists', true); - store.set('page', pageNumber); - store.set('zoom', normalizedScaleValue); - store.set('scrollLeft', intLeft); - store.set('scrollTop', intTop); + PDFView.store.initializedPromise.then(function() { + PDFView.store.setMultiple({ + 'exists': true, + 'page': pageNumber, + 'zoom': normalizedScaleValue, + 'scrollLeft': intLeft, + 'scrollTop': intTop + }); }); var href = PDFView.getAnchorUrl(pdfOpenParams); document.getElementById('viewBookmark').href = href; document.getElementById('secondaryViewBookmark').href = href; // Update the current bookmark in the browsing history. PDFHistory.updateCurrentBookmark(pdfOpenParams, pageNumber); }
--- a/browser/extensions/pdfjs/test/browser_pdfjs_main.js +++ b/browser/extensions/pdfjs/test/browser_pdfjs_main.js @@ -27,31 +27,36 @@ function test() { newTabBrowser.addEventListener("load", function eventHandler() { newTabBrowser.removeEventListener("load", eventHandler, true); var document = newTabBrowser.contentDocument, window = newTabBrowser.contentWindow; // Runs tests after all 'load' event handlers have fired off window.addEventListener("documentload", function() { - runTests(document, window, finish); + runTests(document, window, tab, finish); }, false, true); }, true); } -function runTests(document, window, callback) { +function runTests(document, window, tab, callback) { // // Overall sanity tests // ok(document.querySelector('div#viewer'), "document content has viewer UI"); ok('PDFJS' in window.wrappedJSObject, "window content has PDFJS object"); // + // Browser Find + // + ok(gBrowser.isFindBarInitialized(tab), "Browser FindBar initialized!"); + + // // Sidebar: open // var sidebar = document.querySelector('button#sidebarToggle'), outerContainer = document.querySelector('div#outerContainer'); sidebar.click(); ok(outerContainer.classList.contains('sidebarOpen'), 'sidebar opens on click');