diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 227c4230b571..b7487637b781 100644 --- 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: 0.8.759 +Current extension version is: 0.8.934 diff --git a/browser/extensions/pdfjs/components/PdfStreamConverter.js b/browser/extensions/pdfjs/components/PdfStreamConverter.js index 2a6fa3415f59..cc3b9aaf4a39 100644 --- a/browser/extensions/pdfjs/components/PdfStreamConverter.js +++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js @@ -377,11 +377,19 @@ ChromeActions.prototype = { break; } }, - fallback: function(url, sendResponse) { + fallback: function(args, sendResponse) { + var featureId = args.featureId; + var url = args.url; + var self = this; var domWindow = this.domWindow; var strings = getLocalizedStrings('chrome.properties'); - var message = getLocalizedString(strings, 'unsupported_feature'); + var message; + if (featureId === 'forms') { + message = getLocalizedString(strings, 'unsupported_feature_forms'); + } else { + message = getLocalizedString(strings, 'unsupported_feature'); + } PdfJsTelemetry.onFallback(); diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 55240dd50390..3e7c0bb91064 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.759'; -PDFJS.build = 'd3b5aa3'; +PDFJS.version = '0.8.934'; +PDFJS.build = 'c80df60'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -43,7 +43,8 @@ PDFJS.build = 'd3b5aa3'; * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL */ +/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL, + Promise */ 'use strict'; @@ -51,9 +52,6 @@ var globalScope = (typeof window === 'undefined') ? this : window; var isWorker = (typeof window == 'undefined'); -var ERRORS = 0, WARNINGS = 1, INFOS = 5; -var verbosity = WARNINGS; - var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; var TextRenderingMode = { @@ -78,6 +76,12 @@ if (!globalScope.PDFJS) { globalScope.PDFJS.pdfBug = false; +PDFJS.VERBOSITY_LEVELS = { + errors: 0, + warnings: 1, + infos: 5 +}; + // All the possible operations for an operator list. var OPS = PDFJS.OPS = { // Intentionally start from 1 so it is easy to spot bad operators that will be @@ -171,32 +175,19 @@ var OPS = PDFJS.OPS = { paintInlineImageXObjectGroup: 87 }; -// Use only for debugging purposes. This should not be used in any code that is -// in mozilla master. -var log = (function() { - if ('console' in globalScope && 'log' in globalScope['console']) { - return globalScope['console']['log'].bind(globalScope['console']); - } else { - return function nop() { - }; - } -})(); - -// A notice for devs that will not trigger the fallback UI. These are good -// for things that are helpful to devs, such as warning that Workers were -// disabled, which is important to devs but not end users. +// A notice for devs. These are good for things that are helpful to devs, such +// as warning that Workers were disabled, which is important to devs but not +// end users. function info(msg) { - if (verbosity >= INFOS) { - log('Info: ' + msg); - PDFJS.LogManager.notify('info', msg); + if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) { + console.log('Info: ' + msg); } } -// Non-fatal warnings that should trigger the fallback UI. +// Non-fatal warnings. function warn(msg) { - if (verbosity >= WARNINGS) { - log('Warning: ' + msg); - PDFJS.LogManager.notify('warn', msg); + if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) { + console.log('Warning: ' + msg); } } @@ -207,22 +198,17 @@ function error(msg) { if (arguments.length > 1) { var logArguments = ['Error:']; logArguments.push.apply(logArguments, arguments); - log.apply(null, logArguments); + console.log.apply(console, logArguments); // Join the arguments into a single string for the lines below. msg = [].join.call(arguments, ' '); } else { - log('Error: ' + msg); + console.log('Error: ' + msg); } - log(backtrace()); - PDFJS.LogManager.notify('error', msg); + console.log(backtrace()); + UnsupportedManager.notify(UNSUPPORTED_FEATURES.unknown); throw new Error(msg); } -// Missing features that should trigger the fallback UI. -function TODO(what) { - warn('TODO: ' + what); -} - function backtrace() { try { throw new Error(); @@ -236,12 +222,37 @@ function assert(cond, msg) { error(msg); } +var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = { + unknown: 'unknown', + forms: 'forms', + javaScript: 'javaScript', + smask: 'smask', + shadingPattern: 'shadingPattern', + font: 'font' +}; + +var UnsupportedManager = PDFJS.UnsupportedManager = + (function UnsupportedManagerClosure() { + var listeners = []; + return { + listen: function (cb) { + listeners.push(cb); + }, + notify: function (featureId) { + warn('Unsupported feature "' + featureId + '"'); + for (var i = 0, ii = listeners.length; i < ii; i++) { + listeners[i](featureId); + } + } + }; +})(); + // Combines two URLs. The baseUrl shall be absolute URL. If the url is an // absolute URL, it will be returned as is. function combineUrl(baseUrl, url) { if (!url) return baseUrl; - if (url.indexOf(':') >= 0) + if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) return url; if (url.charAt(0) == '/') { // absolute path @@ -265,11 +276,13 @@ function isValidUrl(url, allowRelative) { if (!url) { return false; } - var colon = url.indexOf(':'); - if (colon < 0) { + // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) + // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); + if (!protocol) { return allowRelative; } - var protocol = url.substr(0, colon); + protocol = protocol[0].toLowerCase(); switch (protocol) { case 'http': case 'https': @@ -289,22 +302,6 @@ function assertWellFormed(cond, msg) { error(msg); } -var LogManager = PDFJS.LogManager = (function LogManagerClosure() { - var loggers = []; - return { - addLogger: function logManager_addLogger(logger) { - loggers.push(logger); - }, - notify: function(type, message) { - for (var i = 0, ii = loggers.length; i < ii; i++) { - var logger = loggers[i]; - if (logger[type]) - logger[type](message); - } - } - }; -})(); - function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, enumerable: true, @@ -834,6 +831,24 @@ function isPDFFunction(v) { } /** + * Legacy support for PDFJS Promise implementation. + * TODO remove eventually + */ +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; + }; +})(); + +/** + * 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. @@ -842,229 +857,40 @@ function isPDFFunction(v) { * Based off of the work in: * https://bugzilla.mozilla.org/show_bug.cgi?id=810490 */ -var Promise = PDFJS.Promise = (function PromiseClosure() { - var STATUS_PENDING = 0; - var STATUS_RESOLVED = 1; - var STATUS_REJECTED = 2; - - // In an attempt to avoid silent exceptions, unhandled rejections are - // tracked and if they aren't handled in a certain amount of time an - // error is logged. - var REJECTION_TIMEOUT = 500; - - var HandlerManager = { - handlers: [], - running: false, - unhandledRejections: [], - pendingRejectionCheck: false, - - scheduleHandlers: function scheduleHandlers(promise) { - if (promise._status == STATUS_PENDING) { - return; - } - - this.handlers = this.handlers.concat(promise._handlers); - promise._handlers = []; - - if (this.running) { - return; - } - this.running = true; - - setTimeout(this.runHandlers.bind(this), 0); - }, - - runHandlers: function runHandlers() { - while (this.handlers.length > 0) { - var handler = this.handlers.shift(); - - var nextStatus = handler.thisPromise._status; - var nextValue = handler.thisPromise._value; - - try { - if (nextStatus === STATUS_RESOLVED) { - if (typeof(handler.onResolve) == 'function') { - nextValue = handler.onResolve(nextValue); +(function PromiseClosure() { + if (globalScope.Promise) { + // Promises existing in the DOM/Worker, checking presence of all/resolve + if (typeof globalScope.Promise.all !== 'function') { + globalScope.Promise.all = function (iterable) { + var count = 0, results = [], resolve, reject; + var promise = new globalScope.Promise(function (resolve_, reject_) { + resolve = resolve_; + reject = reject_; + }); + iterable.forEach(function (p, i) { + count++; + p.then(function (result) { + results[i] = result; + count--; + if (count === 0) { + resolve(results); } - } else if (typeof(handler.onReject) === 'function') { - nextValue = handler.onReject(nextValue); - nextStatus = STATUS_RESOLVED; - - if (handler.thisPromise._unhandledRejection) { - this.removeUnhandeledRejection(handler.thisPromise); - } - } - } catch (ex) { - nextStatus = STATUS_REJECTED; - nextValue = ex; + }, reject); + }); + if (count === 0) { + resolve(results); } - - handler.nextPromise._updateStatus(nextStatus, nextValue); - } - - this.running = false; - }, - - addUnhandledRejection: function addUnhandledRejection(promise) { - this.unhandledRejections.push({ - promise: promise, - time: Date.now() - }); - this.scheduleRejectionCheck(); - }, - - removeUnhandeledRejection: function removeUnhandeledRejection(promise) { - promise._unhandledRejection = false; - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (this.unhandledRejections[i].promise === promise) { - this.unhandledRejections.splice(i); - i--; - } - } - }, - - scheduleRejectionCheck: function scheduleRejectionCheck() { - if (this.pendingRejectionCheck) { - return; - } - this.pendingRejectionCheck = true; - setTimeout(function rejectionCheck() { - this.pendingRejectionCheck = false; - var now = Date.now(); - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { - var unhandled = this.unhandledRejections[i].promise._value; - var msg = 'Unhandled rejection: ' + unhandled; - if (unhandled.stack) { - msg += '\n' + unhandled.stack; - } - warn(msg); - this.unhandledRejections.splice(i); - i--; - } - } - if (this.unhandledRejections.length) { - this.scheduleRejectionCheck(); - } - }.bind(this), REJECTION_TIMEOUT); + return promise; + }; } - }; - - function Promise() { - this._status = STATUS_PENDING; - this._handlers = []; + if (typeof globalScope.Promise.resolve !== 'function') { + globalScope.Promise.resolve = function (x) { + return new globalScope.Promise(function (resolve) { resolve(x); }); + }; + } + return; } - /** - * Builds a promise that is resolved when all the passed in promises are - * resolved. - * @param {array} array of data and/or promises to wait for. - * @return {Promise} New dependant promise. - */ - Promise.all = function Promise_all(promises) { - var deferred = new Promise(); - var unresolved = promises.length; - var results = []; - if (unresolved === 0) { - deferred.resolve(results); - return deferred; - } - function reject(reason) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results = []; - deferred.reject(reason); - } - for (var i = 0, ii = promises.length; i < ii; ++i) { - var promise = promises[i]; - var resolve = (function(i) { - return function(value) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results[i] = value; - unresolved--; - if (unresolved === 0) - deferred.resolve(results); - }; - })(i); - if (Promise.isPromise(promise)) { - promise.then(resolve, reject); - } else { - resolve(promise); - } - } - return deferred; - }; - - /** - * Checks if the value is likely a promise (has a 'then' function). - * @return {boolean} true if x is thenable - */ - Promise.isPromise = function Promise_isPromise(value) { - return value && typeof value.then === 'function'; - }; - - Promise.prototype = { - _status: null, - _value: null, - _handlers: null, - _unhandledRejection: null, - - _updateStatus: function Promise__updateStatus(status, value) { - if (this._status === STATUS_RESOLVED || - this._status === STATUS_REJECTED) { - return; - } - - if (status == STATUS_RESOLVED && - Promise.isPromise(value)) { - value.then(this._updateStatus.bind(this, STATUS_RESOLVED), - this._updateStatus.bind(this, STATUS_REJECTED)); - return; - } - - this._status = status; - this._value = value; - - if (status === STATUS_REJECTED && this._handlers.length === 0) { - this._unhandledRejection = true; - HandlerManager.addUnhandledRejection(this); - } - - HandlerManager.scheduleHandlers(this); - }, - - get isResolved() { - return this._status === STATUS_RESOLVED; - }, - - get isRejected() { - return this._status === STATUS_REJECTED; - }, - - resolve: function Promise_resolve(value) { - this._updateStatus(STATUS_RESOLVED, value); - }, - - reject: function Promise_reject(reason) { - this._updateStatus(STATUS_REJECTED, reason); - }, - - then: function Promise_then(onResolve, onReject) { - var nextPromise = new Promise(); - this._handlers.push({ - thisPromise: this, - onResolve: onResolve, - onReject: onReject, - nextPromise: nextPromise - }); - HandlerManager.scheduleHandlers(this); - return nextPromise; - } - }; - - return Promise; + throw new Error('DOM Promise is not present'); })(); var StatTimer = (function StatTimerClosure() { @@ -1130,18 +956,17 @@ PDFJS.createBlob = function createBlob(data, contentType) { }; PDFJS.createObjectURL = (function createObjectURLClosure() { - if (typeof URL !== 'undefined' && URL.createObjectURL) { - return function createObjectURL(data, contentType) { - var blob = PDFJS.createBlob(data, contentType); - return URL.createObjectURL(blob); - }; - } - // Blob/createObjectURL is not available, falling back to data schema. var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; return function createObjectURL(data, contentType) { + if (!PDFJS.disableCreateObjectURL && + typeof URL !== 'undefined' && URL.createObjectURL) { + var blob = PDFJS.createBlob(data, contentType); + return URL.createObjectURL(blob); + } + var buffer = 'data:' + contentType + ';base64,'; for (var i = 0, ii = data.length; i < ii; i += 3) { var b1 = data[i] & 0xFF; @@ -1165,21 +990,13 @@ function MessageHandler(name, comObj) { var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { - log.apply(null, data); + console.log.apply(console, data); }]; - // If there's no console available, console_error in the - // action handler will do nothing. - if ('console' in globalScope) { - ah['console_error'] = [function ahConsoleError(data) { - globalScope['console'].error.apply(null, data); - }]; - } else { - ah['console_error'] = [function ahConsoleError(data) { - log.apply(null, data); - }]; - } - ah['_warn'] = [function ah_Warn(data) { - warn(data); + ah['console_error'] = [function ahConsoleError(data) { + console.error.apply(console, data); + }]; + ah['_unsupported_feature'] = [function ah_unsupportedFeature(data) { + UnsupportedManager.notify(data); }]; comObj.onmessage = function messageHandlerComObjOnMessage(event) { @@ -1196,7 +1013,12 @@ function MessageHandler(name, comObj) { } else if (data.action in ah) { var action = ah[data.action]; if (data.callbackId) { - var promise = new Promise(); + var deferred = {}; + var promise = new Promise(function (resolve, reject) { + deferred.resolve = resolve; + deferred.reject = reject; + }); + deferred.promise = promise; promise.then(function(resolvedData) { comObj.postMessage({ isReply: true, @@ -1204,7 +1026,7 @@ function MessageHandler(name, comObj) { data: resolvedData }); }); - action[0].call(action[1], data.data, promise); + action[0].call(action[1], data.data, deferred); } else { action[0].call(action[1], data.data); } @@ -1283,17 +1105,22 @@ var ColorSpace = (function ColorSpaceClosure() { * The colors are located in the src array starting from the srcOffset. * The result is placed into the dest array starting from the destOffset. * The src array items shall be in [0,2^bits) range, the dest array items - * will be in [0,255] range. + * will be in [0,255] range. alpha01 indicates how many alpha components + * there are in the dest array; it will be either 0 (RGB array) or 1 (RGBA + * array). */ getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { + dest, destOffset, bits, + alpha01) { error('Should not call ColorSpace.getRgbBuffer'); }, /** - * Determines amount of the bytes is required to store the reslut of the - * conversion that done by the getRgbBuffer method. + * Determines the number of bytes required to store the result of the + * conversion done by the getRgbBuffer method. As in getRgbBuffer, + * |alpha01| is either 0 (RGB output) or 1 (RGBA output). */ - getOutputLength: function ColorSpace_getOutputLength(inputLength) { + getOutputLength: function ColorSpace_getOutputLength(inputLength, + alpha01) { error('Should not call ColorSpace.getOutputLength'); }, /** @@ -1303,45 +1130,84 @@ var ColorSpace = (function ColorSpaceClosure() { return false; }, /** - * Creates the output buffer and converts the specified number of the color - * values to the RGB colors, similar to the getRgbBuffer. + * Fills in the RGB colors in an RGBA buffer. */ - createRgbBuffer: function ColorSpace_createRgbBuffer(src, srcOffset, - count, bits) { - if (this.isPassthrough(bits)) { - return src.subarray(srcOffset); - } - var dest = new Uint8Array(count * 3); - var numComponentColors = 1 << bits; - // Optimization: create a color map when there is just one component and - // we are converting more colors than the size of the color map. We - // don't build the map if the colorspace is gray or rgb since those - // methods are faster than building a map. This mainly offers big speed - // ups for indexed and alternate colorspaces. - if (this.numComps === 1 && count > numComponentColors && + fillRgb: function ColorSpace_fillRgb(rgbaBuf, originalWidth, + originalHeight, width, height, + actualHeight, bpc, comps) { + var count = originalWidth * originalHeight; + var rgbBuf = null; + var numComponentColors = 1 << bpc; + var needsResizing = originalHeight != height || originalWidth != width; + + if (this.isPassthrough(bpc)) { + rgbBuf = comps; + + } else if (this.numComps === 1 && count > numComponentColors && this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') { + // Optimization: create a color map when there is just one component and + // we are converting more colors than the size of the color map. We + // don't build the map if the colorspace is gray or rgb since those + // methods are faster than building a map. This mainly offers big speed + // ups for indexed and alternate colorspaces. + // // TODO it may be worth while to cache the color map. While running // testing I never hit a cache so I will leave that out for now (perhaps // we are reparsing colorspaces too much?). - var allColors = bits <= 8 ? new Uint8Array(numComponentColors) : - new Uint16Array(numComponentColors); + var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : + new Uint16Array(numComponentColors); for (var i = 0; i < numComponentColors; i++) { allColors[i] = i; } var colorMap = new Uint8Array(numComponentColors * 3); - this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bits); + this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, + /* alpha01 = */ 0); - var destOffset = 0; - for (var i = 0; i < count; ++i) { - var key = src[srcOffset++] * 3; - dest[destOffset++] = colorMap[key]; - dest[destOffset++] = colorMap[key + 1]; - dest[destOffset++] = colorMap[key + 2]; + if (!needsResizing) { + // Fill in the RGB values directly into |rgbaBuf|. + var rgbaPos = 0; + for (var i = 0; i < count; ++i) { + var key = comps[i] * 3; + rgbaBuf[rgbaPos++] = colorMap[key]; + rgbaBuf[rgbaPos++] = colorMap[key + 1]; + rgbaBuf[rgbaPos++] = colorMap[key + 2]; + rgbaPos++; + } + } else { + rgbBuf = new Uint8Array(count * 3); + var rgbPos = 0; + for (var i = 0; i < count; ++i) { + var key = comps[i] * 3; + rgbBuf[rgbPos++] = colorMap[key]; + rgbBuf[rgbPos++] = colorMap[key + 1]; + rgbBuf[rgbPos++] = colorMap[key + 2]; + } + } + } else { + if (!needsResizing) { + // Fill in the RGB values directly into |rgbaBuf|. + this.getRgbBuffer(comps, 0, width * actualHeight, rgbaBuf, 0, bpc, + /* alpha01 = */ 1); + } else { + 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); + } + var rgbPos = 0; + var actualLength = width * actualHeight * 4; + for (var i = 0; i < actualLength; i += 4) { + rgbaBuf[i] = rgbBuf[rgbPos++]; + rgbaBuf[i + 1] = rgbBuf[rgbPos++]; + rgbaBuf[i + 2] = rgbBuf[rgbPos++]; } - return dest; } - this.getRgbBuffer(src, srcOffset, count, dest, 0, bits); - return dest; }, /** * True if the colorspace has components in the default range of [0, 1]. @@ -1573,13 +1439,15 @@ var AlternateCS = (function AlternateCSClosure() { this.base.getRgbItem(tinted, 0, dest, destOffset); }, getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { + dest, destOffset, bits, + alpha01) { var tintFn = this.tintFn; var base = this.base; var scale = 1 / ((1 << bits) - 1); var baseNumComps = base.numComps; var usesZeroToOneRange = base.usesZeroToOneRange; - var isPassthrough = base.isPassthrough(8) || !usesZeroToOneRange; + var isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) && + alpha01 === 0; var pos = isPassthrough ? destOffset : 0; var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count); var numComps = this.numComps; @@ -1600,15 +1468,17 @@ var AlternateCS = (function AlternateCSClosure() { } } if (!isPassthrough) { - base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8); + base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01); } }, - getOutputLength: function AlternateCS_getOutputLength(inputLength) { + getOutputLength: function AlternateCS_getOutputLength(inputLength, + alpha01) { return this.base.getOutputLength(inputLength * - this.base.numComps / this.numComps); + this.base.numComps / this.numComps, + alpha01); }, isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, + fillRgb: ColorSpace.prototype.fillRgb, isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, @@ -1669,23 +1539,25 @@ var IndexedCS = (function IndexedCSClosure() { this.base.getRgbItem(this.lookup, start, dest, destOffset); }, getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset) { + dest, destOffset, bits, + alpha01) { var base = this.base; var numComps = base.numComps; - var outputDelta = base.getOutputLength(numComps); + var outputDelta = base.getOutputLength(numComps, alpha01); var lookup = this.lookup; for (var i = 0; i < count; ++i) { var lookupPos = src[srcOffset++] * numComps; - base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8); + base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01); destOffset += outputDelta; } }, - getOutputLength: function IndexedCS_getOutputLength(inputLength) { - return this.base.getOutputLength(inputLength * this.base.numComps); + getOutputLength: function IndexedCS_getOutputLength(inputLength, alpha01) { + return this.base.getOutputLength(inputLength * this.base.numComps, + alpha01); }, isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, + fillRgb: ColorSpace.prototype.fillRgb, isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) { // indexed color maps shouldn't be changed return true; @@ -1715,7 +1587,8 @@ var DeviceGrayCS = (function DeviceGrayCSClosure() { dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c; }, getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { + dest, destOffset, bits, + alpha01) { var scale = 255 / ((1 << bits) - 1); var j = srcOffset, q = destOffset; for (var i = 0; i < count; ++i) { @@ -1723,13 +1596,15 @@ var DeviceGrayCS = (function DeviceGrayCSClosure() { dest[q++] = c; dest[q++] = c; dest[q++] = c; + q += alpha01; } }, - getOutputLength: function DeviceGrayCS_getOutputLength(inputLength) { - return inputLength * 3; + getOutputLength: function DeviceGrayCS_getOutputLength(inputLength, + alpha01) { + return inputLength * (3 + alpha01); }, isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, + fillRgb: ColorSpace.prototype.fillRgb, isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, @@ -1760,25 +1635,29 @@ var DeviceRgbCS = (function DeviceRgbCSClosure() { dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b; }, getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - var length = count * 3; - if (bits == 8) { - dest.set(src.subarray(srcOffset, srcOffset + length), destOffset); + dest, destOffset, bits, + alpha01) { + if (bits === 8 && alpha01 === 0) { + dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset); return; } var scale = 255 / ((1 << bits) - 1); var j = srcOffset, q = destOffset; - for (var i = 0; i < length; ++i) { + for (var i = 0; i < count; ++i) { dest[q++] = (scale * src[j++]) | 0; + dest[q++] = (scale * src[j++]) | 0; + dest[q++] = (scale * src[j++]) | 0; + q += alpha01; } }, - getOutputLength: function DeviceRgbCS_getOutputLength(inputLength) { - return inputLength; + getOutputLength: function DeviceRgbCS_getOutputLength(inputLength, + alpha01) { + return (inputLength * (3 + alpha01) / 3) | 0; }, isPassthrough: function DeviceRgbCS_isPassthrough(bits) { return bits == 8; }, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, + fillRgb: ColorSpace.prototype.fillRgb, isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, @@ -1848,19 +1727,21 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { convertToRgb(src, srcOffset, 1, dest, destOffset); }, getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { + dest, destOffset, bits, + alpha01) { var scale = 1 / ((1 << bits) - 1); for (var i = 0; i < count; i++) { convertToRgb(src, srcOffset, scale, dest, destOffset); srcOffset += 4; - destOffset += 3; + destOffset += 3 + alpha01; } }, - getOutputLength: function DeviceCmykCS_getOutputLength(inputLength) { - return (inputLength >> 2) * 3; + getOutputLength: function DeviceCmykCS_getOutputLength(inputLength, + alpha01) { + return (inputLength / 4 * (3 + alpha01)) | 0; }, isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, + fillRgb: ColorSpace.prototype.fillRgb, isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, @@ -1876,8 +1757,8 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { var CalGrayCS = (function CalGrayCSClosure() { function CalGrayCS(whitePoint, blackPoint, gamma) { this.name = 'CalGray'; - this.numComps = 3; - this.defaultColor = new Float32Array([0, 0, 0]); + this.numComps = 1; + this.defaultColor = new Float32Array([0]); if (!whitePoint) { error('WhitePoint missing - required for color space CalGray'); @@ -1908,7 +1789,7 @@ var CalGrayCS = (function CalGrayCSClosure() { } if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) { - TODO(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB + + warn(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB + ', ZB: ' + this.ZB + ', only default values are supported.'); } @@ -1919,6 +1800,33 @@ var CalGrayCS = (function CalGrayCSClosure() { } } + 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. + // 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; + } + CalGrayCS.prototype = { getRgb: function CalGrayCS_getRgb(src, srcOffset) { var rgb = new Uint8Array(3); @@ -1927,48 +1835,24 @@ var CalGrayCS = (function CalGrayCSClosure() { }, getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, dest, destOffset) { - // A represents a gray component of a calibrated gray space. - // A <---> AG in the spec - var A = src[srcOffset]; - var AG = Math.pow(A, this.G); - - // Computes intermediate variables M, L, N as per spec. - // Except if other than default BlackPoint values are used. - var M = this.XW * AG; - var L = this.YW * AG; - var N = this.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; + convertToRgb(this, src, srcOffset, dest, destOffset, 1); }, getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - // TODO: This part is copied from DeviceGray. Make this utility function. - var scale = 255 / ((1 << bits) - 1); - var j = srcOffset, q = destOffset; + dest, destOffset, bits, + alpha01) { + var scale = 1 / ((1 << bits) - 1); + for (var i = 0; i < count; ++i) { - var c = (scale * src[j++]) | 0; - dest[q++] = c; - dest[q++] = c; - dest[q++] = c; + convertToRgb(this, src, srcOffset, dest, destOffset, scale); + srcOffset += 1; + destOffset += 3 + alpha01; } }, - getOutputLength: function CalGrayCS_getOutputLength(inputLength) { - return inputLength * 3; + getOutputLength: function CalGrayCS_getOutputLength(inputLength, alpha01) { + return inputLength * (3 + alpha01); }, isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, + fillRgb: ColorSpace.prototype.fillRgb, isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) { return ColorSpace.isDefaultDecode(decodeMap, this.numComps); }, @@ -2096,16 +1980,17 @@ var LabCS = (function LabCSClosure() { convertToRgb(this, src, srcOffset, false, dest, destOffset); }, getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { + dest, destOffset, bits, + alpha01) { var maxVal = (1 << bits) - 1; for (var i = 0; i < count; i++) { convertToRgb(this, src, srcOffset, maxVal, dest, destOffset); srcOffset += 3; - destOffset += 3; + destOffset += 3 + alpha01; } }, - getOutputLength: function LabCS_getOutputLength(inputLength) { - return inputLength; + getOutputLength: function LabCS_getOutputLength(inputLength, alpha01) { + return (inputLength * (3 + alpha01) / 3) | 0; }, isPassthrough: ColorSpace.prototype.isPassthrough, isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) { @@ -2155,7 +2040,7 @@ var Pattern = (function PatternClosure() { // Both radial and axial shadings are handled by RadialAxial shading. return new Shadings.RadialAxial(dict, matrix, xref, res); default: - TODO('Unsupported shading type: ' + type); + UnsupportedManager.notify(UNSUPPORTED_FEATURES.shadingPattern); return new Shadings.Dummy(); } }; @@ -2415,7 +2300,7 @@ var TilingPattern = (function TilingPatternClosure() { var commonObjs = this.commonObjs; var ctx = this.ctx; - TODO('TilingType: ' + tilingType); + info('TilingType: ' + tilingType); var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; @@ -2628,7 +2513,7 @@ var PDFFunction = (function PDFFunctionClosure() { if (order !== 1) { // No description how cubic spline interpolation works in PDF32000:2008 // As in poppler, ignoring order, linear interpolation may work as good - TODO('No support for cubic spline interpolation: ' + order); + info('No support for cubic spline interpolation: ' + order); } var encode = dict.get('Encode'); @@ -3477,6 +3362,29 @@ var Annotation = (function AnnotationClosure() { } else { var borderArray = dict.get('Border') || [0, 0, 1]; data.borderWidth = borderArray[2] || 0; + + // TODO: implement proper support for annotations with line dash patterns. + var dashArray = borderArray[3]; + if (data.borderWidth > 0 && dashArray && isArray(dashArray)) { + var dashArrayLength = dashArray.length; + if (dashArrayLength > 0) { + // According to the PDF specification: the elements in a dashArray + // shall be numbers that are nonnegative and not all equal to zero. + var isInvalid = false; + var numPositive = 0; + for (var i = 0; i < dashArrayLength; i++) { + if (!(+dashArray[i] >= 0)) { + isInvalid = true; + break; + } else if (dashArray[i] > 0) { + numPositive++; + } + } + if (isInvalid || numPositive === 0) { + data.borderWidth = 0; + } + } + } } this.appearance = getDefaultAppearance(dict); @@ -3521,7 +3429,7 @@ var Annotation = (function AnnotationClosure() { }, loadResources: function(keys) { - var promise = new Promise(); + var promise = new LegacyPromise(); this.appearance.dict.getAsync('Resources').then(function(resources) { if (!resources) { promise.resolve(); @@ -3540,7 +3448,7 @@ var Annotation = (function AnnotationClosure() { getOperatorList: function Annotation_getToOperatorList(evaluator) { - var promise = new Promise(); + var promise = new LegacyPromise(); if (!this.appearance) { promise.resolve(new OperatorList()); @@ -3646,7 +3554,7 @@ var Annotation = (function AnnotationClosure() { if (annotation.isViewable()) { return annotation; } else { - TODO('unimplemented annotation type: ' + subtype); + warn('unimplemented annotation type: ' + subtype); } }; @@ -3657,7 +3565,7 @@ var Annotation = (function AnnotationClosure() { annotationsReadyPromise.reject(e); } - var annotationsReadyPromise = new Promise(); + var annotationsReadyPromise = new LegacyPromise(); var annotationPromises = []; for (var i = 0, n = annotations.length; i < n; ++i) { @@ -3738,7 +3646,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { Util.inherit(WidgetAnnotation, Annotation, { isViewable: function WidgetAnnotation_isViewable() { if (this.data.fieldType === 'Sig') { - TODO('unimplemented annotation type: Widget signature'); + warn('unimplemented annotation type: Widget signature'); return false; } @@ -3819,7 +3727,7 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { return Annotation.prototype.getOperatorList.call(this, evaluator); } - var promise = new Promise(); + var promise = new LegacyPromise(); var opList = new OperatorList(); var data = this.data; @@ -3896,7 +3804,7 @@ var TextAnnotation = (function TextAnnotationClosure() { Util.inherit(TextAnnotation, Annotation, { getOperatorList: function TextAnnotation_getOperatorList(evaluator) { - var promise = new Promise(); + var promise = new LegacyPromise(); promise.resolve(new OperatorList()); return promise; }, @@ -3996,7 +3904,13 @@ var LinkAnnotation = (function LinkAnnotationClosure() { if (action) { var linkType = action.get('S').name; if (linkType === 'URI') { - var url = addDefaultProtocolToUrl(action.get('URI')); + var url = action.get('URI'); + if (isName(url)) { + // Some bad PDFs do not put parentheses around relative URLs. + url = '/' + url.name; + } else { + url = addDefaultProtocolToUrl(url); + } // TODO: pdf spec mentions urls can be relative to a Base // entry in the dictionary. if (!isValidUrl(url, false)) { @@ -4023,7 +3937,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { } else if (linkType === 'Named') { data.action = action.get('N').name; } else { - TODO('unrecognized link type: ' + linkType); + warn('unrecognized link type: ' + linkType); } } else if (dict.has('Dest')) { // simple destination link @@ -4080,7 +3994,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { /** * The maximum allowed image size in total pixels e.g. width * height. Images * above this value will not be drawn. Use -1 for no limit. - * @var {Number} + * @var {number} */ PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize; @@ -4088,7 +4002,7 @@ PDFJS.maxImageSize = PDFJS.maxImageSize === undefined ? -1 : PDFJS.maxImageSize; * By default fonts are converted to OpenType fonts and loaded via font face * rules. If disabled, the font will be rendered using a built in font renderer * that constructs the glyphs with primitive path commands. - * @var {Boolean} + * @var {boolean} */ PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ? false : PDFJS.disableFontFace; @@ -4096,7 +4010,7 @@ PDFJS.disableFontFace = PDFJS.disableFontFace === undefined ? /** * Path for image resources, mainly for annotation icons. Include trailing * slash. - * @var {String} + * @var {string} */ PDFJS.imageResourcesPath = PDFJS.imageResourcesPath === undefined ? '' : PDFJS.imageResourcesPath; @@ -4105,7 +4019,7 @@ PDFJS.imageResourcesPath = PDFJS.imageResourcesPath === undefined ? * Disable the web worker and run all code on the main thread. This will happen * automatically if the browser doesn't support workers or sending typed arrays * to workers. - * @var {Boolean} + * @var {boolean} */ PDFJS.disableWorker = PDFJS.disableWorker === undefined ? false : PDFJS.disableWorker; @@ -4114,7 +4028,7 @@ PDFJS.disableWorker = PDFJS.disableWorker === undefined ? * Path and filename of the worker file. Required when the worker is enabled in * development mode. If unspecified in the production build, the worker will be * loaded based on the location of the pdf.js file. - * @var {String} + * @var {string} */ PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc; @@ -4122,7 +4036,7 @@ PDFJS.workerSrc = PDFJS.workerSrc === undefined ? null : PDFJS.workerSrc; * Disable range request loading of PDF files. When enabled and if the server * supports partial content requests then the PDF will be fetched in chunks. * Enabled (false) by default. - * @var {Boolean} + * @var {boolean} */ PDFJS.disableRange = PDFJS.disableRange === undefined ? false : PDFJS.disableRange; @@ -4131,14 +4045,14 @@ PDFJS.disableRange = PDFJS.disableRange === undefined ? * Disable pre-fetching of PDF file data. When range requests are enabled PDF.js * will automatically keep fetching more data even if it isn't needed to display * the current page. This default behavior can be disabled. - * @var {Boolean} + * @var {boolean} */ PDFJS.disableAutoFetch = PDFJS.disableAutoFetch === undefined ? false : PDFJS.disableAutoFetch; /** * Enables special hooks for debugging PDF.js. - * @var {Boolean} + * @var {boolean} */ PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug; @@ -4148,24 +4062,52 @@ PDFJS.pdfBug = PDFJS.pdfBug === undefined ? false : PDFJS.pdfBug; */ PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ? true : PDFJS.postMessageTransfers; + +/** + * Disables URL.createObjectURL usage. + * @var {boolean} + */ +PDFJS.disableCreateObjectURL = PDFJS.disableCreateObjectURL === undefined ? + false : PDFJS.disableCreateObjectURL; + +/** + * Controls the logging level. + * The constants from PDFJS.VERBOSITY_LEVELS should be used: + * - errors + * - warnings [default] + * - infos + * @var {number} + */ +PDFJS.verbosity = PDFJS.verbosity === undefined ? + PDFJS.VERBOSITY_LEVELS.warnings : PDFJS.verbosity; + +/** + * Document initialization / loading parameters object. + * + * @typedef {Object} DocumentInitParameters + * @property {string} url - The URL of the PDF. + * @property {TypedArray} data - A typed array with PDF data. + * @property {Object} httpHeaders - Basic authentication headers. + * @property {boolean} withCredentials - Indicates whether or not cross-site + * Access-Control requests should be made using credentials such as cookies + * or authorization headers. The default is false. + * @property {string} password - For decrypting password-protected PDFs. + * @property {TypedArray} initialData - A typed array with the first portion or + * all of the pdf data. Used by the extension since some data is already + * loaded before the switch to range requests. + */ + /** * This is the main entry point for loading a PDF and interacting with it. * NOTE: If a URL is used to fetch the PDF data a standard XMLHttpRequest(XHR) * is used, which means it must follow the same origin rules that any XHR does * e.g. No cross domain requests without CORS. * - * @param {string|TypedAray|object} source Can be an url to where a PDF is - * located, a typed array (Uint8Array) already populated with data or - * and parameter object with the following possible fields: - * - url - The URL of the PDF. - * - data - A typed array with PDF data. - * - httpHeaders - Basic authentication headers. - * - password - For decrypting password-protected PDFs. - * - initialData - A typed array with the first portion or all of the pdf data. - * Used by the extension since some data is already loaded - * before the switch to range requests. + * @param {string|TypedArray|DocumentInitParameters} source Can be a url to + * where a PDF is located, a typed array (Uint8Array) already populated with + * data or parameter object. * - * @param {object} pdfDataRangeTransport is optional. It is used if you want + * @param {Object} pdfDataRangeTransport is optional. It is used if you want * to manually serve range requests for data in the PDF. See viewer.js for * an example of pdfDataRangeTransport's interface. * @@ -4174,7 +4116,8 @@ PDFJS.postMessageTransfers = PDFJS.postMessageTransfers === undefined ? * parameters: function that needs to be called with new password and reason * (see {PasswordResponses}). * - * @return {Promise} A promise that is resolved with {PDFDocumentProxy} object. + * @return {Promise} A promise that is resolved with {@link PDFDocumentProxy} + * object. */ PDFJS.getDocument = function getDocument(source, pdfDataRangeTransport, @@ -4204,8 +4147,8 @@ PDFJS.getDocument = function getDocument(source, params[key] = source[key]; } - workerInitializedPromise = new PDFJS.Promise(); - workerReadyPromise = new PDFJS.Promise(); + workerInitializedPromise = new PDFJS.LegacyPromise(); + workerReadyPromise = new PDFJS.LegacyPromise(); transport = new WorkerTransport(workerInitializedPromise, workerReadyPromise, pdfDataRangeTransport, progressCallback); workerInitializedPromise.then(function transportInitialized() { @@ -4218,13 +4161,14 @@ PDFJS.getDocument = function getDocument(source, /** * Proxy to a PDFDocument in the worker thread. Also, contains commonly used * properties that can be read synchronously. + * @class */ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { function PDFDocumentProxy(pdfInfo, transport) { this.pdfInfo = pdfInfo; this.transport = transport; } - PDFDocumentProxy.prototype = { + PDFDocumentProxy.prototype = /** @lends PDFDocumentProxy.prototype */ { /** * @return {number} Total number of pages the PDF contains. */ @@ -4246,15 +4190,16 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { return this.transport.embeddedFontsUsed; }, /** - * @param {number} The page number to get. The first page is 1. - * @return {Promise} A promise that is resolved with a {PDFPageProxy} + * @param {number} pageNumber The page number to get. The first page is 1. + * @return {Promise} A promise that is resolved with a {@link PDFPageProxy} * object. */ - getPage: function PDFDocumentProxy_getPage(number) { - return this.transport.getPage(number); + getPage: function PDFDocumentProxy_getPage(pageNumber) { + return this.transport.getPage(pageNumber); }, /** - * @param {object} Must have 'num' and 'gen' properties. + * @param {{num: number, gen: number}} ref The page reference. Must have + * the 'num' and 'gen' properties. * @return {Promise} A promise that is resolved with the page index that is * associated with the reference. */ @@ -4272,14 +4217,14 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * @return {Promise} A promise that is resolved with an array of all the * JavaScript strings in the name tree. */ - getJavaScript: function PDFDocumentProxy_getDestinations() { - var promise = new PDFJS.Promise(); + getJavaScript: function PDFDocumentProxy_getJavaScript() { + var promise = new PDFJS.LegacyPromise(); var js = this.pdfInfo.javaScript; promise.resolve(js); return promise; }, /** - * @return {Promise} A promise that is resolved with an {array} that is a + * @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: * [ * { @@ -4294,19 +4239,19 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * ]. */ getOutline: function PDFDocumentProxy_getOutline() { - var promise = new PDFJS.Promise(); + var promise = new PDFJS.LegacyPromise(); var outline = this.pdfInfo.outline; promise.resolve(outline); return promise; }, /** - * @return {Promise} A promise that is resolved with an {object} that has - * info and metadata properties. Info is an {object} filled with anything + * @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.Promise(); + var promise = new PDFJS.LegacyPromise(); var info = this.pdfInfo.info; var metadata = this.pdfInfo.metadata; promise.resolve({ @@ -4315,17 +4260,12 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { }); return promise; }, - isEncrypted: function PDFDocumentProxy_isEncrypted() { - var promise = new PDFJS.Promise(); - promise.resolve(this.pdfInfo.encrypted); - return promise; - }, /** * @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.Promise(); + var promise = new PDFJS.LegacyPromise(); this.transport.getData(promise); return promise; }, @@ -4336,9 +4276,15 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { dataLoaded: function PDFDocumentProxy_dataLoaded() { return this.transport.dataLoaded(); }, + /** + * Cleans up resources allocated by the document, e.g. created @font-face. + */ cleanup: function PDFDocumentProxy_cleanup() { this.transport.startCleanup(); }, + /** + * Destroys current document instance and terminates worker. + */ destroy: function PDFDocumentProxy_destroy() { this.transport.destroy(); } @@ -4346,6 +4292,22 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { return PDFDocumentProxy; })(); +/** + * Page text content part. + * + * @typedef {Object} BidiText + * @property {string} str - text content. + * @property {string} dir - text direction: 'ttb', 'ltr' or 'rtl'. + * @property {number} x - x position of the text on the page. + * @property {number} y - y position of the text on the page. + * @property {number} angle - text rotation. + * @property {number} size - font size. + */ + +/** + * Proxy to a PDFPage in the worker thread. + * @class + */ var PDFPageProxy = (function PDFPageProxyClosure() { function PDFPageProxy(pageInfo, transport) { this.pageInfo = pageInfo; @@ -4359,7 +4321,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { this.pendingDestroy = false; this.renderTasks = []; } - PDFPageProxy.prototype = { + PDFPageProxy.prototype = /** @lends PDFPageProxy.prototype */ { /** * @return {number} Page number of the page. First page is 1. */ @@ -4373,14 +4335,14 @@ var PDFPageProxy = (function PDFPageProxyClosure() { return this.pageInfo.rotate; }, /** - * @return {object} The reference that points to this page. It has 'num' and + * @return {Object} The reference that points to this page. It has 'num' and * 'gen' properties. */ get ref() { return this.pageInfo.ref; }, /** - * @return {array} An array of the visible portion of the PDF page in the + * @return {Array} An array of the visible portion of the PDF page in the * user space units - [x1, y1, x2, y2]. */ get view() { @@ -4399,21 +4361,21 @@ var PDFPageProxy = (function PDFPageProxyClosure() { return new PDFJS.PageViewport(this.view, scale, rotate, 0, 0); }, /** - * @return {Promise} A promise that is resolved with an {array} of the + * @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.Promise(); + var promise = new PDFJS.LegacyPromise(); this.annotationsPromise = promise; this.transport.getAnnotations(this.pageInfo.pageIndex); return promise; }, /** * Begins the process of rendering a page to the desired context. - * @param {object} params A parameter object that supports: + * @param {Object} params A parameter object that supports: * { * canvasContext(required): A 2D context of a DOM Canvas object., * textLayer(optional): An object that has beginLayout, endLayout, and @@ -4440,7 +4402,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { // requested before. Make the request and create the promise. if (!this.displayReadyPromise) { this.receivingOperatorList = true; - this.displayReadyPromise = new Promise(); + this.displayReadyPromise = new LegacyPromise(); this.operatorList = { fnArray: [], argsArray: [], @@ -4487,9 +4449,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() { self._tryDestroy(); if (error) { - renderTask.reject(error); + renderTask.promise.reject(error); } else { - renderTask.resolve(); + renderTask.promise.resolve(); } stats.timeEnd('Rendering'); stats.timeEnd('Overall'); @@ -4498,11 +4460,11 @@ var PDFPageProxy = (function PDFPageProxyClosure() { return renderTask; }, /** - * @return {Promise} That is resolved with the a {string} that is the text - * content from the page. + * @return {Promise} That is resolved with the array of {@link BidiText} + * objects that represent the page text content. */ getTextContent: function PDFPageProxy_getTextContent() { - var promise = new PDFJS.Promise(); + var promise = new PDFJS.LegacyPromise(); this.transport.messageHandler.send('GetTextContent', { pageIndex: this.pageNumber - 1 }, @@ -4512,18 +4474,6 @@ var PDFPageProxy = (function PDFPageProxyClosure() { ); return promise; }, - /** - * Stub for future feature. - */ - getOperationList: function PDFPageProxy_getOperationList() { - var promise = new PDFJS.Promise(); - var operationList = { // not implemented - dependencyFontsID: null, - operatorList: null - }; - promise.resolve(operationList); - return promise; - }, /** * Destroys resources allocated by the page. */ @@ -4534,6 +4484,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { /** * For internal use only. Attempts to clean up if rendering is in a state * where that's possible. + * @ignore */ _tryDestroy: function PDFPageProxy__destroy() { if (!this.pendingDestroy || @@ -4549,12 +4500,14 @@ var PDFPageProxy = (function PDFPageProxyClosure() { }, /** * For internal use only. + * @ignore */ _startRenderPage: function PDFPageProxy_startRenderPage(transparency) { this.displayReadyPromise.resolve(transparency); }, /** * For internal use only. + * @ignore */ _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk) { // Add the new chunk to the current operator list. @@ -4577,8 +4530,10 @@ var PDFPageProxy = (function PDFPageProxyClosure() { }; return PDFPageProxy; })(); + /** * For internal use only. + * @ignore */ var WorkerTransport = (function WorkerTransportClosure() { function WorkerTransport(workerInitializedPromise, workerReadyPromise, @@ -4660,6 +4615,7 @@ var WorkerTransport = (function WorkerTransportClosure() { this.pagePromises = []; var self = this; this.messageHandler.send('Terminate', null, function () { + FontLoader.clear(); if (self.worker) { self.worker.terminate(); } @@ -4668,7 +4624,7 @@ var WorkerTransport = (function WorkerTransportClosure() { loadFakeWorkerFiles: function WorkerTransport_loadFakeWorkerFiles() { if (!PDFJS.fakeWorkerFilesLoadedPromise) { - PDFJS.fakeWorkerFilesLoadedPromise = new Promise(); + PDFJS.fakeWorkerFilesLoadedPromise = new LegacyPromise(); // 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. @@ -4873,7 +4829,7 @@ var WorkerTransport = (function WorkerTransportClosure() { error(data.error); }, this); - messageHandler.on('JpegDecode', function(data, promise) { + messageHandler.on('JpegDecode', function(data, deferred) { var imageUrl = data[0]; var components = data[1]; if (components != 3 && components != 1) @@ -4902,7 +4858,7 @@ var WorkerTransport = (function WorkerTransportClosure() { buf[j] = data[i]; } } - promise.resolve({ data: buf, width: width, height: height}); + deferred.resolve({ data: buf, width: width, height: height}); }).bind(this); img.src = imageUrl; }); @@ -4915,7 +4871,9 @@ var WorkerTransport = (function WorkerTransportClosure() { source: source, disableRange: PDFJS.disableRange, maxImageSize: PDFJS.maxImageSize, - disableFontFace: PDFJS.disableFontFace + disableFontFace: PDFJS.disableFontFace, + disableCreateObjectURL: PDFJS.disableCreateObjectURL, + verbosity: PDFJS.verbosity }); }, @@ -4926,10 +4884,14 @@ var WorkerTransport = (function WorkerTransportClosure() { }, dataLoaded: function WorkerTransport_dataLoaded() { - var promise = new PDFJS.Promise(); + if (this.dataLoadedPromise) { + return this.dataLoadedPromise; + } + var promise = new PDFJS.LegacyPromise(); this.messageHandler.send('DataLoaded', null, function(args) { promise.resolve(args); }); + this.dataLoadedPromise = promise; return promise; }, @@ -4937,14 +4899,14 @@ var WorkerTransport = (function WorkerTransportClosure() { var pageIndex = pageNumber - 1; if (pageIndex in this.pagePromises) return this.pagePromises[pageIndex]; - var promise = new PDFJS.Promise('Page ' + pageNumber); + var promise = new PDFJS.LegacyPromise(); this.pagePromises[pageIndex] = promise; this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex }); return promise; }, getPageIndex: function WorkerTransport_getPageIndexByRef(ref) { - var promise = new PDFJS.Promise(); + var promise = new PDFJS.LegacyPromise(); this.messageHandler.send('GetPageIndex', { ref: ref }, function (pageIndex) { promise.resolve(pageIndex); @@ -4959,7 +4921,7 @@ var WorkerTransport = (function WorkerTransportClosure() { }, getDestinations: function WorkerTransport_getDestinations() { - var promise = new PDFJS.Promise(); + var promise = new PDFJS.LegacyPromise(); this.messageHandler.send('GetDestinations', null, function transportDestinations(destinations) { promise.resolve(destinations); @@ -4992,6 +4954,7 @@ var WorkerTransport = (function WorkerTransportClosure() { * for fonts, images, rendering code and such. These objects might get processed * inside of a worker. The `PDFObjects` implements some basic functions to * manage these objects. + * @ignore */ var PDFObjects = (function PDFObjectsClosure() { function PDFObjects() { @@ -5008,7 +4971,7 @@ var PDFObjects = (function PDFObjectsClosure() { return this.objs[objId]; var obj = { - promise: new Promise(objId), + promise: new LegacyPromise(), data: null, resolved: false }; @@ -5089,29 +5052,40 @@ var PDFObjects = (function PDFObjectsClosure() { }; return PDFObjects; })(); -/* - * RenderTask is basically a promise but adds a cancel function to terminate it. + +/** + * Allows controlling of the rendering tasks. + * @class */ var RenderTask = (function RenderTaskClosure() { function RenderTask(internalRenderTask) { this.internalRenderTask = internalRenderTask; - Promise.call(this); + /** + * Promise for rendering task completion. + * @type {Promise} + */ + this.promise = new PDFJS.LegacyPromise(); } - RenderTask.prototype = Object.create(Promise.prototype); - - /** - * Cancel the rendering task. If the task is curently rendering it will not be - * cancelled until graphics pauses with a timeout. The promise that this - * object extends will resolved when cancelled. - */ - RenderTask.prototype.cancel = function RenderTask_cancel() { - this.internalRenderTask.cancel(); + 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')); + } }; return RenderTask; })(); +/** + * For internal use only. + * @ignore + */ var InternalRenderTask = (function InternalRenderTaskClosure() { function InternalRenderTask(callback, params, objs, commonObjs, operatorList, @@ -5465,6 +5439,18 @@ function compileType3Glyph(imgData) { var points = new Uint8Array(width1 * (height + 1)); var POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]); + + // decodes bit-packed mask data + var lineSize = (width + 7) & ~7, data0 = imgData.data; + var data = new Uint8Array(lineSize * height), pos = 0, ii; + for (i = 0, ii = data0.length; i < ii; i++) { + var mask = 128, elem = data0[i]; + while (mask > 0) { + data[pos++] = (elem & mask) ? 0 : 255; + mask >>= 1; + } + } + // finding iteresting points: every point is located between mask pixels, // so there will be points of the (width + 1)x(height + 1) grid. Every point // will have flags assigned based on neighboring mask pixels: @@ -5475,24 +5461,25 @@ function compileType3Glyph(imgData) { // - outside corners: 1, 2, 4, 8; // - inside corners: 7, 11, 13, 14; // - and, intersections: 5, 10. - var pos = 3, data = imgData.data, lineSize = width * 4, count = 0; - if (data[3] !== 0) { + var count = 0; + pos = 0; + if (data[pos] !== 0) { points[0] = 1; ++count; } for (j = 1; j < width; j++) { - if (data[pos] !== data[pos + 4]) { + if (data[pos] !== data[pos + 1]) { points[j] = data[pos] ? 2 : 1; ++count; } - pos += 4; + pos++; } if (data[pos] !== 0) { points[j] = 2; ++count; } - pos += 4; for (i = 1; i < height; i++) { + pos = i * lineSize; j0 = i * width1; if (data[pos - lineSize] !== data[pos]) { points[j0] = data[pos] ? 1 : 8; @@ -5502,37 +5489,36 @@ function compileType3Glyph(imgData) { // array (in order 8-1-2-4, so we can use '>>2' to shift the column). var sum = (data[pos] ? 4 : 0) + (data[pos - lineSize] ? 8 : 0); for (j = 1; j < width; j++) { - sum = (sum >> 2) + (data[pos + 4] ? 4 : 0) + - (data[pos - lineSize + 4] ? 8 : 0); + sum = (sum >> 2) + (data[pos + 1] ? 4 : 0) + + (data[pos - lineSize + 1] ? 8 : 0); if (POINT_TYPES[sum]) { points[j0 + j] = POINT_TYPES[sum]; ++count; } - pos += 4; + pos++; } if (data[pos - lineSize] !== data[pos]) { points[j0 + j] = data[pos] ? 2 : 4; ++count; } - pos += 4; if (count > POINT_TO_PROCESS_LIMIT) { return null; } } - pos -= lineSize; + pos = lineSize * (height - 1); j0 = i * width1; if (data[pos] !== 0) { points[j0] = 8; ++count; } for (j = 1; j < width; j++) { - if (data[pos] !== data[pos + 4]) { + if (data[pos] !== data[pos + 1]) { points[j0 + j] = data[pos] ? 4 : 8; ++count; } - pos += 4; + pos++; } if (data[pos] !== 0) { points[j0 + j] = 4; @@ -5638,7 +5624,6 @@ var CanvasExtraState = (function CanvasExtraStateClosure() { this.fillAlpha = 1; this.strokeAlpha = 1; this.lineWidth = 1; - this.paintFormXObjectDepth = 0; this.old = old; } @@ -5690,16 +5675,77 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { return; } - var tmpImgData = ctx.createImageData(imgData.width, imgData.height); + // Put the image data to the canvas in chunks, rather than putting the + // whole image at once. This saves JS memory, because the ImageData object + // is smaller. It also possibly saves C++ memory within the implementation + // of putImageData(). (E.g. in Firefox we make two short-lived copies of + // the data passed to putImageData()). |n| shouldn't be too small, however, + // because too many putImageData() calls will slow things down. + var rowsInFullChunks = 16; + var fullChunks = (imgData.height / rowsInFullChunks) | 0; + var rowsInLastChunk = imgData.height - fullChunks * rowsInFullChunks; + var elemsInFullChunks = imgData.width * rowsInFullChunks * 4; + var elemsInLastChunk = imgData.width * rowsInLastChunk * 4; + + var chunkImgData = ctx.createImageData(imgData.width, rowsInFullChunks); + var srcPos = 0; + var src = imgData.data; + var dst = chunkImgData.data; + var haveSetAndSubarray = 'set' in dst && 'subarray' in src; + + // Do all the full-size chunks. + for (var i = 0; i < fullChunks; i++) { + if (haveSetAndSubarray) { + dst.set(src.subarray(srcPos, srcPos + elemsInFullChunks)); + srcPos += elemsInFullChunks; + } else { + for (var j = 0; j < elemsInFullChunks; j++) { + chunkImgData.data[j] = imgData.data[srcPos++]; + } + } + ctx.putImageData(chunkImgData, 0, i * rowsInFullChunks); + } + + // Do the final, partial chunk, if required. + if (rowsInLastChunk !== 0) { + if (haveSetAndSubarray) { + dst.set(src.subarray(srcPos, srcPos + elemsInLastChunk)); + srcPos += elemsInLastChunk; + } else { + for (var j = 0; j < elemsInLastChunk; j++) { + chunkImgData.data[j] = imgData.data[srcPos++]; + } + } + // This (conceptually) puts pixels past the bounds of the canvas. But + // that's ok; any such pixels are ignored. + ctx.putImageData(chunkImgData, 0, fullChunks * rowsInFullChunks); + } + } + + function putBinaryImageMask(ctx, imgData) { + var width = imgData.width, height = imgData.height; + var tmpImgData = ctx.createImageData(width, height); var data = imgData.data; var tmpImgDataPixels = tmpImgData.data; - if ('set' in tmpImgDataPixels) - tmpImgDataPixels.set(data); - else { - // Copy over the imageData pixel by pixel. - for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++) - tmpImgDataPixels[i] = data[i]; + var dataPos = 0; + + // Expand the mask so it can be used by the canvas. Any required inversion + // has already been handled. + var tmpPos = 3; // alpha component offset + for (var i = 0; i < height; i++) { + var mask = 0; + for (var j = 0; j < width; j++) { + if (!mask) { + var elem = data[dataPos++]; + mask = 128; + } + if (!(elem & mask)) { + tmpImgDataPixels[tmpPos] = 255; + } + tmpPos += 4; + mask >>= 1; + } } ctx.putImageData(tmpImgData, 0, 0); @@ -5782,6 +5828,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var commonObjs = this.commonObjs; var objs = this.objs; var fnId; + var deferred = Promise.resolve(); while (true) { if (stepper && i === stepper.nextBreakPoint) { @@ -5823,7 +5870,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // to continue exeution after a short delay. // However, this is only possible if a 'continueCallback' is passed in. if (continueCallback && Date.now() > endTime) { - setTimeout(continueCallback, 0); + deferred.then(continueCallback); return i; } @@ -6227,6 +6274,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { geometry.fontName = font.loadedName; geometry.fontFamily = font.fallbackName; geometry.fontSize = this.current.fontSize; + geometry.ascent = font.ascent; + geometry.descent = font.descent; return geometry; }, @@ -6283,6 +6332,23 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } }, + get isFontSubpixelAAEnabled() { + // Checks if anti-aliasing is enabled when scaled text is painted. + // On Windows GDI scaled fonts looks bad. + var ctx = document.createElement('canvas').getContext('2d'); + ctx.scale(1.5, 1); + ctx.fillText('I', 0, 10); + var data = ctx.getImageData(0, 0, 10, 10).data; + var enabled = false; + for (var i = 3; i < data.length; i += 4) { + if (data[i] > 0 && data[i] < 255) { + enabled = true; + break; + } + } + return shadow(this, 'isFontSubpixelAAEnabled', enabled); + }, + showText: function CanvasGraphics_showText(glyphs, skipTextSelection) { var ctx = this.ctx; var current = this.current; @@ -6397,7 +6463,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { scaledY = 0; } - if (font.remeasure && width > 0) { + if (font.remeasure && width > 0 && this.isFontSubpixelAAEnabled) { // some standard fonts may not have the exact width, trying to // rescale per character var measuredWidth = ctx.measureText(character).width * 1000 / @@ -6684,7 +6750,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix, bbox) { this.save(); - this.current.paintFormXObjectDepth++; this.baseTransformStack.push(this.baseTransform); if (matrix && isArray(matrix) && 6 == matrix.length) @@ -6702,12 +6767,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { }, paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() { - var depth = this.current.paintFormXObjectDepth; - do { - this.restore(); - // some pdf don't close all restores inside object - // closing those for them - } while (this.current.paintFormXObjectDepth >= depth); + this.restore(); this.baseTransform = this.baseTransformStack.pop(); }, @@ -6734,7 +6794,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // TODO knockout - supposedly possible with the clever use of compositing // modes. if (group.knockout) { - TODO('Support knockout groups.'); + warn('Knockout groups not supported.'); } var currentTransform = currentCtx.mozCurrentTransform; @@ -6884,7 +6944,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var maskCtx = maskCanvas.context; maskCtx.save(); - putBinaryImageData(maskCtx, img); + putBinaryImageMask(maskCtx, img); maskCtx.globalCompositeOperation = 'source-in'; @@ -6911,7 +6971,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var maskCtx = maskCanvas.context; maskCtx.save(); - putBinaryImageData(maskCtx, image); + putBinaryImageMask(maskCtx, image); maskCtx.globalCompositeOperation = 'source-in'; diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index d6628609ff91..5e995f0224f0 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -20,8 +20,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.759'; -PDFJS.build = 'd3b5aa3'; +PDFJS.version = '0.8.934'; +PDFJS.build = 'c80df60'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -43,7 +43,8 @@ PDFJS.build = 'd3b5aa3'; * See the License for the specific language governing permissions and * limitations under the License. */ -/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL */ +/* globals Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL, + Promise */ 'use strict'; @@ -51,9 +52,6 @@ var globalScope = (typeof window === 'undefined') ? this : window; var isWorker = (typeof window == 'undefined'); -var ERRORS = 0, WARNINGS = 1, INFOS = 5; -var verbosity = WARNINGS; - var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; var TextRenderingMode = { @@ -78,6 +76,12 @@ if (!globalScope.PDFJS) { globalScope.PDFJS.pdfBug = false; +PDFJS.VERBOSITY_LEVELS = { + errors: 0, + warnings: 1, + infos: 5 +}; + // All the possible operations for an operator list. var OPS = PDFJS.OPS = { // Intentionally start from 1 so it is easy to spot bad operators that will be @@ -171,32 +175,19 @@ var OPS = PDFJS.OPS = { paintInlineImageXObjectGroup: 87 }; -// Use only for debugging purposes. This should not be used in any code that is -// in mozilla master. -var log = (function() { - if ('console' in globalScope && 'log' in globalScope['console']) { - return globalScope['console']['log'].bind(globalScope['console']); - } else { - return function nop() { - }; - } -})(); - -// A notice for devs that will not trigger the fallback UI. These are good -// for things that are helpful to devs, such as warning that Workers were -// disabled, which is important to devs but not end users. +// A notice for devs. These are good for things that are helpful to devs, such +// as warning that Workers were disabled, which is important to devs but not +// end users. function info(msg) { - if (verbosity >= INFOS) { - log('Info: ' + msg); - PDFJS.LogManager.notify('info', msg); + if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) { + console.log('Info: ' + msg); } } -// Non-fatal warnings that should trigger the fallback UI. +// Non-fatal warnings. function warn(msg) { - if (verbosity >= WARNINGS) { - log('Warning: ' + msg); - PDFJS.LogManager.notify('warn', msg); + if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) { + console.log('Warning: ' + msg); } } @@ -207,22 +198,17 @@ function error(msg) { if (arguments.length > 1) { var logArguments = ['Error:']; logArguments.push.apply(logArguments, arguments); - log.apply(null, logArguments); + console.log.apply(console, logArguments); // Join the arguments into a single string for the lines below. msg = [].join.call(arguments, ' '); } else { - log('Error: ' + msg); + console.log('Error: ' + msg); } - log(backtrace()); - PDFJS.LogManager.notify('error', msg); + console.log(backtrace()); + UnsupportedManager.notify(UNSUPPORTED_FEATURES.unknown); throw new Error(msg); } -// Missing features that should trigger the fallback UI. -function TODO(what) { - warn('TODO: ' + what); -} - function backtrace() { try { throw new Error(); @@ -236,12 +222,37 @@ function assert(cond, msg) { error(msg); } +var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = { + unknown: 'unknown', + forms: 'forms', + javaScript: 'javaScript', + smask: 'smask', + shadingPattern: 'shadingPattern', + font: 'font' +}; + +var UnsupportedManager = PDFJS.UnsupportedManager = + (function UnsupportedManagerClosure() { + var listeners = []; + return { + listen: function (cb) { + listeners.push(cb); + }, + notify: function (featureId) { + warn('Unsupported feature "' + featureId + '"'); + for (var i = 0, ii = listeners.length; i < ii; i++) { + listeners[i](featureId); + } + } + }; +})(); + // Combines two URLs. The baseUrl shall be absolute URL. If the url is an // absolute URL, it will be returned as is. function combineUrl(baseUrl, url) { if (!url) return baseUrl; - if (url.indexOf(':') >= 0) + if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) return url; if (url.charAt(0) == '/') { // absolute path @@ -265,11 +276,13 @@ function isValidUrl(url, allowRelative) { if (!url) { return false; } - var colon = url.indexOf(':'); - if (colon < 0) { + // RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1) + // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url); + if (!protocol) { return allowRelative; } - var protocol = url.substr(0, colon); + protocol = protocol[0].toLowerCase(); switch (protocol) { case 'http': case 'https': @@ -289,22 +302,6 @@ function assertWellFormed(cond, msg) { error(msg); } -var LogManager = PDFJS.LogManager = (function LogManagerClosure() { - var loggers = []; - return { - addLogger: function logManager_addLogger(logger) { - loggers.push(logger); - }, - notify: function(type, message) { - for (var i = 0, ii = loggers.length; i < ii; i++) { - var logger = loggers[i]; - if (logger[type]) - logger[type](message); - } - } - }; -})(); - function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, enumerable: true, @@ -834,6 +831,24 @@ function isPDFFunction(v) { } /** + * Legacy support for PDFJS Promise implementation. + * TODO remove eventually + */ +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; + }; +})(); + +/** + * 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. @@ -842,229 +857,40 @@ function isPDFFunction(v) { * Based off of the work in: * https://bugzilla.mozilla.org/show_bug.cgi?id=810490 */ -var Promise = PDFJS.Promise = (function PromiseClosure() { - var STATUS_PENDING = 0; - var STATUS_RESOLVED = 1; - var STATUS_REJECTED = 2; - - // In an attempt to avoid silent exceptions, unhandled rejections are - // tracked and if they aren't handled in a certain amount of time an - // error is logged. - var REJECTION_TIMEOUT = 500; - - var HandlerManager = { - handlers: [], - running: false, - unhandledRejections: [], - pendingRejectionCheck: false, - - scheduleHandlers: function scheduleHandlers(promise) { - if (promise._status == STATUS_PENDING) { - return; - } - - this.handlers = this.handlers.concat(promise._handlers); - promise._handlers = []; - - if (this.running) { - return; - } - this.running = true; - - setTimeout(this.runHandlers.bind(this), 0); - }, - - runHandlers: function runHandlers() { - while (this.handlers.length > 0) { - var handler = this.handlers.shift(); - - var nextStatus = handler.thisPromise._status; - var nextValue = handler.thisPromise._value; - - try { - if (nextStatus === STATUS_RESOLVED) { - if (typeof(handler.onResolve) == 'function') { - nextValue = handler.onResolve(nextValue); +(function PromiseClosure() { + if (globalScope.Promise) { + // Promises existing in the DOM/Worker, checking presence of all/resolve + if (typeof globalScope.Promise.all !== 'function') { + globalScope.Promise.all = function (iterable) { + var count = 0, results = [], resolve, reject; + var promise = new globalScope.Promise(function (resolve_, reject_) { + resolve = resolve_; + reject = reject_; + }); + iterable.forEach(function (p, i) { + count++; + p.then(function (result) { + results[i] = result; + count--; + if (count === 0) { + resolve(results); } - } else if (typeof(handler.onReject) === 'function') { - nextValue = handler.onReject(nextValue); - nextStatus = STATUS_RESOLVED; - - if (handler.thisPromise._unhandledRejection) { - this.removeUnhandeledRejection(handler.thisPromise); - } - } - } catch (ex) { - nextStatus = STATUS_REJECTED; - nextValue = ex; + }, reject); + }); + if (count === 0) { + resolve(results); } - - handler.nextPromise._updateStatus(nextStatus, nextValue); - } - - this.running = false; - }, - - addUnhandledRejection: function addUnhandledRejection(promise) { - this.unhandledRejections.push({ - promise: promise, - time: Date.now() - }); - this.scheduleRejectionCheck(); - }, - - removeUnhandeledRejection: function removeUnhandeledRejection(promise) { - promise._unhandledRejection = false; - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (this.unhandledRejections[i].promise === promise) { - this.unhandledRejections.splice(i); - i--; - } - } - }, - - scheduleRejectionCheck: function scheduleRejectionCheck() { - if (this.pendingRejectionCheck) { - return; - } - this.pendingRejectionCheck = true; - setTimeout(function rejectionCheck() { - this.pendingRejectionCheck = false; - var now = Date.now(); - for (var i = 0; i < this.unhandledRejections.length; i++) { - if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) { - var unhandled = this.unhandledRejections[i].promise._value; - var msg = 'Unhandled rejection: ' + unhandled; - if (unhandled.stack) { - msg += '\n' + unhandled.stack; - } - warn(msg); - this.unhandledRejections.splice(i); - i--; - } - } - if (this.unhandledRejections.length) { - this.scheduleRejectionCheck(); - } - }.bind(this), REJECTION_TIMEOUT); + return promise; + }; } - }; - - function Promise() { - this._status = STATUS_PENDING; - this._handlers = []; + if (typeof globalScope.Promise.resolve !== 'function') { + globalScope.Promise.resolve = function (x) { + return new globalScope.Promise(function (resolve) { resolve(x); }); + }; + } + return; } - /** - * Builds a promise that is resolved when all the passed in promises are - * resolved. - * @param {array} array of data and/or promises to wait for. - * @return {Promise} New dependant promise. - */ - Promise.all = function Promise_all(promises) { - var deferred = new Promise(); - var unresolved = promises.length; - var results = []; - if (unresolved === 0) { - deferred.resolve(results); - return deferred; - } - function reject(reason) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results = []; - deferred.reject(reason); - } - for (var i = 0, ii = promises.length; i < ii; ++i) { - var promise = promises[i]; - var resolve = (function(i) { - return function(value) { - if (deferred._status === STATUS_REJECTED) { - return; - } - results[i] = value; - unresolved--; - if (unresolved === 0) - deferred.resolve(results); - }; - })(i); - if (Promise.isPromise(promise)) { - promise.then(resolve, reject); - } else { - resolve(promise); - } - } - return deferred; - }; - - /** - * Checks if the value is likely a promise (has a 'then' function). - * @return {boolean} true if x is thenable - */ - Promise.isPromise = function Promise_isPromise(value) { - return value && typeof value.then === 'function'; - }; - - Promise.prototype = { - _status: null, - _value: null, - _handlers: null, - _unhandledRejection: null, - - _updateStatus: function Promise__updateStatus(status, value) { - if (this._status === STATUS_RESOLVED || - this._status === STATUS_REJECTED) { - return; - } - - if (status == STATUS_RESOLVED && - Promise.isPromise(value)) { - value.then(this._updateStatus.bind(this, STATUS_RESOLVED), - this._updateStatus.bind(this, STATUS_REJECTED)); - return; - } - - this._status = status; - this._value = value; - - if (status === STATUS_REJECTED && this._handlers.length === 0) { - this._unhandledRejection = true; - HandlerManager.addUnhandledRejection(this); - } - - HandlerManager.scheduleHandlers(this); - }, - - get isResolved() { - return this._status === STATUS_RESOLVED; - }, - - get isRejected() { - return this._status === STATUS_REJECTED; - }, - - resolve: function Promise_resolve(value) { - this._updateStatus(STATUS_RESOLVED, value); - }, - - reject: function Promise_reject(reason) { - this._updateStatus(STATUS_REJECTED, reason); - }, - - then: function Promise_then(onResolve, onReject) { - var nextPromise = new Promise(); - this._handlers.push({ - thisPromise: this, - onResolve: onResolve, - onReject: onReject, - nextPromise: nextPromise - }); - HandlerManager.scheduleHandlers(this); - return nextPromise; - } - }; - - return Promise; + throw new Error('DOM Promise is not present'); })(); var StatTimer = (function StatTimerClosure() { @@ -1130,18 +956,17 @@ PDFJS.createBlob = function createBlob(data, contentType) { }; PDFJS.createObjectURL = (function createObjectURLClosure() { - if (typeof URL !== 'undefined' && URL.createObjectURL) { - return function createObjectURL(data, contentType) { - var blob = PDFJS.createBlob(data, contentType); - return URL.createObjectURL(blob); - }; - } - // Blob/createObjectURL is not available, falling back to data schema. var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; return function createObjectURL(data, contentType) { + if (!PDFJS.disableCreateObjectURL && + typeof URL !== 'undefined' && URL.createObjectURL) { + var blob = PDFJS.createBlob(data, contentType); + return URL.createObjectURL(blob); + } + var buffer = 'data:' + contentType + ';base64,'; for (var i = 0, ii = data.length; i < ii; i += 3) { var b1 = data[i] & 0xFF; @@ -1165,21 +990,13 @@ function MessageHandler(name, comObj) { var ah = this.actionHandler = {}; ah['console_log'] = [function ahConsoleLog(data) { - log.apply(null, data); + console.log.apply(console, data); }]; - // If there's no console available, console_error in the - // action handler will do nothing. - if ('console' in globalScope) { - ah['console_error'] = [function ahConsoleError(data) { - globalScope['console'].error.apply(null, data); - }]; - } else { - ah['console_error'] = [function ahConsoleError(data) { - log.apply(null, data); - }]; - } - ah['_warn'] = [function ah_Warn(data) { - warn(data); + ah['console_error'] = [function ahConsoleError(data) { + console.error.apply(console, data); + }]; + ah['_unsupported_feature'] = [function ah_unsupportedFeature(data) { + UnsupportedManager.notify(data); }]; comObj.onmessage = function messageHandlerComObjOnMessage(event) { @@ -1196,7 +1013,12 @@ function MessageHandler(name, comObj) { } else if (data.action in ah) { var action = ah[data.action]; if (data.callbackId) { - var promise = new Promise(); + var deferred = {}; + var promise = new Promise(function (resolve, reject) { + deferred.resolve = resolve; + deferred.reject = reject; + }); + deferred.promise = promise; promise.then(function(resolvedData) { comObj.postMessage({ isReply: true, @@ -1204,7 +1026,7 @@ function MessageHandler(name, comObj) { data: resolvedData }); }); - action[0].call(action[1], data.data, promise); + action[0].call(action[1], data.data, deferred); } else { action[0].call(action[1], data.data); } @@ -1256,6 +1078,933 @@ function loadJpegStream(id, imageUrl, objs) { } +var ColorSpace = (function ColorSpaceClosure() { + // Constructor should define this.numComps, this.defaultColor, this.name + function ColorSpace() { + error('should not call ColorSpace constructor'); + } + + ColorSpace.prototype = { + /** + * Converts the color value to the RGB color. The color components are + * located in the src array starting from the srcOffset. Returns the array + * of the rgb components, each value ranging from [0,255]. + */ + getRgb: function ColorSpace_getRgb(src, srcOffset) { + error('Should not call ColorSpace.getRgb'); + }, + /** + * Converts the color value to the RGB color, similar to the getRgb method. + * The result placed into the dest array starting from the destOffset. + */ + getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) { + error('Should not call ColorSpace.getRgbItem'); + }, + /** + * Converts the specified number of the color values to the RGB colors. + * The colors are located in the src array starting from the srcOffset. + * The result is placed into the dest array starting from the destOffset. + * The src array items shall be in [0,2^bits) range, the dest array items + * will be in [0,255] range. alpha01 indicates how many alpha components + * there are in the dest array; it will be either 0 (RGB array) or 1 (RGBA + * array). + */ + getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + error('Should not call ColorSpace.getRgbBuffer'); + }, + /** + * Determines the number of bytes required to store the result of the + * conversion done by the getRgbBuffer method. As in getRgbBuffer, + * |alpha01| is either 0 (RGB output) or 1 (RGBA output). + */ + getOutputLength: function ColorSpace_getOutputLength(inputLength, + alpha01) { + error('Should not call ColorSpace.getOutputLength'); + }, + /** + * Returns true if source data will be equal the result/output data. + */ + isPassthrough: function ColorSpace_isPassthrough(bits) { + return false; + }, + /** + * Fills in the RGB colors in an RGBA buffer. + */ + fillRgb: function ColorSpace_fillRgb(rgbaBuf, originalWidth, + originalHeight, width, height, + actualHeight, bpc, comps) { + var count = originalWidth * originalHeight; + var rgbBuf = null; + var numComponentColors = 1 << bpc; + var needsResizing = originalHeight != height || originalWidth != width; + + if (this.isPassthrough(bpc)) { + rgbBuf = comps; + + } else if (this.numComps === 1 && count > numComponentColors && + this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') { + // Optimization: create a color map when there is just one component and + // we are converting more colors than the size of the color map. We + // don't build the map if the colorspace is gray or rgb since those + // methods are faster than building a map. This mainly offers big speed + // ups for indexed and alternate colorspaces. + // + // TODO it may be worth while to cache the color map. While running + // testing I never hit a cache so I will leave that out for now (perhaps + // we are reparsing colorspaces too much?). + var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : + new Uint16Array(numComponentColors); + for (var i = 0; i < numComponentColors; i++) { + allColors[i] = i; + } + var colorMap = new Uint8Array(numComponentColors * 3); + this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, + /* alpha01 = */ 0); + + if (!needsResizing) { + // Fill in the RGB values directly into |rgbaBuf|. + var rgbaPos = 0; + for (var i = 0; i < count; ++i) { + var key = comps[i] * 3; + rgbaBuf[rgbaPos++] = colorMap[key]; + rgbaBuf[rgbaPos++] = colorMap[key + 1]; + rgbaBuf[rgbaPos++] = colorMap[key + 2]; + rgbaPos++; + } + } else { + rgbBuf = new Uint8Array(count * 3); + var rgbPos = 0; + for (var i = 0; i < count; ++i) { + var key = comps[i] * 3; + rgbBuf[rgbPos++] = colorMap[key]; + rgbBuf[rgbPos++] = colorMap[key + 1]; + rgbBuf[rgbPos++] = colorMap[key + 2]; + } + } + } else { + if (!needsResizing) { + // Fill in the RGB values directly into |rgbaBuf|. + this.getRgbBuffer(comps, 0, width * actualHeight, rgbaBuf, 0, bpc, + /* alpha01 = */ 1); + } else { + 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); + } + var rgbPos = 0; + var actualLength = width * actualHeight * 4; + for (var i = 0; i < actualLength; i += 4) { + rgbaBuf[i] = rgbBuf[rgbPos++]; + rgbaBuf[i + 1] = rgbBuf[rgbPos++]; + rgbaBuf[i + 2] = rgbBuf[rgbPos++]; + } + } + }, + /** + * 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]. + */ + usesZeroToOneRange: true + }; + + ColorSpace.parse = function ColorSpace_parse(cs, xref, res) { + var IR = ColorSpace.parseToIR(cs, xref, res); + if (IR instanceof AlternateCS) + return IR; + + return ColorSpace.fromIR(IR); + }; + + ColorSpace.fromIR = function ColorSpace_fromIR(IR) { + var name = isArray(IR) ? IR[0] : IR; + + switch (name) { + case 'DeviceGrayCS': + return this.singletons.gray; + case 'DeviceRgbCS': + return this.singletons.rgb; + case 'DeviceCmykCS': + return this.singletons.cmyk; + case 'CalGrayCS': + var whitePoint = IR[1].WhitePoint; + var blackPoint = IR[1].BlackPoint; + var gamma = IR[1].Gamma; + return new CalGrayCS(whitePoint, blackPoint, gamma); + case 'PatternCS': + var basePatternCS = IR[1]; + if (basePatternCS) + basePatternCS = ColorSpace.fromIR(basePatternCS); + return new PatternCS(basePatternCS); + case 'IndexedCS': + var baseIndexedCS = IR[1]; + var hiVal = IR[2]; + var lookup = IR[3]; + return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup); + case 'AlternateCS': + var numComps = IR[1]; + var alt = IR[2]; + var tintFnIR = IR[3]; + + return new AlternateCS(numComps, ColorSpace.fromIR(alt), + PDFFunction.fromIR(tintFnIR)); + case 'LabCS': + var whitePoint = IR[1].WhitePoint; + var blackPoint = IR[1].BlackPoint; + var range = IR[1].Range; + return new LabCS(whitePoint, blackPoint, range); + default: + error('Unkown name ' + name); + } + return null; + }; + + ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) { + if (isName(cs)) { + var colorSpaces = res.get('ColorSpace'); + if (isDict(colorSpaces)) { + var refcs = colorSpaces.get(cs.name); + if (refcs) + cs = refcs; + } + } + + cs = xref.fetchIfRef(cs); + var mode; + + if (isName(cs)) { + mode = cs.name; + this.mode = mode; + + switch (mode) { + case 'DeviceGray': + case 'G': + return 'DeviceGrayCS'; + case 'DeviceRGB': + case 'RGB': + return 'DeviceRgbCS'; + case 'DeviceCMYK': + case 'CMYK': + return 'DeviceCmykCS'; + case 'Pattern': + return ['PatternCS', null]; + default: + error('unrecognized colorspace ' + mode); + } + } else if (isArray(cs)) { + mode = cs[0].name; + this.mode = mode; + + switch (mode) { + case 'DeviceGray': + case 'G': + return 'DeviceGrayCS'; + case 'DeviceRGB': + case 'RGB': + return 'DeviceRgbCS'; + case 'DeviceCMYK': + case 'CMYK': + return 'DeviceCmykCS'; + case 'CalGray': + var params = cs[1].getAll(); + return ['CalGrayCS', params]; + case 'CalRGB': + return 'DeviceRgbCS'; + case 'ICCBased': + var stream = xref.fetchIfRef(cs[1]); + var dict = stream.dict; + var numComps = dict.get('N'); + if (numComps == 1) + return 'DeviceGrayCS'; + if (numComps == 3) + return 'DeviceRgbCS'; + if (numComps == 4) + return 'DeviceCmykCS'; + break; + case 'Pattern': + var basePatternCS = cs[1]; + if (basePatternCS) + basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res); + return ['PatternCS', basePatternCS]; + case 'Indexed': + case 'I': + var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res); + var hiVal = cs[2] + 1; + var lookup = xref.fetchIfRef(cs[3]); + if (isStream(lookup)) { + lookup = lookup.getBytes(); + } + return ['IndexedCS', baseIndexedCS, hiVal, lookup]; + case 'Separation': + case 'DeviceN': + var name = cs[1]; + var numComps = 1; + if (isName(name)) + numComps = 1; + else if (isArray(name)) + numComps = name.length; + var alt = ColorSpace.parseToIR(cs[2], xref, res); + var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); + return ['AlternateCS', numComps, alt, tintFnIR]; + case 'Lab': + var params = cs[1].getAll(); + return ['LabCS', params]; + default: + error('unimplemented color space object "' + mode + '"'); + } + } else { + error('unrecognized color space object: "' + cs + '"'); + } + return null; + }; + /** + * Checks if a decode map matches the default decode map for a color space. + * This handles the general decode maps where there are two values per + * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color. + * This does not handle Lab, Indexed, or Pattern decode maps since they are + * slightly different. + * @param {Array} decode Decode map (usually from an image). + * @param {Number} n Number of components the color space has. + */ + ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) { + if (!decode) + return true; + + if (n * 2 !== decode.length) { + warn('The decode map is not the correct length'); + return true; + } + for (var i = 0, ii = decode.length; i < ii; i += 2) { + if (decode[i] !== 0 || decode[i + 1] != 1) + return false; + } + return true; + }; + + ColorSpace.singletons = { + get gray() { + return shadow(this, 'gray', new DeviceGrayCS()); + }, + get rgb() { + return shadow(this, 'rgb', new DeviceRgbCS()); + }, + get cmyk() { + return shadow(this, 'cmyk', new DeviceCmykCS()); + } + }; + + return ColorSpace; +})(); + +/** + * Alternate color space handles both Separation and DeviceN color spaces. A + * Separation color space is actually just a DeviceN with one color component. + * Both color spaces use a tinting function to convert colors to a base color + * space. + */ +var AlternateCS = (function AlternateCSClosure() { + function AlternateCS(numComps, base, tintFn) { + this.name = 'Alternate'; + this.numComps = numComps; + this.defaultColor = new Float32Array(numComps); + for (var i = 0; i < numComps; ++i) { + this.defaultColor[i] = 1; + } + this.base = base; + this.tintFn = tintFn; + } + + AlternateCS.prototype = { + getRgb: function AlternateCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + }, + getRgbItem: function AlternateCS_getRgbItem(src, srcOffset, + dest, destOffset) { + var baseNumComps = this.base.numComps; + var input = 'subarray' in src ? + src.subarray(srcOffset, srcOffset + this.numComps) : + Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps); + var tinted = this.tintFn(input); + this.base.getRgbItem(tinted, 0, dest, destOffset); + }, + getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + var tintFn = this.tintFn; + var base = this.base; + var scale = 1 / ((1 << bits) - 1); + var baseNumComps = base.numComps; + var usesZeroToOneRange = base.usesZeroToOneRange; + var isPassthrough = (base.isPassthrough(8) || !usesZeroToOneRange) && + alpha01 === 0; + var pos = isPassthrough ? destOffset : 0; + var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count); + var numComps = this.numComps; + + var scaled = new Float32Array(numComps); + for (var i = 0; i < count; i++) { + for (var j = 0; j < numComps; j++) { + scaled[j] = src[srcOffset++] * scale; + } + var tinted = tintFn(scaled); + if (usesZeroToOneRange) { + for (var j = 0; j < baseNumComps; j++) { + baseBuf[pos++] = tinted[j] * 255; + } + } else { + base.getRgbItem(tinted, 0, baseBuf, pos); + pos += baseNumComps; + } + } + if (!isPassthrough) { + base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8, alpha01); + } + }, + getOutputLength: function AlternateCS_getOutputLength(inputLength, + alpha01) { + return this.base.getOutputLength(inputLength * + this.base.numComps / this.numComps, + alpha01); + }, + isPassthrough: ColorSpace.prototype.isPassthrough, + fillRgb: ColorSpace.prototype.fillRgb, + isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) { + return ColorSpace.isDefaultDecode(decodeMap, this.numComps); + }, + usesZeroToOneRange: true + }; + + return AlternateCS; +})(); + +var PatternCS = (function PatternCSClosure() { + function PatternCS(baseCS) { + this.name = 'Pattern'; + this.base = baseCS; + } + PatternCS.prototype = {}; + + return PatternCS; +})(); + +var IndexedCS = (function IndexedCSClosure() { + function IndexedCS(base, highVal, lookup) { + this.name = 'Indexed'; + this.numComps = 1; + this.defaultColor = new Uint8Array([0]); + this.base = base; + this.highVal = highVal; + + var baseNumComps = base.numComps; + var length = baseNumComps * highVal; + var lookupArray; + + if (isStream(lookup)) { + lookupArray = new Uint8Array(length); + var bytes = lookup.getBytes(length); + lookupArray.set(bytes); + } else if (isString(lookup)) { + lookupArray = new Uint8Array(length); + for (var i = 0; i < length; ++i) + lookupArray[i] = lookup.charCodeAt(i); + } else if (lookup instanceof Uint8Array || lookup instanceof Array) { + lookupArray = lookup; + } else { + error('Unrecognized lookup table: ' + lookup); + } + this.lookup = lookupArray; + } + + IndexedCS.prototype = { + getRgb: function IndexedCS_getRgb(src, srcOffset) { + var numComps = this.base.numComps; + var start = src[srcOffset] * numComps; + return this.base.getRgb(this.lookup, start); + }, + getRgbItem: function IndexedCS_getRgbItem(src, srcOffset, + dest, destOffset) { + var numComps = this.base.numComps; + var start = src[srcOffset] * numComps; + this.base.getRgbItem(this.lookup, start, dest, destOffset); + }, + getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + var base = this.base; + var numComps = base.numComps; + var outputDelta = base.getOutputLength(numComps, alpha01); + var lookup = this.lookup; + + for (var i = 0; i < count; ++i) { + var lookupPos = src[srcOffset++] * numComps; + base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8, alpha01); + destOffset += outputDelta; + } + }, + getOutputLength: function IndexedCS_getOutputLength(inputLength, alpha01) { + return this.base.getOutputLength(inputLength * this.base.numComps, + alpha01); + }, + isPassthrough: ColorSpace.prototype.isPassthrough, + fillRgb: ColorSpace.prototype.fillRgb, + isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) { + // indexed color maps shouldn't be changed + return true; + }, + usesZeroToOneRange: true + }; + return IndexedCS; +})(); + +var DeviceGrayCS = (function DeviceGrayCSClosure() { + function DeviceGrayCS() { + this.name = 'DeviceGray'; + this.numComps = 1; + this.defaultColor = new Float32Array([0]); + } + + DeviceGrayCS.prototype = { + getRgb: function DeviceGrayCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + }, + getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset, + dest, destOffset) { + var c = (src[srcOffset] * 255) | 0; + c = c < 0 ? 0 : c > 255 ? 255 : c; + dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c; + }, + getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + var scale = 255 / ((1 << bits) - 1); + var j = srcOffset, q = destOffset; + for (var i = 0; i < count; ++i) { + var c = (scale * src[j++]) | 0; + dest[q++] = c; + dest[q++] = c; + dest[q++] = c; + q += alpha01; + } + }, + getOutputLength: function DeviceGrayCS_getOutputLength(inputLength, + alpha01) { + return inputLength * (3 + alpha01); + }, + isPassthrough: ColorSpace.prototype.isPassthrough, + fillRgb: ColorSpace.prototype.fillRgb, + isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) { + return ColorSpace.isDefaultDecode(decodeMap, this.numComps); + }, + usesZeroToOneRange: true + }; + return DeviceGrayCS; +})(); + +var DeviceRgbCS = (function DeviceRgbCSClosure() { + function DeviceRgbCS() { + this.name = 'DeviceRGB'; + this.numComps = 3; + this.defaultColor = new Float32Array([0, 0, 0]); + } + DeviceRgbCS.prototype = { + getRgb: function DeviceRgbCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + }, + getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset, + dest, destOffset) { + var r = (src[srcOffset] * 255) | 0; + var g = (src[srcOffset + 1] * 255) | 0; + var b = (src[srcOffset + 2] * 255) | 0; + dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r; + dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g; + dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b; + }, + getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + if (bits === 8 && alpha01 === 0) { + dest.set(src.subarray(srcOffset, srcOffset + count * 3), destOffset); + return; + } + var scale = 255 / ((1 << bits) - 1); + var j = srcOffset, q = destOffset; + for (var i = 0; i < count; ++i) { + dest[q++] = (scale * src[j++]) | 0; + dest[q++] = (scale * src[j++]) | 0; + dest[q++] = (scale * src[j++]) | 0; + q += alpha01; + } + }, + getOutputLength: function DeviceRgbCS_getOutputLength(inputLength, + alpha01) { + return (inputLength * (3 + alpha01) / 3) | 0; + }, + isPassthrough: function DeviceRgbCS_isPassthrough(bits) { + return bits == 8; + }, + fillRgb: ColorSpace.prototype.fillRgb, + isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) { + return ColorSpace.isDefaultDecode(decodeMap, this.numComps); + }, + usesZeroToOneRange: true + }; + return DeviceRgbCS; +})(); + +var DeviceCmykCS = (function DeviceCmykCSClosure() { + // The coefficients below was found using numerical analysis: the method of + // steepest descent for the sum((f_i - color_value_i)^2) for r/g/b colors, + // where color_value is the tabular value from the table of sampled RGB colors + // from CMYK US Web Coated (SWOP) colorspace, and f_i is the corresponding + // CMYK color conversion using the estimation below: + // f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255 + function convertToRgb(src, srcOffset, srcScale, dest, destOffset) { + var c = src[srcOffset + 0] * srcScale; + var m = src[srcOffset + 1] * srcScale; + var y = src[srcOffset + 2] * srcScale; + var k = src[srcOffset + 3] * srcScale; + + var r = + c * (-4.387332384609988 * c + 54.48615194189176 * m + + 18.82290502165302 * y + 212.25662451639585 * k + + -285.2331026137004) + + m * (1.7149763477362134 * m - 5.6096736904047315 * y + + -17.873870861415444 * k - 5.497006427196366) + + y * (-2.5217340131683033 * y - 21.248923337353073 * k + + 17.5119270841813) + + k * (-21.86122147463605 * k - 189.48180835922747) + 255; + var g = + c * (8.841041422036149 * c + 60.118027045597366 * m + + 6.871425592049007 * y + 31.159100130055922 * k + + -79.2970844816548) + + m * (-15.310361306967817 * m + 17.575251261109482 * y + + 131.35250912493976 * k - 190.9453302588951) + + y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + + k * (-20.737325471181034 * k - 187.80453709719578) + 255; + var b = + c * (0.8842522430003296 * c + 8.078677503112928 * m + + 30.89978309703729 * y - 0.23883238689178934 * k + + -14.183576799673286) + + m * (10.49593273432072 * m + 63.02378494754052 * y + + 50.606957656360734 * k - 112.23884253719248) + + y * (0.03296041114873217 * y + 115.60384449646641 * k + + -193.58209356861505) + + k * (-22.33816807309886 * k - 180.12613974708367) + 255; + + dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r; + dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g; + dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b; + } + + function DeviceCmykCS() { + this.name = 'DeviceCMYK'; + this.numComps = 4; + this.defaultColor = new Float32Array([0, 0, 0, 1]); + } + DeviceCmykCS.prototype = { + getRgb: function DeviceCmykCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + convertToRgb(src, srcOffset, 1, rgb, 0); + return rgb; + }, + getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset, + dest, destOffset) { + convertToRgb(src, srcOffset, 1, dest, destOffset); + }, + getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + var scale = 1 / ((1 << bits) - 1); + for (var i = 0; i < count; i++) { + convertToRgb(src, srcOffset, scale, dest, destOffset); + srcOffset += 4; + destOffset += 3 + alpha01; + } + }, + getOutputLength: function DeviceCmykCS_getOutputLength(inputLength, + alpha01) { + return (inputLength / 4 * (3 + alpha01)) | 0; + }, + isPassthrough: ColorSpace.prototype.isPassthrough, + fillRgb: ColorSpace.prototype.fillRgb, + isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) { + return ColorSpace.isDefaultDecode(decodeMap, this.numComps); + }, + usesZeroToOneRange: true + }; + + return DeviceCmykCS; +})(); + +// +// CalGrayCS: Based on "PDF Reference, Sixth Ed", p.245 +// +var CalGrayCS = (function CalGrayCSClosure() { + function CalGrayCS(whitePoint, blackPoint, gamma) { + this.name = 'CalGray'; + this.numComps = 1; + this.defaultColor = new Float32Array([0]); + + if (!whitePoint) { + error('WhitePoint missing - required for color space CalGray'); + } + blackPoint = blackPoint || [0, 0, 0]; + gamma = gamma || 1; + + // Translate arguments to spec variables. + this.XW = whitePoint[0]; + this.YW = whitePoint[1]; + this.ZW = whitePoint[2]; + + this.XB = blackPoint[0]; + this.YB = blackPoint[1]; + this.ZB = blackPoint[2]; + + this.G = gamma; + + // Validate variables as per spec. + if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) { + error('Invalid WhitePoint components for ' + this.name + + ', no fallback available'); + } + + if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { + info('Invalid BlackPoint for ' + this.name + ', falling back to default'); + this.XB = this.YB = this.ZB = 0; + } + + if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) { + warn(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB + + ', ZB: ' + this.ZB + ', only default values are supported.'); + } + + if (this.G < 1) { + info('Invalid Gamma: ' + this.G + ' for ' + this.name + + ', falling back to default'); + this.G = 1; + } + } + + 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. + // 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; + } + + CalGrayCS.prototype = { + getRgb: function CalGrayCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + this.getRgbItem(src, srcOffset, rgb, 0); + return rgb; + }, + getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, + dest, destOffset) { + convertToRgb(this, src, srcOffset, dest, destOffset, 1); + }, + getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + var scale = 1 / ((1 << bits) - 1); + + for (var i = 0; i < count; ++i) { + convertToRgb(this, src, srcOffset, dest, destOffset, scale); + srcOffset += 1; + destOffset += 3 + alpha01; + } + }, + getOutputLength: function CalGrayCS_getOutputLength(inputLength, alpha01) { + return inputLength * (3 + alpha01); + }, + isPassthrough: ColorSpace.prototype.isPassthrough, + fillRgb: ColorSpace.prototype.fillRgb, + isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) { + return ColorSpace.isDefaultDecode(decodeMap, this.numComps); + }, + usesZeroToOneRange: true + }; + return CalGrayCS; +})(); + +// +// LabCS: Based on "PDF Reference, Sixth Ed", p.250 +// +var LabCS = (function LabCSClosure() { + function LabCS(whitePoint, blackPoint, range) { + this.name = 'Lab'; + this.numComps = 3; + this.defaultColor = new Float32Array([0, 0, 0]); + + if (!whitePoint) + error('WhitePoint missing - required for color space Lab'); + blackPoint = blackPoint || [0, 0, 0]; + range = range || [-100, 100, -100, 100]; + + // Translate args to spec variables + this.XW = whitePoint[0]; + this.YW = whitePoint[1]; + this.ZW = whitePoint[2]; + this.amin = range[0]; + this.amax = range[1]; + this.bmin = range[2]; + this.bmax = range[3]; + + // These are here just for completeness - the spec doesn't offer any + // formulas that use BlackPoint in Lab + this.XB = blackPoint[0]; + this.YB = blackPoint[1]; + this.ZB = blackPoint[2]; + + // Validate vars as per spec + if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) + error('Invalid WhitePoint components, no fallback available'); + + if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { + info('Invalid BlackPoint, falling back to default'); + this.XB = this.YB = this.ZB = 0; + } + + if (this.amin > this.amax || this.bmin > this.bmax) { + info('Invalid Range, falling back to defaults'); + this.amin = -100; + this.amax = 100; + this.bmin = -100; + this.bmax = 100; + } + } + + // Function g(x) from spec + function fn_g(x) { + if (x >= 6 / 29) + return x * x * x; + else + return (108 / 841) * (x - 4 / 29); + } + + function decode(value, high1, low2, high2) { + return low2 + (value) * (high2 - low2) / (high1); + } + + // If decoding is needed maxVal should be 2^bits per component - 1. + function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) { + // XXX: Lab input is in the range of [0, 100], [amin, amax], [bmin, bmax] + // not the usual [0, 1]. If a command like setFillColor is used the src + // values will already be within the correct range. However, if we are + // converting an image we have to map the values to the correct range given + // above. + // Ls,as,bs <---> L*,a*,b* in the spec + var Ls = src[srcOffset]; + var as = src[srcOffset + 1]; + var bs = src[srcOffset + 2]; + if (maxVal !== false) { + Ls = decode(Ls, maxVal, 0, 100); + as = decode(as, maxVal, cs.amin, cs.amax); + bs = decode(bs, maxVal, cs.bmin, cs.bmax); + } + + // Adjust limits of 'as' and 'bs' + as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as; + bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs; + + // Computes intermediate variables X,Y,Z as per spec + var M = (Ls + 16) / 116; + var L = M + (as / 500); + var N = M - (bs / 200); + + var X = cs.XW * fn_g(L); + var Y = cs.YW * fn_g(M); + var Z = cs.ZW * fn_g(N); + + var r, g, b; + // Using different conversions for D50 and D65 white points, + // per http://www.color.org/srgb.pdf + if (cs.ZW < 1) { + // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249) + r = X * 3.1339 + Y * -1.6170 + Z * -0.4906; + g = X * -0.9785 + Y * 1.9160 + Z * 0.0333; + b = X * 0.0720 + Y * -0.2290 + Z * 1.4057; + } else { + // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888) + r = X * 3.2406 + Y * -1.5372 + Z * -0.4986; + g = X * -0.9689 + Y * 1.8758 + Z * 0.0415; + b = X * 0.0557 + Y * -0.2040 + Z * 1.0570; + } + // clamp color values to [0,1] range then convert to [0,255] range. + dest[destOffset] = Math.sqrt(r < 0 ? 0 : r > 1 ? 1 : r) * 255; + dest[destOffset + 1] = Math.sqrt(g < 0 ? 0 : g > 1 ? 1 : g) * 255; + dest[destOffset + 2] = Math.sqrt(b < 0 ? 0 : b > 1 ? 1 : b) * 255; + } + + LabCS.prototype = { + getRgb: function LabCS_getRgb(src, srcOffset) { + var rgb = new Uint8Array(3); + convertToRgb(this, src, srcOffset, false, rgb, 0); + return rgb; + }, + getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) { + convertToRgb(this, src, srcOffset, false, dest, destOffset); + }, + getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count, + dest, destOffset, bits, + alpha01) { + var maxVal = (1 << bits) - 1; + for (var i = 0; i < count; i++) { + convertToRgb(this, src, srcOffset, maxVal, dest, destOffset); + srcOffset += 3; + destOffset += 3 + alpha01; + } + }, + getOutputLength: function LabCS_getOutputLength(inputLength, alpha01) { + return (inputLength * (3 + alpha01) / 3) | 0; + }, + isPassthrough: ColorSpace.prototype.isPassthrough, + isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) { + // XXX: Decoding is handled with the lab conversion because of the strange + // ranges that are used. + return true; + }, + usesZeroToOneRange: false + }; + return LabCS; +})(); + + + var PatternType = { AXIAL: 2, RADIAL: 3 @@ -1291,7 +2040,7 @@ var Pattern = (function PatternClosure() { // Both radial and axial shadings are handled by RadialAxial shading. return new Shadings.RadialAxial(dict, matrix, xref, res); default: - TODO('Unsupported shading type: ' + type); + UnsupportedManager.notify(UNSUPPORTED_FEATURES.shadingPattern); return new Shadings.Dummy(); } }; @@ -1551,7 +2300,7 @@ var TilingPattern = (function TilingPatternClosure() { var commonObjs = this.commonObjs; var ctx = this.ctx; - TODO('TilingType: ' + tilingType); + info('TilingType: ' + tilingType); var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3]; @@ -1764,7 +2513,7 @@ var PDFFunction = (function PDFFunctionClosure() { if (order !== 1) { // No description how cubic spline interpolation works in PDF32000:2008 // As in poppler, ignoring order, linear interpolation may work as good - TODO('No support for cubic spline interpolation: ' + order); + info('No support for cubic spline interpolation: ' + order); } var encode = dict.get('Encode'); @@ -2613,6 +3362,29 @@ var Annotation = (function AnnotationClosure() { } else { var borderArray = dict.get('Border') || [0, 0, 1]; data.borderWidth = borderArray[2] || 0; + + // TODO: implement proper support for annotations with line dash patterns. + var dashArray = borderArray[3]; + if (data.borderWidth > 0 && dashArray && isArray(dashArray)) { + var dashArrayLength = dashArray.length; + if (dashArrayLength > 0) { + // According to the PDF specification: the elements in a dashArray + // shall be numbers that are nonnegative and not all equal to zero. + var isInvalid = false; + var numPositive = 0; + for (var i = 0; i < dashArrayLength; i++) { + if (!(+dashArray[i] >= 0)) { + isInvalid = true; + break; + } else if (dashArray[i] > 0) { + numPositive++; + } + } + if (isInvalid || numPositive === 0) { + data.borderWidth = 0; + } + } + } } this.appearance = getDefaultAppearance(dict); @@ -2657,7 +3429,7 @@ var Annotation = (function AnnotationClosure() { }, loadResources: function(keys) { - var promise = new Promise(); + var promise = new LegacyPromise(); this.appearance.dict.getAsync('Resources').then(function(resources) { if (!resources) { promise.resolve(); @@ -2676,7 +3448,7 @@ var Annotation = (function AnnotationClosure() { getOperatorList: function Annotation_getToOperatorList(evaluator) { - var promise = new Promise(); + var promise = new LegacyPromise(); if (!this.appearance) { promise.resolve(new OperatorList()); @@ -2782,7 +3554,7 @@ var Annotation = (function AnnotationClosure() { if (annotation.isViewable()) { return annotation; } else { - TODO('unimplemented annotation type: ' + subtype); + warn('unimplemented annotation type: ' + subtype); } }; @@ -2793,7 +3565,7 @@ var Annotation = (function AnnotationClosure() { annotationsReadyPromise.reject(e); } - var annotationsReadyPromise = new Promise(); + var annotationsReadyPromise = new LegacyPromise(); var annotationPromises = []; for (var i = 0, n = annotations.length; i < n; ++i) { @@ -2874,7 +3646,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { Util.inherit(WidgetAnnotation, Annotation, { isViewable: function WidgetAnnotation_isViewable() { if (this.data.fieldType === 'Sig') { - TODO('unimplemented annotation type: Widget signature'); + warn('unimplemented annotation type: Widget signature'); return false; } @@ -2955,7 +3727,7 @@ var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() { return Annotation.prototype.getOperatorList.call(this, evaluator); } - var promise = new Promise(); + var promise = new LegacyPromise(); var opList = new OperatorList(); var data = this.data; @@ -3032,7 +3804,7 @@ var TextAnnotation = (function TextAnnotationClosure() { Util.inherit(TextAnnotation, Annotation, { getOperatorList: function TextAnnotation_getOperatorList(evaluator) { - var promise = new Promise(); + var promise = new LegacyPromise(); promise.resolve(new OperatorList()); return promise; }, @@ -3132,7 +3904,13 @@ var LinkAnnotation = (function LinkAnnotationClosure() { if (action) { var linkType = action.get('S').name; if (linkType === 'URI') { - var url = addDefaultProtocolToUrl(action.get('URI')); + var url = action.get('URI'); + if (isName(url)) { + // Some bad PDFs do not put parentheses around relative URLs. + url = '/' + url.name; + } else { + url = addDefaultProtocolToUrl(url); + } // TODO: pdf spec mentions urls can be relative to a Base // entry in the dictionary. if (!isValidUrl(url, false)) { @@ -3159,7 +3937,7 @@ var LinkAnnotation = (function LinkAnnotationClosure() { } else if (linkType === 'Named') { data.action = action.get('N').name; } else { - TODO('unrecognized link type: ' + linkType); + warn('unrecognized link type: ' + linkType); } } else if (dict.has('Dest')) { // simple destination link @@ -3432,7 +4210,8 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { }; this.networkManager = new NetworkManager(this.url, { getXhr: getXhr, - httpHeaders: args.httpHeaders + httpHeaders: args.httpHeaders, + withCredentials: args.withCredentials }); this.sendRequest = function ChunkedStreamManager_sendRequest(begin, end) { this.networkManager.requestRange(begin, end, { @@ -3448,7 +4227,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { this.requestsByChunk = {}; this.callbacksByRequest = {}; - this.loadedStream = new Promise(); + this.loadedStream = new LegacyPromise(); if (args.initialData) { this.setInitialData(args.initialData); } @@ -3759,7 +4538,7 @@ var LocalPdfManager = (function LocalPdfManagerClosure() { function LocalPdfManager(data, password) { var stream = new Stream(data); this.pdfModel = new PDFDocument(this, stream, password); - this.loadedStream = new Promise(); + this.loadedStream = new LegacyPromise(); this.loadedStream.resolve(stream); } @@ -3768,7 +4547,7 @@ var LocalPdfManager = (function LocalPdfManagerClosure() { LocalPdfManager.prototype.ensure = function LocalPdfManager_ensure(obj, prop, args) { - var promise = new Promise(); + var promise = new LegacyPromise(); try { var value = obj[prop]; var result; @@ -3787,7 +4566,7 @@ var LocalPdfManager = (function LocalPdfManagerClosure() { LocalPdfManager.prototype.requestRange = function LocalPdfManager_requestRange(begin, end) { - var promise = new Promise(); + var promise = new LegacyPromise(); promise.resolve(); return promise; }; @@ -3820,6 +4599,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { var params = { msgHandler: msgHandler, httpHeaders: args.httpHeaders, + withCredentials: args.withCredentials, chunkedViewerLoading: args.chunkedViewerLoading, disableAutoFetch: args.disableAutoFetch, initialData: args.initialData @@ -3836,7 +4616,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { NetworkPdfManager.prototype.ensure = function NetworkPdfManager_ensure(obj, prop, args) { - var promise = new Promise(); + var promise = new LegacyPromise(); this.ensureHelper(promise, obj, prop, args); return promise; }; @@ -3867,7 +4647,7 @@ var NetworkPdfManager = (function NetworkPdfManagerClosure() { NetworkPdfManager.prototype.requestRange = function NetworkPdfManager_requestRange(begin, end) { - var promise = new Promise(); + var promise = new LegacyPromise(); this.streamManager.requestRange(begin, end, function() { promise.resolve(); }); @@ -3994,7 +4774,7 @@ var Page = (function PageClosure() { // TODO: add async inheritPageProp and remove this. this.resourcesPromise = this.pdfManager.ensure(this, 'resources'); } - var promise = new Promise(); + var promise = new LegacyPromise(); this.resourcesPromise.then(function resourceSuccess() { var objectLoader = new ObjectLoader(this.resources.map, keys, @@ -4007,13 +4787,13 @@ var Page = (function PageClosure() { }, getOperatorList: function Page_getOperatorList(handler) { var self = this; - var promise = new Promise(); + var promise = new LegacyPromise(); function reject(e) { promise.reject(e); } - var pageListPromise = new Promise(); + var pageListPromise = new LegacyPromise(); var pdfManager = this.pdfManager; var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', @@ -4079,7 +4859,7 @@ var Page = (function PageClosure() { var self = this; - var textContentPromise = new Promise(); + var textContentPromise = new LegacyPromise(); var pdfManager = this.pdfManager; var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', @@ -4454,7 +5234,7 @@ var Dict = (function DictClosure() { if (xref) { return xref.fetchIfRefAsync(value); } - promise = new Promise(); + promise = new LegacyPromise(); promise.resolve(value); return promise; } @@ -4463,7 +5243,7 @@ var Dict = (function DictClosure() { if (xref) { return xref.fetchIfRefAsync(value); } - promise = new Promise(); + promise = new LegacyPromise(); promise.resolve(value); return promise; } @@ -4471,7 +5251,7 @@ var Dict = (function DictClosure() { if (xref) { return xref.fetchIfRefAsync(value); } - promise = new Promise(); + promise = new LegacyPromise(); promise.resolve(value); return promise; }, @@ -4791,7 +5571,7 @@ var Catalog = (function CatalogClosure() { }, getPageDict: function Catalog_getPageDict(pageIndex) { - var promise = new Promise(); + var promise = new LegacyPromise(); var nodesToVisit = [this.catDict.getRaw('Pages')]; var currentPageIndex = 0; var xref = this.xref; @@ -5356,7 +6136,7 @@ var XRef = (function XRefClosure() { if (e instanceof MissingDataException) { throw e; } - log('(while reading XRef): ' + e); + info('(while reading XRef): ' + e); } if (recoveryMode) @@ -5477,14 +6257,14 @@ var XRef = (function XRefClosure() { }, fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) { if (!isRef(obj)) { - var promise = new Promise(); + var promise = new LegacyPromise(); promise.resolve(obj); return promise; } return this.fetchAsync(obj); }, fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) { - var promise = new Promise(); + var promise = new LegacyPromise(); var tryFetch = function (promise) { try { promise.resolve(this.fetch(ref, suppressEncryption)); @@ -5611,7 +6391,7 @@ var ObjectLoader = (function() { load: function ObjectLoader_load() { var keys = this.keys; - this.promise = new Promise(); + this.promise = new LegacyPromise(); // Don't walk the graph if all the data is already loaded. if (!(this.xref.stream instanceof ChunkedStream) || this.xref.stream.getMissingChunks().length === 0) { @@ -12738,870 +13518,6 @@ var CIDToUnicodeMaps = { -var ColorSpace = (function ColorSpaceClosure() { - // Constructor should define this.numComps, this.defaultColor, this.name - function ColorSpace() { - error('should not call ColorSpace constructor'); - } - - ColorSpace.prototype = { - /** - * Converts the color value to the RGB color. The color components are - * located in the src array starting from the srcOffset. Returns the array - * of the rgb components, each value ranging from [0,255]. - */ - getRgb: function ColorSpace_getRgb(src, srcOffset) { - error('Should not call ColorSpace.getRgb'); - }, - /** - * Converts the color value to the RGB color, similar to the getRgb method. - * The result placed into the dest array starting from the destOffset. - */ - getRgbItem: function ColorSpace_getRgb(src, srcOffset, dest, destOffset) { - error('Should not call ColorSpace.getRgbItem'); - }, - /** - * Converts the specified number of the color values to the RGB colors. - * The colors are located in the src array starting from the srcOffset. - * The result is placed into the dest array starting from the destOffset. - * The src array items shall be in [0,2^bits) range, the dest array items - * will be in [0,255] range. - */ - getRgbBuffer: function ColorSpace_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - error('Should not call ColorSpace.getRgbBuffer'); - }, - /** - * Determines amount of the bytes is required to store the reslut of the - * conversion that done by the getRgbBuffer method. - */ - getOutputLength: function ColorSpace_getOutputLength(inputLength) { - error('Should not call ColorSpace.getOutputLength'); - }, - /** - * Returns true if source data will be equal the result/output data. - */ - isPassthrough: function ColorSpace_isPassthrough(bits) { - return false; - }, - /** - * Creates the output buffer and converts the specified number of the color - * values to the RGB colors, similar to the getRgbBuffer. - */ - createRgbBuffer: function ColorSpace_createRgbBuffer(src, srcOffset, - count, bits) { - if (this.isPassthrough(bits)) { - return src.subarray(srcOffset); - } - var dest = new Uint8Array(count * 3); - var numComponentColors = 1 << bits; - // Optimization: create a color map when there is just one component and - // we are converting more colors than the size of the color map. We - // don't build the map if the colorspace is gray or rgb since those - // methods are faster than building a map. This mainly offers big speed - // ups for indexed and alternate colorspaces. - if (this.numComps === 1 && count > numComponentColors && - this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') { - // TODO it may be worth while to cache the color map. While running - // testing I never hit a cache so I will leave that out for now (perhaps - // we are reparsing colorspaces too much?). - var allColors = bits <= 8 ? new Uint8Array(numComponentColors) : - new Uint16Array(numComponentColors); - for (var i = 0; i < numComponentColors; i++) { - allColors[i] = i; - } - var colorMap = new Uint8Array(numComponentColors * 3); - this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bits); - - var destOffset = 0; - for (var i = 0; i < count; ++i) { - var key = src[srcOffset++] * 3; - dest[destOffset++] = colorMap[key]; - dest[destOffset++] = colorMap[key + 1]; - dest[destOffset++] = colorMap[key + 2]; - } - return dest; - } - this.getRgbBuffer(src, srcOffset, count, dest, 0, bits); - return dest; - }, - /** - * 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]. - */ - usesZeroToOneRange: true - }; - - ColorSpace.parse = function ColorSpace_parse(cs, xref, res) { - var IR = ColorSpace.parseToIR(cs, xref, res); - if (IR instanceof AlternateCS) - return IR; - - return ColorSpace.fromIR(IR); - }; - - ColorSpace.fromIR = function ColorSpace_fromIR(IR) { - var name = isArray(IR) ? IR[0] : IR; - - switch (name) { - case 'DeviceGrayCS': - return this.singletons.gray; - case 'DeviceRgbCS': - return this.singletons.rgb; - case 'DeviceCmykCS': - return this.singletons.cmyk; - case 'CalGrayCS': - var whitePoint = IR[1].WhitePoint; - var blackPoint = IR[1].BlackPoint; - var gamma = IR[1].Gamma; - return new CalGrayCS(whitePoint, blackPoint, gamma); - case 'PatternCS': - var basePatternCS = IR[1]; - if (basePatternCS) - basePatternCS = ColorSpace.fromIR(basePatternCS); - return new PatternCS(basePatternCS); - case 'IndexedCS': - var baseIndexedCS = IR[1]; - var hiVal = IR[2]; - var lookup = IR[3]; - return new IndexedCS(ColorSpace.fromIR(baseIndexedCS), hiVal, lookup); - case 'AlternateCS': - var numComps = IR[1]; - var alt = IR[2]; - var tintFnIR = IR[3]; - - return new AlternateCS(numComps, ColorSpace.fromIR(alt), - PDFFunction.fromIR(tintFnIR)); - case 'LabCS': - var whitePoint = IR[1].WhitePoint; - var blackPoint = IR[1].BlackPoint; - var range = IR[1].Range; - return new LabCS(whitePoint, blackPoint, range); - default: - error('Unkown name ' + name); - } - return null; - }; - - ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) { - if (isName(cs)) { - var colorSpaces = res.get('ColorSpace'); - if (isDict(colorSpaces)) { - var refcs = colorSpaces.get(cs.name); - if (refcs) - cs = refcs; - } - } - - cs = xref.fetchIfRef(cs); - var mode; - - if (isName(cs)) { - mode = cs.name; - this.mode = mode; - - switch (mode) { - case 'DeviceGray': - case 'G': - return 'DeviceGrayCS'; - case 'DeviceRGB': - case 'RGB': - return 'DeviceRgbCS'; - case 'DeviceCMYK': - case 'CMYK': - return 'DeviceCmykCS'; - case 'Pattern': - return ['PatternCS', null]; - default: - error('unrecognized colorspace ' + mode); - } - } else if (isArray(cs)) { - mode = cs[0].name; - this.mode = mode; - - switch (mode) { - case 'DeviceGray': - case 'G': - return 'DeviceGrayCS'; - case 'DeviceRGB': - case 'RGB': - return 'DeviceRgbCS'; - case 'DeviceCMYK': - case 'CMYK': - return 'DeviceCmykCS'; - case 'CalGray': - var params = cs[1].getAll(); - return ['CalGrayCS', params]; - case 'CalRGB': - return 'DeviceRgbCS'; - case 'ICCBased': - var stream = xref.fetchIfRef(cs[1]); - var dict = stream.dict; - var numComps = dict.get('N'); - if (numComps == 1) - return 'DeviceGrayCS'; - if (numComps == 3) - return 'DeviceRgbCS'; - if (numComps == 4) - return 'DeviceCmykCS'; - break; - case 'Pattern': - var basePatternCS = cs[1]; - if (basePatternCS) - basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res); - return ['PatternCS', basePatternCS]; - case 'Indexed': - case 'I': - var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res); - var hiVal = cs[2] + 1; - var lookup = xref.fetchIfRef(cs[3]); - if (isStream(lookup)) { - lookup = lookup.getBytes(); - } - return ['IndexedCS', baseIndexedCS, hiVal, lookup]; - case 'Separation': - case 'DeviceN': - var name = cs[1]; - var numComps = 1; - if (isName(name)) - numComps = 1; - else if (isArray(name)) - numComps = name.length; - var alt = ColorSpace.parseToIR(cs[2], xref, res); - var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); - return ['AlternateCS', numComps, alt, tintFnIR]; - case 'Lab': - var params = cs[1].getAll(); - return ['LabCS', params]; - default: - error('unimplemented color space object "' + mode + '"'); - } - } else { - error('unrecognized color space object: "' + cs + '"'); - } - return null; - }; - /** - * Checks if a decode map matches the default decode map for a color space. - * This handles the general decode maps where there are two values per - * component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color. - * This does not handle Lab, Indexed, or Pattern decode maps since they are - * slightly different. - * @param {Array} decode Decode map (usually from an image). - * @param {Number} n Number of components the color space has. - */ - ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) { - if (!decode) - return true; - - if (n * 2 !== decode.length) { - warn('The decode map is not the correct length'); - return true; - } - for (var i = 0, ii = decode.length; i < ii; i += 2) { - if (decode[i] !== 0 || decode[i + 1] != 1) - return false; - } - return true; - }; - - ColorSpace.singletons = { - get gray() { - return shadow(this, 'gray', new DeviceGrayCS()); - }, - get rgb() { - return shadow(this, 'rgb', new DeviceRgbCS()); - }, - get cmyk() { - return shadow(this, 'cmyk', new DeviceCmykCS()); - } - }; - - return ColorSpace; -})(); - -/** - * Alternate color space handles both Separation and DeviceN color spaces. A - * Separation color space is actually just a DeviceN with one color component. - * Both color spaces use a tinting function to convert colors to a base color - * space. - */ -var AlternateCS = (function AlternateCSClosure() { - function AlternateCS(numComps, base, tintFn) { - this.name = 'Alternate'; - this.numComps = numComps; - this.defaultColor = new Float32Array(numComps); - for (var i = 0; i < numComps; ++i) { - this.defaultColor[i] = 1; - } - this.base = base; - this.tintFn = tintFn; - } - - AlternateCS.prototype = { - getRgb: function AlternateCS_getRgb(src, srcOffset) { - var rgb = new Uint8Array(3); - this.getRgbItem(src, srcOffset, rgb, 0); - return rgb; - }, - getRgbItem: function AlternateCS_getRgbItem(src, srcOffset, - dest, destOffset) { - var baseNumComps = this.base.numComps; - var input = 'subarray' in src ? - src.subarray(srcOffset, srcOffset + this.numComps) : - Array.prototype.slice.call(src, srcOffset, srcOffset + this.numComps); - var tinted = this.tintFn(input); - this.base.getRgbItem(tinted, 0, dest, destOffset); - }, - getRgbBuffer: function AlternateCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - var tintFn = this.tintFn; - var base = this.base; - var scale = 1 / ((1 << bits) - 1); - var baseNumComps = base.numComps; - var usesZeroToOneRange = base.usesZeroToOneRange; - var isPassthrough = base.isPassthrough(8) || !usesZeroToOneRange; - var pos = isPassthrough ? destOffset : 0; - var baseBuf = isPassthrough ? dest : new Uint8Array(baseNumComps * count); - var numComps = this.numComps; - - var scaled = new Float32Array(numComps); - for (var i = 0; i < count; i++) { - for (var j = 0; j < numComps; j++) { - scaled[j] = src[srcOffset++] * scale; - } - var tinted = tintFn(scaled); - if (usesZeroToOneRange) { - for (var j = 0; j < baseNumComps; j++) { - baseBuf[pos++] = tinted[j] * 255; - } - } else { - base.getRgbItem(tinted, 0, baseBuf, pos); - pos += baseNumComps; - } - } - if (!isPassthrough) { - base.getRgbBuffer(baseBuf, 0, count, dest, destOffset, 8); - } - }, - getOutputLength: function AlternateCS_getOutputLength(inputLength) { - return this.base.getOutputLength(inputLength * - this.base.numComps / this.numComps); - }, - isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, - isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) { - return ColorSpace.isDefaultDecode(decodeMap, this.numComps); - }, - usesZeroToOneRange: true - }; - - return AlternateCS; -})(); - -var PatternCS = (function PatternCSClosure() { - function PatternCS(baseCS) { - this.name = 'Pattern'; - this.base = baseCS; - } - PatternCS.prototype = {}; - - return PatternCS; -})(); - -var IndexedCS = (function IndexedCSClosure() { - function IndexedCS(base, highVal, lookup) { - this.name = 'Indexed'; - this.numComps = 1; - this.defaultColor = new Uint8Array([0]); - this.base = base; - this.highVal = highVal; - - var baseNumComps = base.numComps; - var length = baseNumComps * highVal; - var lookupArray; - - if (isStream(lookup)) { - lookupArray = new Uint8Array(length); - var bytes = lookup.getBytes(length); - lookupArray.set(bytes); - } else if (isString(lookup)) { - lookupArray = new Uint8Array(length); - for (var i = 0; i < length; ++i) - lookupArray[i] = lookup.charCodeAt(i); - } else if (lookup instanceof Uint8Array || lookup instanceof Array) { - lookupArray = lookup; - } else { - error('Unrecognized lookup table: ' + lookup); - } - this.lookup = lookupArray; - } - - IndexedCS.prototype = { - getRgb: function IndexedCS_getRgb(src, srcOffset) { - var numComps = this.base.numComps; - var start = src[srcOffset] * numComps; - return this.base.getRgb(this.lookup, start); - }, - getRgbItem: function IndexedCS_getRgbItem(src, srcOffset, - dest, destOffset) { - var numComps = this.base.numComps; - var start = src[srcOffset] * numComps; - this.base.getRgbItem(this.lookup, start, dest, destOffset); - }, - getRgbBuffer: function IndexedCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset) { - var base = this.base; - var numComps = base.numComps; - var outputDelta = base.getOutputLength(numComps); - var lookup = this.lookup; - - for (var i = 0; i < count; ++i) { - var lookupPos = src[srcOffset++] * numComps; - base.getRgbBuffer(lookup, lookupPos, 1, dest, destOffset, 8); - destOffset += outputDelta; - } - }, - getOutputLength: function IndexedCS_getOutputLength(inputLength) { - return this.base.getOutputLength(inputLength * this.base.numComps); - }, - isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, - isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) { - // indexed color maps shouldn't be changed - return true; - }, - usesZeroToOneRange: true - }; - return IndexedCS; -})(); - -var DeviceGrayCS = (function DeviceGrayCSClosure() { - function DeviceGrayCS() { - this.name = 'DeviceGray'; - this.numComps = 1; - this.defaultColor = new Float32Array([0]); - } - - DeviceGrayCS.prototype = { - getRgb: function DeviceGrayCS_getRgb(src, srcOffset) { - var rgb = new Uint8Array(3); - this.getRgbItem(src, srcOffset, rgb, 0); - return rgb; - }, - getRgbItem: function DeviceGrayCS_getRgbItem(src, srcOffset, - dest, destOffset) { - var c = (src[srcOffset] * 255) | 0; - c = c < 0 ? 0 : c > 255 ? 255 : c; - dest[destOffset] = dest[destOffset + 1] = dest[destOffset + 2] = c; - }, - getRgbBuffer: function DeviceGrayCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - var scale = 255 / ((1 << bits) - 1); - var j = srcOffset, q = destOffset; - for (var i = 0; i < count; ++i) { - var c = (scale * src[j++]) | 0; - dest[q++] = c; - dest[q++] = c; - dest[q++] = c; - } - }, - getOutputLength: function DeviceGrayCS_getOutputLength(inputLength) { - return inputLength * 3; - }, - isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, - isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) { - return ColorSpace.isDefaultDecode(decodeMap, this.numComps); - }, - usesZeroToOneRange: true - }; - return DeviceGrayCS; -})(); - -var DeviceRgbCS = (function DeviceRgbCSClosure() { - function DeviceRgbCS() { - this.name = 'DeviceRGB'; - this.numComps = 3; - this.defaultColor = new Float32Array([0, 0, 0]); - } - DeviceRgbCS.prototype = { - getRgb: function DeviceRgbCS_getRgb(src, srcOffset) { - var rgb = new Uint8Array(3); - this.getRgbItem(src, srcOffset, rgb, 0); - return rgb; - }, - getRgbItem: function DeviceRgbCS_getRgbItem(src, srcOffset, - dest, destOffset) { - var r = (src[srcOffset] * 255) | 0; - var g = (src[srcOffset + 1] * 255) | 0; - var b = (src[srcOffset + 2] * 255) | 0; - dest[destOffset] = r < 0 ? 0 : r > 255 ? 255 : r; - dest[destOffset + 1] = g < 0 ? 0 : g > 255 ? 255 : g; - dest[destOffset + 2] = b < 0 ? 0 : b > 255 ? 255 : b; - }, - getRgbBuffer: function DeviceRgbCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - var length = count * 3; - if (bits == 8) { - dest.set(src.subarray(srcOffset, srcOffset + length), destOffset); - return; - } - var scale = 255 / ((1 << bits) - 1); - var j = srcOffset, q = destOffset; - for (var i = 0; i < length; ++i) { - dest[q++] = (scale * src[j++]) | 0; - } - }, - getOutputLength: function DeviceRgbCS_getOutputLength(inputLength) { - return inputLength; - }, - isPassthrough: function DeviceRgbCS_isPassthrough(bits) { - return bits == 8; - }, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, - isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) { - return ColorSpace.isDefaultDecode(decodeMap, this.numComps); - }, - usesZeroToOneRange: true - }; - return DeviceRgbCS; -})(); - -var DeviceCmykCS = (function DeviceCmykCSClosure() { - // The coefficients below was found using numerical analysis: the method of - // steepest descent for the sum((f_i - color_value_i)^2) for r/g/b colors, - // where color_value is the tabular value from the table of sampled RGB colors - // from CMYK US Web Coated (SWOP) colorspace, and f_i is the corresponding - // CMYK color conversion using the estimation below: - // f(A, B,.. N) = Acc+Bcm+Ccy+Dck+c+Fmm+Gmy+Hmk+Im+Jyy+Kyk+Ly+Mkk+Nk+255 - function convertToRgb(src, srcOffset, srcScale, dest, destOffset) { - var c = src[srcOffset + 0] * srcScale; - var m = src[srcOffset + 1] * srcScale; - var y = src[srcOffset + 2] * srcScale; - var k = src[srcOffset + 3] * srcScale; - - var r = - c * (-4.387332384609988 * c + 54.48615194189176 * m + - 18.82290502165302 * y + 212.25662451639585 * k + - -285.2331026137004) + - m * (1.7149763477362134 * m - 5.6096736904047315 * y + - -17.873870861415444 * k - 5.497006427196366) + - y * (-2.5217340131683033 * y - 21.248923337353073 * k + - 17.5119270841813) + - k * (-21.86122147463605 * k - 189.48180835922747) + 255; - var g = - c * (8.841041422036149 * c + 60.118027045597366 * m + - 6.871425592049007 * y + 31.159100130055922 * k + - -79.2970844816548) + - m * (-15.310361306967817 * m + 17.575251261109482 * y + - 131.35250912493976 * k - 190.9453302588951) + - y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) + - k * (-20.737325471181034 * k - 187.80453709719578) + 255; - var b = - c * (0.8842522430003296 * c + 8.078677503112928 * m + - 30.89978309703729 * y - 0.23883238689178934 * k + - -14.183576799673286) + - m * (10.49593273432072 * m + 63.02378494754052 * y + - 50.606957656360734 * k - 112.23884253719248) + - y * (0.03296041114873217 * y + 115.60384449646641 * k + - -193.58209356861505) + - k * (-22.33816807309886 * k - 180.12613974708367) + 255; - - dest[destOffset] = r > 255 ? 255 : r < 0 ? 0 : r; - dest[destOffset + 1] = g > 255 ? 255 : g < 0 ? 0 : g; - dest[destOffset + 2] = b > 255 ? 255 : b < 0 ? 0 : b; - } - - function DeviceCmykCS() { - this.name = 'DeviceCMYK'; - this.numComps = 4; - this.defaultColor = new Float32Array([0, 0, 0, 1]); - } - DeviceCmykCS.prototype = { - getRgb: function DeviceCmykCS_getRgb(src, srcOffset) { - var rgb = new Uint8Array(3); - convertToRgb(src, srcOffset, 1, rgb, 0); - return rgb; - }, - getRgbItem: function DeviceCmykCS_getRgbItem(src, srcOffset, - dest, destOffset) { - convertToRgb(src, srcOffset, 1, dest, destOffset); - }, - getRgbBuffer: function DeviceCmykCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - var scale = 1 / ((1 << bits) - 1); - for (var i = 0; i < count; i++) { - convertToRgb(src, srcOffset, scale, dest, destOffset); - srcOffset += 4; - destOffset += 3; - } - }, - getOutputLength: function DeviceCmykCS_getOutputLength(inputLength) { - return (inputLength >> 2) * 3; - }, - isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, - isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) { - return ColorSpace.isDefaultDecode(decodeMap, this.numComps); - }, - usesZeroToOneRange: true - }; - - return DeviceCmykCS; -})(); - -// -// CalGrayCS: Based on "PDF Reference, Sixth Ed", p.245 -// -var CalGrayCS = (function CalGrayCSClosure() { - function CalGrayCS(whitePoint, blackPoint, gamma) { - this.name = 'CalGray'; - this.numComps = 3; - this.defaultColor = new Float32Array([0, 0, 0]); - - if (!whitePoint) { - error('WhitePoint missing - required for color space CalGray'); - } - blackPoint = blackPoint || [0, 0, 0]; - gamma = gamma || 1; - - // Translate arguments to spec variables. - this.XW = whitePoint[0]; - this.YW = whitePoint[1]; - this.ZW = whitePoint[2]; - - this.XB = blackPoint[0]; - this.YB = blackPoint[1]; - this.ZB = blackPoint[2]; - - this.G = gamma; - - // Validate variables as per spec. - if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) { - error('Invalid WhitePoint components for ' + this.name + - ', no fallback available'); - } - - if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { - info('Invalid BlackPoint for ' + this.name + ', falling back to default'); - this.XB = this.YB = this.ZB = 0; - } - - if (this.XB !== 0 || this.YB !== 0 || this.ZB !== 0) { - TODO(this.name + ', BlackPoint: XB: ' + this.XB + ', YB: ' + this.YB + - ', ZB: ' + this.ZB + ', only default values are supported.'); - } - - if (this.G < 1) { - info('Invalid Gamma: ' + this.G + ' for ' + this.name + - ', falling back to default'); - this.G = 1; - } - } - - CalGrayCS.prototype = { - getRgb: function CalGrayCS_getRgb(src, srcOffset) { - var rgb = new Uint8Array(3); - this.getRgbItem(src, srcOffset, rgb, 0); - return rgb; - }, - getRgbItem: function CalGrayCS_getRgbItem(src, srcOffset, - dest, destOffset) { - // A represents a gray component of a calibrated gray space. - // A <---> AG in the spec - var A = src[srcOffset]; - var AG = Math.pow(A, this.G); - - // Computes intermediate variables M, L, N as per spec. - // Except if other than default BlackPoint values are used. - var M = this.XW * AG; - var L = this.YW * AG; - var N = this.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; - }, - getRgbBuffer: function CalGrayCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - // TODO: This part is copied from DeviceGray. Make this utility function. - var scale = 255 / ((1 << bits) - 1); - var j = srcOffset, q = destOffset; - for (var i = 0; i < count; ++i) { - var c = (scale * src[j++]) | 0; - dest[q++] = c; - dest[q++] = c; - dest[q++] = c; - } - }, - getOutputLength: function CalGrayCS_getOutputLength(inputLength) { - return inputLength * 3; - }, - isPassthrough: ColorSpace.prototype.isPassthrough, - createRgbBuffer: ColorSpace.prototype.createRgbBuffer, - isDefaultDecode: function CalGrayCS_isDefaultDecode(decodeMap) { - return ColorSpace.isDefaultDecode(decodeMap, this.numComps); - }, - usesZeroToOneRange: true - }; - return CalGrayCS; -})(); - -// -// LabCS: Based on "PDF Reference, Sixth Ed", p.250 -// -var LabCS = (function LabCSClosure() { - function LabCS(whitePoint, blackPoint, range) { - this.name = 'Lab'; - this.numComps = 3; - this.defaultColor = new Float32Array([0, 0, 0]); - - if (!whitePoint) - error('WhitePoint missing - required for color space Lab'); - blackPoint = blackPoint || [0, 0, 0]; - range = range || [-100, 100, -100, 100]; - - // Translate args to spec variables - this.XW = whitePoint[0]; - this.YW = whitePoint[1]; - this.ZW = whitePoint[2]; - this.amin = range[0]; - this.amax = range[1]; - this.bmin = range[2]; - this.bmax = range[3]; - - // These are here just for completeness - the spec doesn't offer any - // formulas that use BlackPoint in Lab - this.XB = blackPoint[0]; - this.YB = blackPoint[1]; - this.ZB = blackPoint[2]; - - // Validate vars as per spec - if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) - error('Invalid WhitePoint components, no fallback available'); - - if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { - info('Invalid BlackPoint, falling back to default'); - this.XB = this.YB = this.ZB = 0; - } - - if (this.amin > this.amax || this.bmin > this.bmax) { - info('Invalid Range, falling back to defaults'); - this.amin = -100; - this.amax = 100; - this.bmin = -100; - this.bmax = 100; - } - } - - // Function g(x) from spec - function fn_g(x) { - if (x >= 6 / 29) - return x * x * x; - else - return (108 / 841) * (x - 4 / 29); - } - - function decode(value, high1, low2, high2) { - return low2 + (value) * (high2 - low2) / (high1); - } - - // If decoding is needed maxVal should be 2^bits per component - 1. - function convertToRgb(cs, src, srcOffset, maxVal, dest, destOffset) { - // XXX: Lab input is in the range of [0, 100], [amin, amax], [bmin, bmax] - // not the usual [0, 1]. If a command like setFillColor is used the src - // values will already be within the correct range. However, if we are - // converting an image we have to map the values to the correct range given - // above. - // Ls,as,bs <---> L*,a*,b* in the spec - var Ls = src[srcOffset]; - var as = src[srcOffset + 1]; - var bs = src[srcOffset + 2]; - if (maxVal !== false) { - Ls = decode(Ls, maxVal, 0, 100); - as = decode(as, maxVal, cs.amin, cs.amax); - bs = decode(bs, maxVal, cs.bmin, cs.bmax); - } - - // Adjust limits of 'as' and 'bs' - as = as > cs.amax ? cs.amax : as < cs.amin ? cs.amin : as; - bs = bs > cs.bmax ? cs.bmax : bs < cs.bmin ? cs.bmin : bs; - - // Computes intermediate variables X,Y,Z as per spec - var M = (Ls + 16) / 116; - var L = M + (as / 500); - var N = M - (bs / 200); - - var X = cs.XW * fn_g(L); - var Y = cs.YW * fn_g(M); - var Z = cs.ZW * fn_g(N); - - var r, g, b; - // Using different conversions for D50 and D65 white points, - // per http://www.color.org/srgb.pdf - if (cs.ZW < 1) { - // Assuming D50 (X=0.9642, Y=1.00, Z=0.8249) - r = X * 3.1339 + Y * -1.6170 + Z * -0.4906; - g = X * -0.9785 + Y * 1.9160 + Z * 0.0333; - b = X * 0.0720 + Y * -0.2290 + Z * 1.4057; - } else { - // Assuming D65 (X=0.9505, Y=1.00, Z=1.0888) - r = X * 3.2406 + Y * -1.5372 + Z * -0.4986; - g = X * -0.9689 + Y * 1.8758 + Z * 0.0415; - b = X * 0.0557 + Y * -0.2040 + Z * 1.0570; - } - // clamp color values to [0,1] range then convert to [0,255] range. - dest[destOffset] = Math.sqrt(r < 0 ? 0 : r > 1 ? 1 : r) * 255; - dest[destOffset + 1] = Math.sqrt(g < 0 ? 0 : g > 1 ? 1 : g) * 255; - dest[destOffset + 2] = Math.sqrt(b < 0 ? 0 : b > 1 ? 1 : b) * 255; - } - - LabCS.prototype = { - getRgb: function LabCS_getRgb(src, srcOffset) { - var rgb = new Uint8Array(3); - convertToRgb(this, src, srcOffset, false, rgb, 0); - return rgb; - }, - getRgbItem: function LabCS_getRgbItem(src, srcOffset, dest, destOffset) { - convertToRgb(this, src, srcOffset, false, dest, destOffset); - }, - getRgbBuffer: function LabCS_getRgbBuffer(src, srcOffset, count, - dest, destOffset, bits) { - var maxVal = (1 << bits) - 1; - for (var i = 0; i < count; i++) { - convertToRgb(this, src, srcOffset, maxVal, dest, destOffset); - srcOffset += 3; - destOffset += 3; - } - }, - getOutputLength: function LabCS_getOutputLength(inputLength) { - return inputLength; - }, - isPassthrough: ColorSpace.prototype.isPassthrough, - isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) { - // XXX: Decoding is handled with the lab conversion because of the strange - // ranges that are used. - return true; - }, - usesZeroToOneRange: false - }; - return LabCS; -})(); - - - var ARCFourCipher = (function ARCFourCipherClosure() { function ARCFourCipher(key) { this.a = 0; @@ -14273,119 +14189,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { this.fontCache = fontCache; } - // Specifies properties for each command - // - // If variableArgs === true: [0, `numArgs`] expected - // If variableArgs === false: exactly `numArgs` expected - var OP_MAP = { - // Graphic state - w: { id: OPS.setLineWidth, numArgs: 1, variableArgs: false }, - J: { id: OPS.setLineCap, numArgs: 1, variableArgs: false }, - j: { id: OPS.setLineJoin, numArgs: 1, variableArgs: false }, - M: { id: OPS.setMiterLimit, numArgs: 1, variableArgs: false }, - d: { id: OPS.setDash, numArgs: 2, variableArgs: false }, - ri: { id: OPS.setRenderingIntent, numArgs: 1, variableArgs: false }, - i: { id: OPS.setFlatness, numArgs: 1, variableArgs: false }, - gs: { id: OPS.setGState, numArgs: 1, variableArgs: false }, - q: { id: OPS.save, numArgs: 0, variableArgs: false }, - Q: { id: OPS.restore, numArgs: 0, variableArgs: false }, - cm: { id: OPS.transform, numArgs: 6, variableArgs: false }, - - // Path - m: { id: OPS.moveTo, numArgs: 2, variableArgs: false }, - l: { id: OPS.lineTo, numArgs: 2, variableArgs: false }, - c: { id: OPS.curveTo, numArgs: 6, variableArgs: false }, - v: { id: OPS.curveTo2, numArgs: 4, variableArgs: false }, - y: { id: OPS.curveTo3, numArgs: 4, variableArgs: false }, - h: { id: OPS.closePath, numArgs: 0, variableArgs: false }, - re: { id: OPS.rectangle, numArgs: 4, variableArgs: false }, - S: { id: OPS.stroke, numArgs: 0, variableArgs: false }, - s: { id: OPS.closeStroke, numArgs: 0, variableArgs: false }, - f: { id: OPS.fill, numArgs: 0, variableArgs: false }, - F: { id: OPS.fill, numArgs: 0, variableArgs: false }, - 'f*': { id: OPS.eoFill, numArgs: 0, variableArgs: false }, - B: { id: OPS.fillStroke, numArgs: 0, variableArgs: false }, - 'B*': { id: OPS.eoFillStroke, numArgs: 0, variableArgs: false }, - b: { id: OPS.closeFillStroke, numArgs: 0, variableArgs: false }, - 'b*': { id: OPS.closeEOFillStroke, numArgs: 0, variableArgs: false }, - n: { id: OPS.endPath, numArgs: 0, variableArgs: false }, - - // Clipping - W: { id: OPS.clip, numArgs: 0, variableArgs: false }, - 'W*': { id: OPS.eoClip, numArgs: 0, variableArgs: false }, - - // Text - BT: { id: OPS.beginText, numArgs: 0, variableArgs: false }, - ET: { id: OPS.endText, numArgs: 0, variableArgs: false }, - Tc: { id: OPS.setCharSpacing, numArgs: 1, variableArgs: false }, - Tw: { id: OPS.setWordSpacing, numArgs: 1, variableArgs: false }, - Tz: { id: OPS.setHScale, numArgs: 1, variableArgs: false }, - TL: { id: OPS.setLeading, numArgs: 1, variableArgs: false }, - Tf: { id: OPS.setFont, numArgs: 2, variableArgs: false }, - Tr: { id: OPS.setTextRenderingMode, numArgs: 1, variableArgs: false }, - Ts: { id: OPS.setTextRise, numArgs: 1, variableArgs: false }, - Td: { id: OPS.moveText, numArgs: 2, variableArgs: false }, - TD: { id: OPS.setLeadingMoveText, numArgs: 2, variableArgs: false }, - Tm: { id: OPS.setTextMatrix, numArgs: 6, variableArgs: false }, - 'T*': { id: OPS.nextLine, numArgs: 0, variableArgs: false }, - Tj: { id: OPS.showText, numArgs: 1, variableArgs: false }, - TJ: { id: OPS.showSpacedText, numArgs: 1, variableArgs: false }, - '\'': { id: OPS.nextLineShowText, numArgs: 1, variableArgs: false }, - '"': { id: OPS.nextLineSetSpacingShowText, numArgs: 3, - variableArgs: false }, - - // Type3 fonts - d0: { id: OPS.setCharWidth, numArgs: 2, variableArgs: false }, - d1: { id: OPS.setCharWidthAndBounds, numArgs: 6, variableArgs: false }, - - // Color - CS: { id: OPS.setStrokeColorSpace, numArgs: 1, variableArgs: false }, - cs: { id: OPS.setFillColorSpace, numArgs: 1, variableArgs: false }, - SC: { id: OPS.setStrokeColor, numArgs: 4, variableArgs: true }, - SCN: { id: OPS.setStrokeColorN, numArgs: 33, variableArgs: true }, - sc: { id: OPS.setFillColor, numArgs: 4, variableArgs: true }, - scn: { id: OPS.setFillColorN, numArgs: 33, variableArgs: true }, - G: { id: OPS.setStrokeGray, numArgs: 1, variableArgs: false }, - g: { id: OPS.setFillGray, numArgs: 1, variableArgs: false }, - RG: { id: OPS.setStrokeRGBColor, numArgs: 3, variableArgs: false }, - rg: { id: OPS.setFillRGBColor, numArgs: 3, variableArgs: false }, - K: { id: OPS.setStrokeCMYKColor, numArgs: 4, variableArgs: false }, - k: { id: OPS.setFillCMYKColor, numArgs: 4, variableArgs: false }, - - // Shading - sh: { id: OPS.shadingFill, numArgs: 1, variableArgs: false }, - - // Images - BI: { id: OPS.beginInlineImage, numArgs: 0, variableArgs: false }, - ID: { id: OPS.beginImageData, numArgs: 0, variableArgs: false }, - EI: { id: OPS.endInlineImage, numArgs: 1, variableArgs: false }, - - // XObjects - Do: { id: OPS.paintXObject, numArgs: 1, variableArgs: false }, - MP: { id: OPS.markPoint, numArgs: 1, variableArgs: false }, - DP: { id: OPS.markPointProps, numArgs: 2, variableArgs: false }, - BMC: { id: OPS.beginMarkedContent, numArgs: 1, variableArgs: false }, - BDC: { id: OPS.beginMarkedContentProps, numArgs: 2, - variableArgs: false }, - EMC: { id: OPS.endMarkedContent, numArgs: 0, variableArgs: false }, - - // Compatibility - BX: { id: OPS.beginCompat, numArgs: 0, variableArgs: false }, - EX: { id: OPS.endCompat, numArgs: 0, variableArgs: false }, - - // (reserved partial commands for the lexer) - BM: null, - BD: null, - 'true': null, - fa: null, - fal: null, - fals: null, - 'false': null, - nu: null, - nul: null, - 'null': null - }; - var TILING_PATTERN = 1, SHADING_PATTERN = 2; PartialEvaluator.prototype = { @@ -14431,7 +14234,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { buildFormXObject: function PartialEvaluator_buildFormXObject(resources, xobj, smask, - operatorList) { + operatorList, + state) { var self = this; var matrix = xobj.dict.get('Matrix'); @@ -14459,7 +14263,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]); this.getOperatorList(xobj, xobj.dict.get('Resources') || resources, - operatorList); + operatorList, state); operatorList.addOp(OPS.paintFormXObjectEnd, []); if (group) { @@ -14495,8 +14299,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var inverseDecode = !!decode && decode[0] > 0; operatorList.addOp(OPS.paintImageMaskXObject, - [PDFImage.createMask(imgArray, width, height, - inverseDecode)] + [PDFImage.createMask(imgArray, width, height, inverseDecode)] ); return; } @@ -14641,9 +14444,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { gStateObj.push([key, value]); break; case 'SMask': - // We support the default so don't trigger the TODO. + // We support the default so don't trigger a warning bar. if (!isName(value) || value.name != 'None') - TODO('graphic state operator ' + key); + UnsupportedManager.notify(UNSUPPORTED_FEATURES.smask); break; // Only generate info log messages for the following since // they are unlikey to have a big impact on the rendering. @@ -14709,22 +14512,31 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return this.fontCache.get(fontRef); } - font = xref.fetchIfRef(fontRef); if (!isDict(font)) { return errorFont(); } - this.fontCache.put(fontRef, font); + // Workaround for bad PDF generators that doesn't reference fonts + // properly, i.e. by not using an object identifier. + // Check if the fontRef is a Dict (as opposed to a standard object), + // in which case we don't cache the font and instead reference it by + // fontName in font.loadedName below. + var fontRefIsDict = isDict(fontRef); + if (!fontRefIsDict) { + this.fontCache.put(fontRef, font); + } // keep track of each font we translated so the caller can // load them asynchronously before calling display on a page - font.loadedName = 'g_font_' + fontRef.num + '_' + fontRef.gen; + font.loadedName = 'g_font_' + (fontRefIsDict ? + fontName.replace(/\W/g, '') : (fontRef.num + '_' + fontRef.gen)); if (!font.translated) { var translated; try { translated = this.translateFont(font, xref); } catch (e) { + UnsupportedManager.notify(UNSUPPORTED_FEATURES.font); translated = new ErrorFont(e instanceof Error ? e.message : e); } font.translated = translated; @@ -14757,7 +14569,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { getOperatorList: function PartialEvaluator_getOperatorList(stream, resources, - operatorList) { + operatorList, + evaluatorState) { var self = this; var xref = this.xref; @@ -14768,54 +14581,16 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { resources = resources || new Dict(); var xobjs = resources.get('XObject') || new Dict(); var patterns = resources.get('Pattern') || new Dict(); - // TODO(mduan): pass array of knownCommands rather than OP_MAP - // dictionary - var parser = new Parser(new Lexer(stream, OP_MAP), false, xref); + var preprocessor = new EvaluatorPreprocessor(stream, xref); + if (evaluatorState) { + preprocessor.setState(evaluatorState); + } - var promise = new Promise(); - var args = []; - while (true) { - - var obj = parser.getObj(); - - if (isEOF(obj)) { - break; - } - - if (isCmd(obj)) { - var cmd = obj.cmd; - - // Check that the command is valid - var opSpec = OP_MAP[cmd]; - if (!opSpec) { - warn('Unknown command "' + cmd + '"'); - continue; - } - - var fn = opSpec.id; - - // Validate the number of arguments for the command - if (opSpec.variableArgs) { - if (args.length > opSpec.numArgs) { - info('Command ' + fn + ': expected [0,' + opSpec.numArgs + - '] args, but received ' + args.length + ' args'); - } - } else { - if (args.length < opSpec.numArgs) { - // If we receive too few args, it's not possible to possible - // to execute the command, so skip the command - info('Command ' + fn + ': because expected ' + - opSpec.numArgs + ' args, but received ' + args.length + - ' args; skipping'); - args = []; - continue; - } else if (args.length > opSpec.numArgs) { - info('Command ' + fn + ': expected ' + opSpec.numArgs + - ' args, but received ' + args.length + ' args'); - } - } - - // TODO figure out how to type-check vararg functions + var promise = new LegacyPromise(); + var operation; + while ((operation = preprocessor.read())) { + var args = operation.args; + var fn = operation.fn; switch (fn) { case OPS.setStrokeColorN: @@ -14867,7 +14642,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { ); if ('Form' == type.name) { - self.buildFormXObject(resources, xobj, null, operatorList); + self.buildFormXObject(resources, xobj, null, operatorList, + preprocessor.getState()); args = []; continue; } else if ('Image' == type.name) { @@ -14958,36 +14734,25 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } // switch operatorList.addOp(fn, args); - args = []; - parser.saveState(); - } else if (obj !== null && obj !== undefined) { - args.push(obj instanceof Dict ? obj.getAll() : obj); - assertWellFormed(args.length <= 33, 'Too many arguments'); - } + } + + // some pdf don't close all restores inside object/form + // closing those for them + for (var i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) { + operatorList.addOp(OPS.restore, []); } return operatorList; }, getTextContent: function PartialEvaluator_getTextContent( - stream, resources, state) { + stream, resources, textState) { - var bidiTexts; + textState = textState || new TextState(); + + var bidiTexts = []; var SPACE_FACTOR = 0.35; var MULTI_SPACE_FACTOR = 1.5; - var textState; - - if (!state) { - textState = new TextState(); - bidiTexts = []; - state = { - textState: textState, - bidiTexts: bidiTexts - }; - } else { - bidiTexts = state.bidiTexts; - textState = state.textState; - } var self = this; var xref = this.xref; @@ -15000,65 +14765,55 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd. var xobjs = null; - var parser = new Parser(new Lexer(stream), false); + var preprocessor = new EvaluatorPreprocessor(stream, xref); var res = resources; - var args = [], obj; var chunk = ''; var font = null; var charSpace = 0, wordSpace = 0; - while (!isEOF(obj = parser.getObj())) { - if (isCmd(obj)) { - var cmd = obj.cmd; - switch (cmd) { + var operation; + while ((operation = preprocessor.read())) { + var fn = operation.fn; + var args = operation.args; + switch (fn) { // TODO: Add support for SAVE/RESTORE and XFORM here. - case 'Tf': + case OPS.setFont: font = handleSetFont(args[0].name).translated; textState.fontSize = args[1]; break; - case 'Ts': + case OPS.setTextRise: textState.textRise = args[0]; break; - case 'Tz': + case OPS.setHScale: textState.textHScale = args[0] / 100; break; - case 'TL': + case OPS.setLeading: textState.leading = args[0]; break; - case 'Td': + case OPS.moveText: textState.translateTextMatrix(args[0], args[1]); break; - case 'TD': + case OPS.setLeadingMoveText: textState.leading = -args[1]; textState.translateTextMatrix(args[0], args[1]); break; - case 'T*': + case OPS.nextLine: textState.translateTextMatrix(0, -textState.leading); break; - case 'Tm': + case OPS.setTextMatrix: textState.setTextMatrix(args[0], args[1], args[2], args[3], args[4], args[5]); break; - case 'Tc': + case OPS.setCharSpacing: charSpace = args[0]; break; - case 'Tw': + case OPS.setWordSpacing: wordSpace = args[0]; break; - case 'q': - textState.push(); - break; - case 'Q': - textState.pop(); - break; - case 'BT': + case OPS.beginText: textState.initialiseTextObj(); break; - case 'cm': - textState.transformCTM(args[0], args[1], args[2], - args[3], args[4], args[5]); - break; - case 'TJ': + case OPS.showSpacedText: var items = args[0]; for (var j = 0, jj = items.length; j < jj; j++) { if (typeof items[j] === 'string') { @@ -15076,20 +14831,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } } break; - case 'Tj': + case OPS.showText: chunk += fontCharsToUnicode(args[0], font); break; - case '\'': + case OPS.nextLineShowText: // For search, adding a extra white space for line breaks would be // better here, but that causes too much spaces in the // text-selection divs. chunk += fontCharsToUnicode(args[0], font); break; - case '"': + case OPS.nextLineSetSpacingShowText: // Note comment in "'" chunk += fontCharsToUnicode(args[2], font); break; - case 'Do': + case OPS.paintXObject: // Set the chunk such that the following if won't add something // to the state. chunk = ''; @@ -15117,13 +14872,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if ('Form' !== type.name) break; - state = this.getTextContent( + var formTexts = this.getTextContent( xobj, xobj.dict.get('Resources') || resources, - state + textState ); + Util.concatenateToArray(bidiTexts, formTexts); break; - case 'gs': + case OPS.setGState: var dictName = args[0]; var extGState = resources.get('ExtGState'); @@ -15141,29 +14897,36 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } // switch if (chunk !== '') { - var bidiText = PDFJS.bidi(chunk, -1, font.vertical); - var renderParams = textState.calcRenderParams(); + var bidiResult = PDFJS.bidi(chunk, -1, font.vertical); + var bidiText = { + str: bidiResult.str, + dir: bidiResult.dir + }; + var renderParams = textState.calcRenderParams(preprocessor.ctm); bidiText.x = renderParams.renderMatrix[4] - (textState.fontSize * renderParams.vScale * Math.sin(renderParams.angle)); bidiText.y = renderParams.renderMatrix[5] + (textState.fontSize * renderParams.vScale * Math.cos(renderParams.angle)); + var fontHeight = textState.fontSize * renderParams.vScale; + var fontAscent = font.ascent ? font.ascent * fontHeight : + font.descent ? (1 + font.descent) * fontHeight : fontHeight; + bidiText.x = renderParams.renderMatrix[4] - (fontAscent * + Math.sin(renderParams.angle)); + bidiText.y = renderParams.renderMatrix[5] + (fontAscent * + Math.cos(renderParams.angle)); if (bidiText.dir == 'ttb') { bidiText.x += renderParams.vScale / 2; bidiText.y -= renderParams.vScale; } + bidiText.angle = renderParams.angle; + bidiText.size = fontHeight; bidiTexts.push(bidiText); chunk = ''; } - - args = []; - } else if (obj !== null && obj !== undefined) { - assertWellFormed(args.length <= 33, 'Too many arguments'); - args.push(obj); - } } // while - return state; + return bidiTexts; }, extractDataStructures: function @@ -15208,10 +14971,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { Encodings.WinAnsiEncoding : Encodings.StandardEncoding; // The Symbolic attribute can be misused for regular fonts - // Heuristic: we have to check if the font is a standard one also + // Heuristic: we have to check if the font is a standard one and has + // Symbolic font name if (!!(flags & FontFlags.Symbolic)) { - baseEncoding = !properties.file ? Encodings.SymbolSetEncoding : - Encodings.MacRomanEncoding; + baseEncoding = !properties.file && /Symbol/i.test(properties.name) ? + Encodings.SymbolSetEncoding : Encodings.MacRomanEncoding; } if (dict.has('Encoding')) { var encoding = dict.get('Encoding'); @@ -15239,7 +15003,17 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } else if (isName(encoding)) { overridableEncoding = false; hasEncoding = true; - baseEncoding = Encodings[encoding.name]; + var currentEncoding = Encodings[encoding.name]; + + // Some bad PDF files contain fonts whose encoding name is not among + // the predefined encodings, causing baseEncoding to be undefined. + // In this case, fallback to using the baseEncoding as defined above + // and let the font override the encoding if one is available. + if (currentEncoding) { + baseEncoding = currentEncoding; + } else { + overridableEncoding = true; + } } else { error('Encoding is not a Name nor a Dict'); } @@ -15479,6 +15253,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var properties = { type: type.name, + name: baseFontName, widths: metrics.widths, defaultWidth: metrics.defaultWidth, flags: flags, @@ -15537,6 +15312,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var properties = { type: type.name, + name: fontName.name, subtype: subtype, file: fontFile, length1: length1, @@ -15816,7 +15592,6 @@ var OperatorList = (function OperatorListClosure() { var TextState = (function TextStateClosure() { function TextState() { this.fontSize = 0; - this.ctm = [1, 0, 0, 1, 0, 0]; this.textMatrix = [1, 0, 0, 1, 0, 0]; this.stateStack = []; //textState variables @@ -15825,15 +15600,6 @@ var TextState = (function TextStateClosure() { this.textRise = 0; } TextState.prototype = { - push: function TextState_push() { - this.stateStack.push(this.ctm.slice()); - }, - pop: function TextState_pop() { - var prev = this.stateStack.pop(); - if (prev) { - this.ctm = prev; - } - }, initialiseTextObj: function TextState_initialiseTextObj() { var m = this.textMatrix; m[0] = 1, m[1] = 0, m[2] = 0, m[3] = 1, m[4] = 0, m[5] = 0; @@ -15842,24 +15608,13 @@ var TextState = (function TextStateClosure() { var m = this.textMatrix; m[0] = a, m[1] = b, m[2] = c, m[3] = d, m[4] = e, m[5] = f; }, - transformCTM: function TextState_transformCTM(a, b, c, d, e, f) { - var m = this.ctm; - var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], m4 = m[4], m5 = m[5]; - m[0] = m0 * a + m2 * b; - m[1] = m1 * a + m3 * b; - m[2] = m0 * c + m2 * d; - m[3] = m1 * c + m3 * d; - m[4] = m0 * e + m2 * f + m4; - m[5] = m1 * e + m3 * f + m5; - }, translateTextMatrix: function TextState_translateTextMatrix(x, y) { var m = this.textMatrix; m[4] = m[0] * x + m[2] * y + m[4]; m[5] = m[1] * x + m[3] * y + m[5]; }, - calcRenderParams: function TextState_calcRenderingParams() { + calcRenderParams: function TextState_calcRenderingParams(cm) { var tm = this.textMatrix; - var cm = this.ctm; var a = this.fontSize; var b = a * this.textHScale; var c = this.textRise; @@ -15902,6 +15657,221 @@ var EvalState = (function EvalStateClosure() { return EvalState; })(); +var EvaluatorPreprocessor = (function EvaluatorPreprocessor() { + // Specifies properties for each command + // + // If variableArgs === true: [0, `numArgs`] expected + // If variableArgs === false: exactly `numArgs` expected + var OP_MAP = { + // Graphic state + w: { id: OPS.setLineWidth, numArgs: 1, variableArgs: false }, + J: { id: OPS.setLineCap, numArgs: 1, variableArgs: false }, + j: { id: OPS.setLineJoin, numArgs: 1, variableArgs: false }, + M: { id: OPS.setMiterLimit, numArgs: 1, variableArgs: false }, + d: { id: OPS.setDash, numArgs: 2, variableArgs: false }, + ri: { id: OPS.setRenderingIntent, numArgs: 1, variableArgs: false }, + i: { id: OPS.setFlatness, numArgs: 1, variableArgs: false }, + gs: { id: OPS.setGState, numArgs: 1, variableArgs: false }, + q: { id: OPS.save, numArgs: 0, variableArgs: false }, + Q: { id: OPS.restore, numArgs: 0, variableArgs: false }, + cm: { id: OPS.transform, numArgs: 6, variableArgs: false }, + + // Path + m: { id: OPS.moveTo, numArgs: 2, variableArgs: false }, + l: { id: OPS.lineTo, numArgs: 2, variableArgs: false }, + c: { id: OPS.curveTo, numArgs: 6, variableArgs: false }, + v: { id: OPS.curveTo2, numArgs: 4, variableArgs: false }, + y: { id: OPS.curveTo3, numArgs: 4, variableArgs: false }, + h: { id: OPS.closePath, numArgs: 0, variableArgs: false }, + re: { id: OPS.rectangle, numArgs: 4, variableArgs: false }, + S: { id: OPS.stroke, numArgs: 0, variableArgs: false }, + s: { id: OPS.closeStroke, numArgs: 0, variableArgs: false }, + f: { id: OPS.fill, numArgs: 0, variableArgs: false }, + F: { id: OPS.fill, numArgs: 0, variableArgs: false }, + 'f*': { id: OPS.eoFill, numArgs: 0, variableArgs: false }, + B: { id: OPS.fillStroke, numArgs: 0, variableArgs: false }, + 'B*': { id: OPS.eoFillStroke, numArgs: 0, variableArgs: false }, + b: { id: OPS.closeFillStroke, numArgs: 0, variableArgs: false }, + 'b*': { id: OPS.closeEOFillStroke, numArgs: 0, variableArgs: false }, + n: { id: OPS.endPath, numArgs: 0, variableArgs: false }, + + // Clipping + W: { id: OPS.clip, numArgs: 0, variableArgs: false }, + 'W*': { id: OPS.eoClip, numArgs: 0, variableArgs: false }, + + // Text + BT: { id: OPS.beginText, numArgs: 0, variableArgs: false }, + ET: { id: OPS.endText, numArgs: 0, variableArgs: false }, + Tc: { id: OPS.setCharSpacing, numArgs: 1, variableArgs: false }, + Tw: { id: OPS.setWordSpacing, numArgs: 1, variableArgs: false }, + Tz: { id: OPS.setHScale, numArgs: 1, variableArgs: false }, + TL: { id: OPS.setLeading, numArgs: 1, variableArgs: false }, + Tf: { id: OPS.setFont, numArgs: 2, variableArgs: false }, + Tr: { id: OPS.setTextRenderingMode, numArgs: 1, variableArgs: false }, + Ts: { id: OPS.setTextRise, numArgs: 1, variableArgs: false }, + Td: { id: OPS.moveText, numArgs: 2, variableArgs: false }, + TD: { id: OPS.setLeadingMoveText, numArgs: 2, variableArgs: false }, + Tm: { id: OPS.setTextMatrix, numArgs: 6, variableArgs: false }, + 'T*': { id: OPS.nextLine, numArgs: 0, variableArgs: false }, + Tj: { id: OPS.showText, numArgs: 1, variableArgs: false }, + TJ: { id: OPS.showSpacedText, numArgs: 1, variableArgs: false }, + '\'': { id: OPS.nextLineShowText, numArgs: 1, variableArgs: false }, + '"': { id: OPS.nextLineSetSpacingShowText, numArgs: 3, + variableArgs: false }, + + // Type3 fonts + d0: { id: OPS.setCharWidth, numArgs: 2, variableArgs: false }, + d1: { id: OPS.setCharWidthAndBounds, numArgs: 6, variableArgs: false }, + + // Color + CS: { id: OPS.setStrokeColorSpace, numArgs: 1, variableArgs: false }, + cs: { id: OPS.setFillColorSpace, numArgs: 1, variableArgs: false }, + SC: { id: OPS.setStrokeColor, numArgs: 4, variableArgs: true }, + SCN: { id: OPS.setStrokeColorN, numArgs: 33, variableArgs: true }, + sc: { id: OPS.setFillColor, numArgs: 4, variableArgs: true }, + scn: { id: OPS.setFillColorN, numArgs: 33, variableArgs: true }, + G: { id: OPS.setStrokeGray, numArgs: 1, variableArgs: false }, + g: { id: OPS.setFillGray, numArgs: 1, variableArgs: false }, + RG: { id: OPS.setStrokeRGBColor, numArgs: 3, variableArgs: false }, + rg: { id: OPS.setFillRGBColor, numArgs: 3, variableArgs: false }, + K: { id: OPS.setStrokeCMYKColor, numArgs: 4, variableArgs: false }, + k: { id: OPS.setFillCMYKColor, numArgs: 4, variableArgs: false }, + + // Shading + sh: { id: OPS.shadingFill, numArgs: 1, variableArgs: false }, + + // Images + BI: { id: OPS.beginInlineImage, numArgs: 0, variableArgs: false }, + ID: { id: OPS.beginImageData, numArgs: 0, variableArgs: false }, + EI: { id: OPS.endInlineImage, numArgs: 1, variableArgs: false }, + + // XObjects + Do: { id: OPS.paintXObject, numArgs: 1, variableArgs: false }, + MP: { id: OPS.markPoint, numArgs: 1, variableArgs: false }, + DP: { id: OPS.markPointProps, numArgs: 2, variableArgs: false }, + BMC: { id: OPS.beginMarkedContent, numArgs: 1, variableArgs: false }, + BDC: { id: OPS.beginMarkedContentProps, numArgs: 2, + variableArgs: false }, + EMC: { id: OPS.endMarkedContent, numArgs: 0, variableArgs: false }, + + // Compatibility + BX: { id: OPS.beginCompat, numArgs: 0, variableArgs: false }, + EX: { id: OPS.endCompat, numArgs: 0, variableArgs: false }, + + // (reserved partial commands for the lexer) + BM: null, + BD: null, + 'true': null, + fa: null, + fal: null, + fals: null, + 'false': null, + nu: null, + nul: null, + 'null': null + }; + + function EvaluatorPreprocessor(stream, xref) { + // TODO(mduan): pass array of knownCommands rather than OP_MAP + // dictionary + this.parser = new Parser(new Lexer(stream, OP_MAP), false, xref); + this.ctm = new Float32Array([1, 0, 0, 1, 0, 0]); + this.savedStates = []; + } + EvaluatorPreprocessor.prototype = { + get savedStatesDepth() { + return this.savedStates.length; + }, + read: function EvaluatorPreprocessor_read() { + var args = []; + while (true) { + var obj = this.parser.getObj(); + if (isEOF(obj)) { + return null; // no more commands + } + if (!isCmd(obj)) { + // argument + if (obj !== null && obj !== undefined) { + args.push(obj instanceof Dict ? obj.getAll() : obj); + assertWellFormed(args.length <= 33, 'Too many arguments'); + } + continue; + } + + var cmd = obj.cmd; + // Check that the command is valid + var opSpec = OP_MAP[cmd]; + if (!opSpec) { + warn('Unknown command "' + cmd + '"'); + continue; + } + + var fn = opSpec.id; + + // Validate the number of arguments for the command + if (opSpec.variableArgs) { + if (args.length > opSpec.numArgs) { + info('Command ' + fn + ': expected [0,' + opSpec.numArgs + + '] args, but received ' + args.length + ' args'); + } + } else { + if (args.length < opSpec.numArgs) { + // If we receive too few args, it's not possible to possible + // to execute the command, so skip the command + info('Command ' + fn + ': because expected ' + + opSpec.numArgs + ' args, but received ' + args.length + + ' args; skipping'); + args = []; + continue; + } else if (args.length > opSpec.numArgs) { + info('Command ' + fn + ': expected ' + opSpec.numArgs + + ' args, but received ' + args.length + ' args'); + } + } + + // TODO figure out how to type-check vararg functions + + this.preprocessCommand(fn, args); + + return {fn: fn, args: args}; + } + }, + getState: function EvaluatorPreprocessor_getState() { + return { + ctm: this.ctm + }; + }, + setState: function EvaluatorPreprocessor_setState(state) { + this.ctm = state.ctm; + }, + preprocessCommand: function EvaluatorPreprocessor_preprocessCommand(fn, + args) { + switch (fn | 0) { + case OPS.save: + this.savedStates.push(this.getState()); + break; + case OPS.restore: + var previousState = this.savedStates.pop(); + if (previousState) { + this.setState(previousState); + } + break; + case OPS.transform: + var ctm = this.ctm; + var m = new Float32Array(6); + m[0] = ctm[0] * args[0] + ctm[2] * args[1]; + m[1] = ctm[1] * args[0] + ctm[3] * args[1]; + m[2] = ctm[0] * args[2] + ctm[2] * args[3]; + m[3] = ctm[1] * args[2] + ctm[3] * args[3]; + m[4] = ctm[0] * args[4] + ctm[2] * args[5] + ctm[4]; + m[5] = ctm[1] * args[4] + ctm[3] * args[5] + ctm[5]; + this.ctm = m; + break; + } + } + }; + return EvaluatorPreprocessor; +})(); // Unicode Private Use Area @@ -16338,6 +16308,76 @@ var HalfwidthCMaps = { 'UniJIS-UCS2-HW-V': 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, + '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, + '73': 102, '74': 103, '75': 104, '76': 105, '77': 106, '78': 107, '79': 108, + '80': 109, '81': 110, '82': 111, '83': 112, '84': 113, '85': 114, '86': 115, + '87': 116, '88': 117, '89': 118, '90': 119, '91': 120, '92': 121, '93': 122, + '94': 123, '95': 124, '96': 125, '97': 126, '98': 196, '99': 197, '100': 199, + '101': 201, '102': 209, '103': 214, '104': 220, '105': 225, '106': 224, + '107': 226, '108': 228, '109': 227, '110': 229, '111': 231, '112': 233, + '113': 232, '114': 234, '115': 235, '116': 237, '117': 236, '118': 238, + '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, + '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, + '2428': 7825, '2429': 7826, '2430': 7827, '2433': 7682, '2678': 8045, + '2679': 8046, '2830': 1552, '2838': 686, '2840': 751, '2842': 753, + '2843': 754, '2844': 755, '2846': 757, '2856': 767, '2857': 848, '2858': 849, + '2862': 853, '2863': 854, '2864': 855, '2865': 861, '2866': 862, '2906': 7460, + '2908': 7462, '2909': 7463, '2910': 7464, '2912': 7466, '2913': 7467, + '2914': 7468, '2916': 7470, '2917': 7471, '2918': 7472, '2920': 7474, + '2921': 7475, '2922': 7476, '2924': 7478, '2925': 7479, '2926': 7480, + '2928': 7482, '2929': 7483, '2930': 7484, '2932': 7486, '2933': 7487, + '2934': 7488, '2936': 7490, '2937': 7491, '2938': 7492, '2940': 7494, + '2941': 7495, '2942': 7496, '2944': 7498, '2946': 7500, '2948': 7502, + '2950': 7504, '2951': 7505, '2952': 7506, '2954': 7508, '2955': 7509, + '2956': 7510, '2958': 7512, '2959': 7513, '2960': 7514, '2962': 7516, + '2963': 7517, '2964': 7518, '2966': 7520, '2967': 7521, '2968': 7522, + '2970': 7524, '2971': 7525, '2972': 7526, '2974': 7528, '2975': 7529, + '2976': 7530, '2978': 1537, '2979': 1538, '2980': 1539, '2982': 1549, + '2983': 1551, '2984': 1552, '2986': 1554, '2987': 1555, '2988': 1556, + '2990': 1623, '2991': 1624, '2995': 1775, '2999': 1791, '3002': 64290, + '3003': 64291, '3004': 64292, '3006': 64294, '3007': 64295, '3008': 64296, + '3011': 1900, '3014': 8223, '3015': 8244, '3017': 7532, '3018': 7533, + '3019': 7534, '3075': 7590, '3076': 7591, '3079': 7594, '3080': 7595, + '3083': 7598, '3084': 7599, '3087': 7602, '3088': 7603, '3091': 7606, + '3092': 7607, '3095': 7610, '3096': 7611, '3099': 7614, '3100': 7615, + '3103': 7618, '3104': 7619, '3107': 8337, '3108': 8338, '3116': 1884, + '3119': 1885, '3120': 1885, '3123': 1886, '3124': 1886, '3127': 1887, + '3128': 1887, '3131': 1888, '3132': 1888, '3135': 1889, '3136': 1889, + '3139': 1890, '3140': 1890, '3143': 1891, '3144': 1891, '3147': 1892, + '3148': 1892, '3153': 580, '3154': 581, '3157': 584, '3158': 585, '3161': 588, + '3162': 589, '3165': 891, '3166': 892, '3169': 1274, '3170': 1275, + '3173': 1278, '3174': 1279, '3181': 7622, '3182': 7623, '3282': 11799, + '3316': 578, '3379': 42785, '3393': 1159, '3416': 8377 +}; + var decodeBytes; if (typeof TextDecoder !== 'undefined') { // The encodings supported by TextDecoder can be found at: @@ -16377,7 +16417,7 @@ function sjis83pvToUnicode(str) { // TODO: 83pv has incompatible mappings in ed40..ee9c range. return decodeBytes(bytes, 'shift_jis', true); } catch (e) { - TODO('Unsupported 83pv character found'); + warn('Unsupported 83pv character found'); // Just retry without checking errors for now. return decodeBytes(bytes, 'shift_jis'); } @@ -16389,7 +16429,7 @@ function sjis90pvToUnicode(str) { // TODO: 90pv has incompatible mappings in 8740..879c and eb41..ee9c. return decodeBytes(bytes, 'shift_jis', true); } catch (e) { - TODO('Unsupported 90pv character found'); + warn('Unsupported 90pv character found'); // Just retry without checking errors for now. return decodeBytes(bytes, 'shift_jis'); } @@ -18065,6 +18105,8 @@ var Font = (function FontClosure() { this.wideChars = properties.wideChars; this.hasEncoding = properties.hasEncoding; this.cmap = properties.cmap; + this.ascent = properties.ascent / PDF_GLYPH_SPACE_UNITS; + this.descent = properties.descent / PDF_GLYPH_SPACE_UNITS; this.fontMatrix = properties.fontMatrix; if (properties.type == 'Type3') { @@ -18092,6 +18134,7 @@ var Font = (function FontClosure() { // The file data is not specified. Trying to fix the font name // to be used with the canvas.font. var fontName = name.replace(/[,_]/g, '-'); + var isStandardFont = fontName in stdFontMap; fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName; this.bold = (fontName.search(/bold/gi) != -1); @@ -18107,6 +18150,17 @@ var Font = (function FontClosure() { this.encoding = properties.baseEncoding; this.noUnicodeAdaptation = true; + if (isStandardFont && type === 'CIDFontType2' && + properties.cidEncoding.indexOf('Identity-') === 0) { + // Standard fonts might be embedded as CID font without glyph mapping. + // Building one based on GlyphMapForStandardFonts. + var map = []; + for (var code in GlyphMapForStandardFonts) { + map[+code] = GlyphMapForStandardFonts[code]; + } + this.toFontChar = map; + this.toUnicode = map; + } this.loadedName = fontName.split('-')[0]; this.loading = false; return; @@ -20221,7 +20275,7 @@ var Font = (function FontClosure() { var cidEncoding = properties.cidEncoding; if (properties.toUnicode) { if (cidEncoding && cidEncoding.indexOf('Identity-') !== 0) { - TODO('Need to create a reverse mapping from \'ToUnicode\' CMap'); + warn('Need to create a reverse mapping from \'ToUnicode\' CMap'); } return; // 'ToUnicode' CMap will be used } @@ -27902,7 +27956,7 @@ var PDFImage = (function PDFImageClosure() { if (image.getParams) { // JPX/JPEG2000 streams directly contain bits per component // and color space mode information. - TODO('get params from actual stream'); + warn('get params from actual stream'); // var bits = ... // var colorspace = ... } @@ -27935,7 +27989,7 @@ var PDFImage = (function PDFImageClosure() { if (!this.imageMask) { var colorSpace = dict.get('ColorSpace', 'CS'); if (!colorSpace) { - TODO('JPX images (which don"t require color spaces'); + warn('JPX images (which don"t require color spaces'); colorSpace = new Name('DeviceRGB'); } this.colorSpace = ColorSpace.parse(colorSpace, xref, res); @@ -27977,9 +28031,9 @@ var PDFImage = (function PDFImageClosure() { */ PDFImage.buildImage = function PDFImage_buildImage(callback, handler, xref, res, image, inline) { - var imageDataPromise = new Promise(); - var smaskPromise = new Promise(); - var maskPromise = new Promise(); + var imageDataPromise = new LegacyPromise(); + var smaskPromise = new LegacyPromise(); + var maskPromise = new LegacyPromise(); // The image data and smask data may not be ready yet, wait till both are // resolved. Promise.all([imageDataPromise, smaskPromise, maskPromise]).then( @@ -28057,26 +28111,21 @@ var PDFImage = (function PDFImageClosure() { PDFImage.createMask = function PDFImage_createMask(imgArray, width, height, inverseDecode) { - var buffer = new Uint8Array(width * height * 4); - var imgArrayPos = 0; - var i, j, mask, buf; - // removing making non-masked pixels transparent - var bufferPos = 3; // alpha component offset - for (i = 0; i < height; i++) { - mask = 0; - for (j = 0; j < width; j++) { - if (!mask) { - buf = imgArray[imgArrayPos++]; - mask = 128; - } - if (!(buf & mask) !== inverseDecode) { - buffer[bufferPos] = 255; - } - bufferPos += 4; - mask >>= 1; + // Copy imgArray into a typed array (inverting if necessary) so it can be + // transferred to the main thread. + var length = ((width + 7) >> 3) * height; + var data = new Uint8Array(length); + if (inverseDecode) { + for (var i = 0; i < length; i++) { + data[i] = ~imgArray[i]; + } + } else { + for (var i = 0; i < length; i++) { + data[i] = imgArray[i]; } } - return {data: buffer, width: width, height: height}; + + return {data: data, width: width, height: height}; }; PDFImage.prototype = { @@ -28179,38 +28228,39 @@ var PDFImage = (function PDFImageClosure() { } return output; }, - getOpacity: function PDFImage_getOpacity(width, height, image) { + fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height, + actualHeight, image) { var smask = this.smask; var mask = this.mask; - var originalWidth = this.width; - var originalHeight = this.height; - var buf; + var alphaBuf; if (smask) { var sw = smask.width; var sh = smask.height; - buf = new Uint8Array(sw * sh); - smask.fillGrayBuffer(buf); + alphaBuf = new Uint8Array(sw * sh); + smask.fillGrayBuffer(alphaBuf); if (sw != width || sh != height) - buf = PDFImage.resize(buf, smask.bpc, 1, sw, sh, width, height); + alphaBuf = PDFImage.resize(alphaBuf, smask.bpc, 1, sw, sh, width, + height); } else if (mask) { if (mask instanceof PDFImage) { var sw = mask.width; var sh = mask.height; - buf = new Uint8Array(sw * sh); + alphaBuf = new Uint8Array(sw * sh); mask.numComps = 1; - mask.fillGrayBuffer(buf); + mask.fillGrayBuffer(alphaBuf); - // Need to invert values in buffer + // Need to invert values in rgbaBuf for (var i = 0, ii = sw * sh; i < ii; ++i) - buf[i] = 255 - buf[i]; + alphaBuf[i] = 255 - alphaBuf[i]; if (sw != width || sh != height) - buf = PDFImage.resize(buf, mask.bpc, 1, sw, sh, width, height); + alphaBuf = PDFImage.resize(alphaBuf, mask.bpc, 1, sw, sh, width, + height); } else if (isArray(mask)) { // Color key mask: if any of the compontents are outside the range // then they should be painted. - buf = new Uint8Array(width * height); + alphaBuf = new Uint8Array(width * height); var numComps = this.numComps; for (var i = 0, ii = width * height; i < ii; ++i) { var opacity = 0; @@ -28223,17 +28273,23 @@ var PDFImage = (function PDFImageClosure() { break; } } - buf[i] = opacity; + alphaBuf[i] = opacity; } } else { error('Unknown mask format.'); } - } else { - buf = new Uint8Array(width * height); - for (var i = 0, ii = width * height; i < ii; ++i) - buf[i] = 255; } - return buf; + + if (alphaBuf) { + for (var i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { + rgbaBuf[j] = alphaBuf[i]; + } + } else { + // Common case: no mask (and no need to allocate the extra buffer). + for (var 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; @@ -28277,28 +28333,17 @@ var PDFImage = (function PDFImageClosure() { var actualHeight = 0 | (imgArray.length / rowBytes * height / originalHeight); var comps = this.getComponents(imgArray); - // Build opacity here since color key masking needs to be perormed on + + // Handle opacity here since color key masking needs to be performed on // undecoded values. - var opacity = this.getOpacity(width, height, comps); + this.fillOpacity(buffer, width, height, actualHeight, comps); if (this.needsDecode) { this.decodeBuffer(comps); } - var rgbBuf = this.colorSpace.createRgbBuffer(comps, 0, - originalWidth * originalHeight, bpc); - if (originalWidth != width || originalHeight != height) - rgbBuf = PDFImage.resize(rgbBuf, this.bpc, 3, originalWidth, - originalHeight, width, height); - var compsPos = 0; - var opacityPos = 0; - var length = width * actualHeight * 4; - for (var i = 0; i < length; i += 4) { - buffer[i] = rgbBuf[compsPos++]; - buffer[i + 1] = rgbBuf[compsPos++]; - buffer[i + 2] = rgbBuf[compsPos++]; - buffer[i + 3] = opacity[opacityPos++]; - } + this.colorSpace.fillRgb(buffer, originalWidth, originalHeight, width, + height, actualHeight, bpc, comps); this.undoPreblend(buffer, width, actualHeight); }, @@ -31305,21 +31350,6 @@ var Parser = (function ParserClosure() { } Parser.prototype = { - saveState: function Parser_saveState() { - this.state = { - buf1: this.buf1, - buf2: this.buf2, - streamPos: this.lexer.stream.pos - }; - }, - - restoreState: function Parser_restoreState() { - var state = this.state; - this.buf1 = state.buf1; - this.buf2 = state.buf2; - this.lexer.stream.pos = state.streamPos; - }, - refill: function Parser_refill() { this.buf1 = this.lexer.getObj(); this.buf2 = this.lexer.getObj(); @@ -32524,12 +32554,18 @@ var FlateStream = (function FlateStreamClosure() { var buffer = this.ensureBuffer(bufferLength + blockLen); var end = bufferLength + blockLen; this.bufferLength = end; - for (var n = bufferLength; n < end; ++n) { - if (typeof (b = bytes[bytesPos++]) == 'undefined') { + if (blockLen === 0) { + if (typeof bytes[bytesPos] == 'undefined') { this.eof = true; - break; } - buffer[n] = b; + } else { + for (var n = bufferLength; n < end; ++n) { + if (typeof (b = bytes[bytesPos++]) == 'undefined') { + this.eof = true; + break; + } + buffer[n] = b; + } } this.bytesPos = bytesPos; return; @@ -32992,6 +33028,16 @@ var Jbig2Stream = (function Jbig2StreamClosure() { var jbig2Image = new Jbig2Image(); var chunks = [], decodeParams = this.dict.get('DecodeParms'); + + // According to the PDF specification, DecodeParms can be either + // a dictionary, or an array whose elements are dictionaries. + if (isArray(decodeParams)) { + if (decodeParams.length > 1) { + warn('JBIG2 - \'DecodeParms\' array with multiple elements ' + + 'not supported.'); + } + decodeParams = decodeParams[0]; + } if (decodeParams && decodeParams.has('JBIG2Globals')) { var globalsStream = decodeParams.get('JBIG2Globals'); var globals = globalsStream.getBytes(); @@ -34344,7 +34390,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { var pdfManager; function loadDocument(recoveryMode) { - var loadDocumentPromise = new Promise(); + var loadDocumentPromise = new LegacyPromise(); var parseSuccess = function parseSuccess() { var numPagesPromise = pdfManager.ensureModel('numPages'); @@ -34388,7 +34434,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } function getPdfManager(data) { - var pdfManagerPromise = new Promise(); + var pdfManagerPromise = new LegacyPromise(); var source = data.source; var disableRange = data.disableRange; @@ -34411,7 +34457,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { } var networkManager = new NetworkManager(source.url, { - httpHeaders: source.httpHeaders + httpHeaders: source.httpHeaders, + withCredentials: source.withCredentials }); var fullRequestXhrId = networkManager.requestFull({ onHeadersReceived: function onHeadersReceived() { @@ -34547,6 +34594,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { PDFJS.maxImageSize = data.maxImageSize === undefined ? -1 : data.maxImageSize; PDFJS.disableFontFace = data.disableFontFace; + PDFJS.disableCreateObjectURL = data.disableCreateObjectURL; + PDFJS.verbosity = data.verbosity; getPdfManager(data).then(function pdfManagerReady() { loadDocument(false).then(onSuccess, function loadFailure(ex) { @@ -34555,7 +34604,8 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { if (ex instanceof PasswordException) { // after password exception prepare to receive a new password // to repeat loading - pdfManager.passwordChangedPromise = new Promise(); + pdfManager.passwordChangedPromise = + new LegacyPromise(); pdfManager.passwordChangedPromise.then(pdfManagerReady); } @@ -34592,31 +34642,31 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); }); - handler.on('GetPageIndex', function wphSetupGetPageIndex(data, promise) { + handler.on('GetPageIndex', function wphSetupGetPageIndex(data, deferred) { var ref = new Ref(data.ref.num, data.ref.gen); pdfManager.pdfModel.catalog.getPageIndex(ref).then(function (pageIndex) { - promise.resolve(pageIndex); - }, promise.reject.bind(promise)); + deferred.resolve(pageIndex); + }, deferred.reject); }); handler.on('GetDestinations', - function wphSetupGetDestinations(data, promise) { + function wphSetupGetDestinations(data, deferred) { pdfManager.ensureCatalog('destinations').then(function(destinations) { - promise.resolve(destinations); + deferred.resolve(destinations); }); } ); - handler.on('GetData', function wphSetupGetData(data, promise) { + handler.on('GetData', function wphSetupGetData(data, deferred) { pdfManager.requestLoadedStream(); pdfManager.onLoadedStream().then(function(stream) { - promise.resolve(stream.bytes); + deferred.resolve(stream.bytes); }); }); - handler.on('DataLoaded', function wphSetupDataLoaded(data, promise) { + handler.on('DataLoaded', function wphSetupDataLoaded(data, deferred) { pdfManager.onLoadedStream().then(function(stream) { - promise.resolve({ length: stream.bytes.byteLength }); + deferred.resolve({ length: stream.bytes.byteLength }); }); }); @@ -34645,7 +34695,7 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { // Pre compile the pdf page and fetch the fonts/images. page.getOperatorList(handler).then(function(operatorList) { - log('page=%d - getOperatorList: time=%dms, len=%d', pageNum, + info('page=%d - getOperatorList: time=%dms, len=%d', pageNum, Date.now() - start, operatorList.fnArray.length); }, function(e) { @@ -34681,29 +34731,29 @@ var WorkerMessageHandler = PDFJS.WorkerMessageHandler = { }); }, this); - handler.on('GetTextContent', function wphExtractText(data, promise) { + handler.on('GetTextContent', function wphExtractText(data, deferred) { pdfManager.getPage(data.pageIndex).then(function(page) { var pageNum = data.pageIndex + 1; var start = Date.now(); page.extractTextContent().then(function(textContent) { - promise.resolve(textContent); - log('text indexing: page=%d - time=%dms', pageNum, + deferred.resolve(textContent); + info('text indexing: page=%d - time=%dms', pageNum, Date.now() - start); }, function (e) { // Skip errored pages - promise.reject(e); + deferred.reject(e); }); }); }); - handler.on('Cleanup', function wphCleanup(data, promise) { + handler.on('Cleanup', function wphCleanup(data, deferred) { pdfManager.cleanup(); - promise.resolve(true); + deferred.resolve(true); }); - handler.on('Terminate', function wphTerminate(data, promise) { + handler.on('Terminate', function wphTerminate(data, deferred) { pdfManager.terminate(); - promise.resolve(); + deferred.resolve(); }); } }; @@ -34735,25 +34785,25 @@ var workerConsole = { timeEnd: function timeEnd(name) { var time = consoleTimer[name]; if (!time) { - error('Unkown timer name ' + name); + error('Unknown timer name ' + name); } this.log('Timer:', name, Date.now() - time); } }; + // Worker thread? if (typeof window === 'undefined') { - globalScope.console = workerConsole; + if (!('console' in globalScope)) { + globalScope.console = workerConsole; + } - // Add a logger so we can pass warnings on to the main thread, errors will - // throw an exception which will be forwarded on automatically. - PDFJS.LogManager.addLogger({ - warn: function(msg) { - globalScope.postMessage({ - action: '_warn', - data: msg - }); - } + // Listen for unsupported features so we can pass them on to the main thread. + PDFJS.UnsupportedManager.listen(function (msg) { + globalScope.postMessage({ + action: '_unsupported_feature', + data: msg + }); }); var handler = new MessageHandler('worker_processor', this); diff --git a/browser/extensions/pdfjs/content/network.js b/browser/extensions/pdfjs/content/network.js index 2fc0f18eb70c..818fd58e9308 100644 --- a/browser/extensions/pdfjs/content/network.js +++ b/browser/extensions/pdfjs/content/network.js @@ -26,11 +26,13 @@ var EXPORTED_SYMBOLS = ['NetworkManager']; - function log(aMsg) { - var msg = 'network.js: ' + (aMsg.join ? aMsg.join('') : aMsg); - Services.console.logStringMessage(msg); - // TODO(mack): dump() doesn't seem to work here... - dump(msg + '\n'); + var console = { + log: function console_log(aMsg) { + var msg = 'network.js: ' + (aMsg.join ? aMsg.join('') : aMsg); + Services.console.logStringMessage(msg); + // TODO(mack): dump() doesn't seem to work here... + dump(msg + '\n'); + } } var NetworkManager = (function NetworkManagerClosure() { @@ -42,6 +44,7 @@ var NetworkManager = (function NetworkManagerClosure() { this.url = url; args = args || {}; this.httpHeaders = args.httpHeaders || {}; + this.withCredentials = args.withCredentials || false; this.getXhr = args.getXhr || function NetworkManager_getXhr() { return new XMLHttpRequest(); @@ -90,6 +93,7 @@ var NetworkManager = (function NetworkManagerClosure() { }; xhr.open('GET', this.url); + xhr.withCredentials = this.withCredentials; for (var property in this.httpHeaders) { var value = this.httpHeaders[property]; if (typeof value === 'undefined') { diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png b/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png index 08a2c25327d7..4b5551e23600 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-next-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-next.png b/browser/extensions/pdfjs/content/web/images/findbarButton-next.png index beef8ccea48c..f681e4ee44e4 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-next.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-next.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png b/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png index beef8ccea48c..f681e4ee44e4 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-previous-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png b/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png index 08a2c25327d7..4b5551e23600 100644 Binary files a/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png and b/browser/extensions/pdfjs/content/web/images/findbarButton-previous.png differ diff --git a/browser/extensions/pdfjs/content/web/images/grab.cur b/browser/extensions/pdfjs/content/web/images/grab.cur new file mode 100644 index 000000000000..db7ad5aed3ef Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/grab.cur differ diff --git a/browser/extensions/pdfjs/content/web/images/grabbing.cur b/browser/extensions/pdfjs/content/web/images/grabbing.cur new file mode 100644 index 000000000000..e0dfd04e4d3f Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/grabbing.cur differ diff --git a/browser/extensions/pdfjs/content/web/images/loading-small.png b/browser/extensions/pdfjs/content/web/images/loading-small.png index 1b2df8093d63..51848a70fe39 100644 Binary files a/browser/extensions/pdfjs/content/web/images/loading-small.png and b/browser/extensions/pdfjs/content/web/images/loading-small.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png new file mode 100644 index 000000000000..a9e82db331e9 Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-documentProperties.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png index 59b3d345f055..2c1076d467cf 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-firstPage.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool.png new file mode 100644 index 000000000000..10845c695d7b Binary files /dev/null and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-handTool.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png index 3967cc619606..55a9efe42f13 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-lastPage.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png index ffe62936c4a8..d23d84a5435e 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCcw.png differ diff --git a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png index 1fe2a8ffefc5..f36bba81e041 100644 Binary files a/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png and b/browser/extensions/pdfjs/content/web/images/secondaryToolbarButton-rotateCw.png differ diff --git a/browser/extensions/pdfjs/content/web/images/shadow.png b/browser/extensions/pdfjs/content/web/images/shadow.png index dd009dc4ee1c..31d3bdb14f1d 100644 Binary files a/browser/extensions/pdfjs/content/web/images/shadow.png and b/browser/extensions/pdfjs/content/web/images/shadow.png differ diff --git a/browser/extensions/pdfjs/content/web/images/texture.png b/browser/extensions/pdfjs/content/web/images/texture.png index df0086493236..eb5ccb5ec3cd 100644 Binary files a/browser/extensions/pdfjs/content/web/images/texture.png and b/browser/extensions/pdfjs/content/web/images/texture.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png index 1e5259a97ae3..b27050e2116e 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-bookmark.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png index 8676d8e2c2c0..7755eee779e5 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-download.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png index 31b06b5af9e5..63a7c320e9a9 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-menuArrows.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png index fc7023f2aafc..e139f3017d63 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-openFile.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png index c0051f8f0408..9ba3667e9498 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png index 762ac43e08cf..272a3ca8351f 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageDown.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png index 0d49d8e95e3f..da02bc69f7aa 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png index 3155b8bd235c..a4d6c8ea7512 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-pageUp.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png index fa730955054b..2bd1c1dfe843 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-presentationMode.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png index fef84dd59493..ccc62089faef 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-print.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png index 604e652e5ec4..e52370e68dca 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-search.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png index 97930244ced8..145316b8345d 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png index ddd8a8710be0..02ed6c62ad8a 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-secondaryToolbarToggle.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png index d9946fd0ad71..c308668b12c8 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png index 5a937c712b05..be9eceba944b 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-sidebarToggle.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png index d57a12c5d5b6..21ceb3b165cc 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline-rtl.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png index cb3967b7f3c5..9b2e2f5be21b 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewOutline.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png index 3a27f0439399..d71dd27711f6 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-viewThumbnail.png differ diff --git a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png index 810fbf9895d5..7991ee7a5070 100644 Binary files a/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png and b/browser/extensions/pdfjs/content/web/images/toolbarButton-zoomOut.png differ diff --git a/browser/extensions/pdfjs/content/web/viewer.css b/browser/extensions/pdfjs/content/web/viewer.css index 7f36aeb87aa2..3b0914cddc26 100644 --- a/browser/extensions/pdfjs/content/web/viewer.css +++ b/browser/extensions/pdfjs/content/web/viewer.css @@ -20,7 +20,7 @@ html { height: 100%; - /* Font size is needed to make the activity bar the corect size. */ + /* Font size is needed to make the activity bar the correct size. */ font-size: 10px; } @@ -47,8 +47,7 @@ select { #viewerContainer:-moz-full-screen { top: 0px; border-top: 2px solid transparent; - background-color: #404040; - background-image: url(images/texture.png); + background-color: #000; width: 100%; height: 100%; overflow: hidden; @@ -58,8 +57,7 @@ select { #viewerContainer:fullscreen { top: 0px; border-top: 2px solid transparent; - background-color: #404040; - background-image: url(images/texture.png); + background-color: #000; width: 100%; height: 100%; overflow: hidden; @@ -182,20 +180,19 @@ html[dir='rtl'] #outerContainer.sidebarOpen > #mainContainer { overflow: auto; position: absolute; width: 200px; - background-color: hsla(0,0%,0%,.1); - box-shadow: inset -1px 0 0 hsla(0,0%,0%,.25); } html[dir='ltr'] #sidebarContent { left: 0; + box-shadow: inset -1px 0 0 hsla(0,0%,0%,.25); } html[dir='rtl'] #sidebarContent { right: 0; + box-shadow: inset 1px 0 0 hsla(0,0%,0%,.25); } #viewerContainer { overflow: auto; - box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05); position: absolute; top: 32px; right: 0; @@ -203,6 +200,12 @@ html[dir='rtl'] #sidebarContent { left: 0; outline: none; } +html[dir='ltr'] #viewerContainer { + box-shadow: inset 1px 0 0 hsla(0,0%,100%,.05); +} +html[dir='rtl'] #viewerContainer { + box-shadow: inset -1px 0 0 hsla(0,0%,100%,.05); +} .toolbar { position: relative; @@ -222,12 +225,19 @@ html[dir='rtl'] #sidebarContent { background-color: #424242; /* fallback */ background-image: url(images/texture.png), linear-gradient(hsla(0,0%,30%,.99), hsla(0,0%,25%,.95)); +} +html[dir='ltr'] #toolbarSidebar { box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.25), - inset 0 -1px 0 hsla(0,0%,100%,.05), 0 1px 0 hsla(0,0%,0%,.15), 0 0 1px hsla(0,0%,0%,.1); } +html[dir='rtl'] #toolbarSidebar { + box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.25), + inset 0 1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 0 1px hsla(0,0%,0%,.1); +} #toolbarContainer, .findbar, .secondaryToolbar { position: relative; @@ -235,12 +245,21 @@ html[dir='rtl'] #sidebarContent { background-color: #474747; /* fallback */ background-image: url(images/texture.png), linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); +} +html[dir='ltr'] #toolbarContainer, .findbar, .secondaryToolbar { box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08), inset 0 1px 1px hsla(0,0%,0%,.15), inset 0 -1px 0 hsla(0,0%,100%,.05), 0 1px 0 hsla(0,0%,0%,.15), 0 1px 1px hsla(0,0%,0%,.1); } +html[dir='rtl'] #toolbarContainer, .findbar, .secondaryToolbar { + box-shadow: inset -1px 0 0 hsla(0,0%,100%,.08), + inset 0 1px 1px hsla(0,0%,0%,.15), + inset 0 -1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 1px 1px hsla(0,0%,0%,.1); +} #toolbarViewer { height: 32px; @@ -502,7 +521,7 @@ html[dir='rtl'] .splitToolbarButton > .toolbarButton { .splitToolbarButton > .toolbarButton:hover, .splitToolbarButton > .toolbarButton:focus, .dropdownToolbarButton:hover, -.promptButton:hover, +.overlayButton:hover, .toolbarButton.textButton:hover, .toolbarButton.textButton:focus { background-color: hsla(0,0%,0%,.2); @@ -556,7 +575,7 @@ html[dir='rtl'] .splitToolbarButtonSeparator { .toolbarButton, .dropdownToolbarButton, -.promptButton, +.overlayButton, .secondaryToolbarButton { min-width: 16px; padding: 2px 6px 0; @@ -574,12 +593,12 @@ html[dir='rtl'] .splitToolbarButtonSeparator { } html[dir='ltr'] .toolbarButton, -html[dir='ltr'] .promptButton, +html[dir='ltr'] .overlayButton, html[dir='ltr'] .dropdownToolbarButton { margin: 3px 2px 4px 0; } html[dir='rtl'] .toolbarButton, -html[dir='rtl'] .promptButton, +html[dir='rtl'] .overlayButton, html[dir='rtl'] .dropdownToolbarButton { margin: 3px 0 4px 2px; } @@ -587,7 +606,7 @@ html[dir='rtl'] .dropdownToolbarButton { .toolbarButton:hover, .toolbarButton:focus, .dropdownToolbarButton, -.promptButton, +.overlayButton, .secondaryToolbarButton:hover, .secondaryToolbarButton:focus { background-color: hsla(0,0%,0%,.12); @@ -601,7 +620,7 @@ html[dir='rtl'] .dropdownToolbarButton { } .toolbarButton:hover:active, -.promptButton:hover:active, +.overlayButton:hover:active, .dropdownToolbarButton:hover:active, .secondaryToolbarButton:hover:active { background-color: hsla(0,0%,0%,.2); @@ -668,7 +687,7 @@ html[dir='rtl'] .dropdownToolbarButton { background: hsl(0,0%,24%); } -.promptButton { +.overlayButton { margin: 3px 2px 4px 5px !important; line-height: 16px; padding: 2px 6px 3px 6px; @@ -900,6 +919,14 @@ html[dir="rtl"] .secondaryToolbarButton > span { content: url(images/secondaryToolbarButton-rotateCw.png); } +.secondaryToolbarButton.handTool::before { + content: url(images/secondaryToolbarButton-handTool.png); +} + +.secondaryToolbarButton.documentProperties::before { + content: url(images/secondaryToolbarButton-documentProperties.png); +} + .verticalToolbarSeparator { display: block; padding: 8px 0; @@ -1116,22 +1143,6 @@ html[dir='rtl'] .outlineItem > a { cursor: default; } -#findScrollView { - position: absolute; - top: 10px; - bottom: 10px; - left: 10px; - width: 280px; -} - -#sidebarControls { - position:absolute; - width: 180px; - height: 32px; - left: 15px; - bottom: 35px; -} - .canvasWrapper { overflow: hidden; } @@ -1335,6 +1346,73 @@ canvas { border-color: hsla(0,0%,0%,.32) hsla(0,0%,0%,.38) hsla(0,0%,0%,.42); } +#documentPropertiesContainer { + display: table-cell; + vertical-align: middle; + text-align: center; +} + +#documentPropertiesContainer > * { + display: inline-block; + padding: 15px; + border-spacing: 4px; + max-width: 350px; + max-height: 350px; + color: hsl(0,0%,85%); + font-size: 12px; + line-height: 14px; + text-align: left; + cursor: default; + background-color: #474747; /* fallback */ + background-image: url(images/texture.png), + linear-gradient(hsla(0,0%,32%,.99), hsla(0,0%,27%,.95)); + box-shadow: inset 1px 0 0 hsla(0,0%,100%,.08), + inset 0 1px 1px hsla(0,0%,0%,.15), + inset 0 -1px 0 hsla(0,0%,100%,.05), + 0 1px 0 hsla(0,0%,0%,.15), + 0 1px 1px hsla(0,0%,0%,.1); +} + +#documentPropertiesContainer .separator { + display: block; + margin: 4px 0 4px 0; + height: 1px; + width: 100%; + background-color: hsla(0,0%,0%,.5); + box-shadow: 0 0 0 1px hsla(0,0%,100%,.08); +} + +#documentPropertiesContainer .row { + display: table-row; +} + +html[dir='ltr'] #documentPropertiesContainer .row > * { + display: table-cell; + min-width: 100px; +} + +html[dir='rtl'] #documentPropertiesContainer .row > * { + display: table-cell; + min-width: 100px; + text-align: right; +} + +#documentPropertiesContainer .row span { + width: 125px; + word-wrap: break-word; +} + +#documentPropertiesContainer .row p { + max-width: 225px; + word-wrap: break-word; +} + +#documentPropertiesContainer .buttonRow { + margin-top: 10px; + text-align: center; + vertical-align: middle; +} + .clearBoth { clear: both; } @@ -1343,6 +1421,10 @@ canvas { background: white; color: black; margin-top: 5px; + visibility: hidden; + position: fixed; + right: 0; + top: 0; } #PDFBug { @@ -1404,6 +1486,30 @@ canvas { color: black; } +.grab-to-pan-grab * { + cursor: url("images/grab.cur"), move !important; + cursor: -moz-grab !important; + cursor: grab !important; +} +.grab-to-pan-grabbing, +.grab-to-pan-grabbing * { + cursor: url("images/grabbing.cur"), move !important; + cursor: -moz-grabbing !important; + cursor: grabbing !important; +} +.grab-to-pan-grab input, +.grab-to-pan-grab textarea, +.grab-to-pan-grab button, +.grab-to-pan-grab button *, +.grab-to-pan-grab select, +.grab-to-pan-grab option { + cursor: auto !important; +} +.grab-to-pan-grab a[href], +.grab-to-pan-grab a[href] * { + cursor: pointer !important; +} + @page { margin: 0; } diff --git a/browser/extensions/pdfjs/content/web/viewer.html b/browser/extensions/pdfjs/content/web/viewer.html index 32d295657972..3ac26da2ac57 100644 --- a/browser/extensions/pdfjs/content/web/viewer.html +++ b/browser/extensions/pdfjs/content/web/viewer.html @@ -119,6 +119,18 @@ limitations under the License. + +
+ + + +
+ + @@ -247,7 +259,7 @@ limitations under the License.