diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index e0e2bfbc3fe..b753e7be988 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.1334 +Current extension version is: 1.0.2 diff --git a/browser/extensions/pdfjs/content/PdfJs.jsm b/browser/extensions/pdfjs/content/PdfJs.jsm index 7adb77bc2e1..18b4de8d633 100644 --- a/browser/extensions/pdfjs/content/PdfJs.jsm +++ b/browser/extensions/pdfjs/content/PdfJs.jsm @@ -64,7 +64,9 @@ function initializeDefaultPreferences() { var DEFAULT_PREFERENCES = { showPreviousViewOnLoad: true, defaultZoomValue: '', - ifAvailableShowOutlineOnLoad: false + ifAvailableShowOutlineOnLoad: false, + enableHandToolOnLoad: false, + enableWebGL: false }; @@ -134,15 +136,19 @@ let PdfJs = { }, _migrate: function migrate() { - const VERSION = 1; + const VERSION = 2; var currentVersion = getIntPref(PREF_MIGRATION_VERSION, 0); if (currentVersion >= VERSION) { return; } // Make pdf.js the default pdf viewer on the first migration. - if (currentVersion < 2) { + if (currentVersion < 1) { this._becomeHandler(); } + if (currentVersion < 2) { + // cleaning up of unused database preference (see #3994) + Services.prefs.clearUserPref(PREF_PREFIX + '.database'); + } Services.prefs.setIntPref(PREF_MIGRATION_VERSION, VERSION); }, diff --git a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm index 086288c7521..927a1a37369 100644 --- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm +++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm @@ -32,7 +32,6 @@ const PDFJS_EVENT_ID = 'pdf.js.message'; const PDF_CONTENT_TYPE = 'application/pdf'; const PREF_PREFIX = 'pdfjs'; const PDF_VIEWER_WEB_PAGE = 'resource://pdf.js/web/viewer.html'; -const MAX_DATABASE_LENGTH = 4096; const MAX_NUMBER_OF_PREFS = 50; const MAX_STRING_PREF_LENGTH = 128; @@ -295,19 +294,6 @@ ChromeActions.prototype = { channel.asyncOpen(listener, null); }); }, - setDatabase: function(data) { - if (this.isInPrivateBrowsing()) - return; - // Protect against something sending tons of data to setDatabase. - if (data.length > MAX_DATABASE_LENGTH) - return; - setStringPref(PREF_PREFIX + '.database', data); - }, - getDatabase: function() { - if (this.isInPrivateBrowsing()) - return '{}'; - return getStringPref(PREF_PREFIX + '.database', '{}'); - }, getLocale: function() { return getStringPref('general.useragent.locale', 'en-US'); }, @@ -452,7 +438,7 @@ ChromeActions.prototype = { getChromeWindow(this.domWindow).gFindBar .updateControlState(result, findPrevious); }, - setPreferences: function(prefs) { + setPreferences: function(prefs, sendResponse) { var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); var numberOfPrefs = 0; var prefValue, prefName; @@ -483,8 +469,11 @@ ChromeActions.prototype = { break; } } + if (sendResponse) { + sendResponse(true); + } }, - getPreferences: function(prefs) { + getPreferences: function(prefs, sendResponse) { var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); var currentPrefs = {}, numberOfPrefs = 0; var prefValue, prefName; @@ -510,7 +499,11 @@ ChromeActions.prototype = { break; } } - return JSON.stringify(currentPrefs); + if (sendResponse) { + sendResponse(JSON.stringify(currentPrefs)); + } else { + return JSON.stringify(currentPrefs); + } } }; diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index 072bdec6562..9fb0a3c844f 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -21,8 +21,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.1334'; -PDFJS.build = 'b97127a'; +PDFJS.version = '1.0.2'; +PDFJS.build = '2909225'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -182,7 +182,7 @@ var OPS = PDFJS.OPS = { paintInlineImageXObjectGroup: 87, paintImageXObjectRepeat: 88, paintImageMaskXObjectRepeat: 89, - paintSolidColorImageMask: 90, + paintSolidColorImageMask: 90 }; // A notice for devs. These are good for things that are helpful to devs, such @@ -267,9 +267,10 @@ function combineUrl(baseUrl, url) { if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) { return url; } + var i; if (url.charAt(0) == '/') { // absolute path - var i = baseUrl.indexOf('://'); + i = baseUrl.indexOf('://'); if (url.charAt(1) === '/') { ++i; } else { @@ -278,7 +279,7 @@ function combineUrl(baseUrl, url) { return baseUrl.substring(0, i) + url; } else { // relative path - var pathLength = baseUrl.length, i; + var pathLength = baseUrl.length; i = baseUrl.lastIndexOf('#'); pathLength = i >= 0 ? i : pathLength; i = baseUrl.lastIndexOf('?', pathLength); @@ -423,23 +424,43 @@ var XRefParseException = (function XRefParseExceptionClosure() { function bytesToString(bytes) { - var strBuf = []; var length = bytes.length; - for (var n = 0; n < length; ++n) { - strBuf.push(String.fromCharCode(bytes[n])); + var MAX_ARGUMENT_COUNT = 8192; + if (length < MAX_ARGUMENT_COUNT) { + return String.fromCharCode.apply(null, bytes); + } + var strBuf = []; + for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) { + var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); + var chunk = bytes.subarray(i, chunkEnd); + strBuf.push(String.fromCharCode.apply(null, chunk)); } return strBuf.join(''); } +function stringToArray(str) { + var length = str.length; + var array = []; + for (var i = 0; i < length; ++i) { + array[i] = str.charCodeAt(i); + } + return array; +} + function stringToBytes(str) { var length = str.length; var bytes = new Uint8Array(length); - for (var n = 0; n < length; ++n) { - bytes[n] = str.charCodeAt(n) & 0xFF; + for (var i = 0; i < length; ++i) { + bytes[i] = str.charCodeAt(i) & 0xFF; } return bytes; } +function string32(value) { + return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff, + (value >> 8) & 0xff, value & 0xff); +} + // Lazy test the endianness of the platform // NOTE: This will be 'true' for simulated TypedArrays function isLittleEndian() { @@ -971,17 +992,18 @@ var StatTimer = (function StatTimerClosure() { delete this.started[name]; }, toString: function StatTimer_toString() { + var i, ii; var times = this.times; var out = ''; // Find the longest name for padding purposes. var longest = 0; - for (var i = 0, ii = times.length; i < ii; ++i) { + for (i = 0, ii = times.length; i < ii; ++i) { var name = times[i]['name']; if (name.length > longest) { longest = name.length; } } - for (var i = 0, ii = times.length; i < ii; ++i) { + for (i = 0, ii = times.length; i < ii; ++i) { var span = times[i]; var duration = span.end - span.start; out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n'; @@ -1191,10 +1213,10 @@ var ColorSpace = (function ColorSpaceClosure() { var rgbBuf = null; var numComponentColors = 1 << bpc; var needsResizing = originalHeight != height || originalWidth != width; + var i, ii; 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 @@ -1208,18 +1230,20 @@ var ColorSpace = (function ColorSpaceClosure() { // we are reparsing colorspaces too much?). var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : new Uint16Array(numComponentColors); - for (var i = 0; i < numComponentColors; i++) { + var key; + for (i = 0; i < numComponentColors; i++) { allColors[i] = i; } var colorMap = new Uint8Array(numComponentColors * 3); this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, /* alpha01 = */ 0); + var destPos, rgbPos; if (!needsResizing) { // Fill in the RGB values directly into |dest|. - var destPos = 0; - for (var i = 0; i < count; ++i) { - var key = comps[i] * 3; + destPos = 0; + for (i = 0; i < count; ++i) { + key = comps[i] * 3; dest[destPos++] = colorMap[key]; dest[destPos++] = colorMap[key + 1]; dest[destPos++] = colorMap[key + 2]; @@ -1227,9 +1251,9 @@ var ColorSpace = (function ColorSpaceClosure() { } } else { rgbBuf = new Uint8Array(count * 3); - var rgbPos = 0; - for (var i = 0; i < count; ++i) { - var key = comps[i] * 3; + rgbPos = 0; + for (i = 0; i < count; ++i) { + key = comps[i] * 3; rgbBuf[rgbPos++] = colorMap[key]; rgbBuf[rgbPos++] = colorMap[key + 1]; rgbBuf[rgbPos++] = colorMap[key + 2]; @@ -1252,9 +1276,9 @@ var ColorSpace = (function ColorSpaceClosure() { rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth, originalHeight, width, height); } - var rgbPos = 0; - var destPos = 0; - for (var i = 0, ii = width * actualHeight; i < ii; i++) { + rgbPos = 0; + destPos = 0; + for (i = 0, ii = width * actualHeight; i < ii; i++) { dest[destPos++] = rgbBuf[rgbPos++]; dest[destPos++] = rgbBuf[rgbPos++]; dest[destPos++] = rgbBuf[rgbPos++]; @@ -1280,6 +1304,7 @@ var ColorSpace = (function ColorSpaceClosure() { ColorSpace.fromIR = function ColorSpace_fromIR(IR) { var name = isArray(IR) ? IR[0] : IR; + var whitePoint, blackPoint; switch (name) { case 'DeviceGrayCS': @@ -1289,8 +1314,8 @@ var ColorSpace = (function ColorSpaceClosure() { case 'DeviceCmykCS': return this.singletons.cmyk; case 'CalGrayCS': - var whitePoint = IR[1].WhitePoint; - var blackPoint = IR[1].BlackPoint; + whitePoint = IR[1].WhitePoint; + blackPoint = IR[1].BlackPoint; var gamma = IR[1].Gamma; return new CalGrayCS(whitePoint, blackPoint, gamma); case 'PatternCS': @@ -1312,8 +1337,8 @@ var ColorSpace = (function ColorSpaceClosure() { return new AlternateCS(numComps, ColorSpace.fromIR(alt), PDFFunction.fromIR(tintFnIR)); case 'LabCS': - var whitePoint = IR[1].WhitePoint; - var blackPoint = IR[1].BlackPoint; + whitePoint = IR[1].WhitePoint; + blackPoint = IR[1].BlackPoint; var range = IR[1].Range; return new LabCS(whitePoint, blackPoint, range); default: @@ -1358,6 +1383,7 @@ var ColorSpace = (function ColorSpaceClosure() { } else if (isArray(cs)) { mode = cs[0].name; this.mode = mode; + var numComps, params; switch (mode) { case 'DeviceGray': @@ -1370,14 +1396,14 @@ var ColorSpace = (function ColorSpaceClosure() { case 'CMYK': return 'DeviceCmykCS'; case 'CalGray': - var params = cs[1].getAll(); + 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'); + numComps = dict.get('N'); if (numComps == 1) { return 'DeviceGrayCS'; } else if (numComps == 3) { @@ -1404,7 +1430,7 @@ var ColorSpace = (function ColorSpaceClosure() { case 'Separation': case 'DeviceN': var name = cs[1]; - var numComps = 1; + numComps = 1; if (isName(name)) { numComps = 1; } else if (isArray(name)) { @@ -1414,7 +1440,7 @@ var ColorSpace = (function ColorSpaceClosure() { var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); return ['AlternateCS', numComps, alt, tintFnIR]; case 'Lab': - var params = cs[1].getAll(); + params = cs[1].getAll(); return ['LabCS', params]; default: error('unimplemented color space object "' + mode + '"'); @@ -1509,13 +1535,14 @@ var AlternateCS = (function AlternateCSClosure() { var numComps = this.numComps; var scaled = new Float32Array(numComps); - for (var i = 0; i < count; i++) { - for (var j = 0; j < numComps; j++) { + var i, j; + for (i = 0; i < count; i++) { + for (j = 0; j < numComps; j++) { scaled[j] = src[srcOffset++] * scale; } var tinted = tintFn(scaled); if (usesZeroToOneRange) { - for (var j = 0; j < baseNumComps; j++) { + for (j = 0; j < baseNumComps; j++) { baseBuf[pos++] = tinted[j] * 255; } } else { @@ -2050,8 +2077,9 @@ var PDFFunction = (function PDFFunctionClosure() { return { getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps, str) { + var i, ii; var length = 1; - for (var i = 0, ii = size.length; i < ii; i++) { + for (i = 0, ii = size.length; i < ii; i++) { length *= size[i]; } length *= outputSize; @@ -2064,7 +2092,7 @@ var PDFFunction = (function PDFFunctionClosure() { var strBytes = str.getBytes((length * bps + 7) / 8); var strIdx = 0; - for (var i = 0; i < length; i++) { + for (i = 0; i < length; i++) { while (codeSize < bps) { codeBuf <<= 8; codeBuf |= strBytes[strIdx++]; @@ -2207,13 +2235,14 @@ var PDFFunction = (function PDFFunctionClosure() { var cubeVertices = 1 << m; var cubeN = new Float64Array(cubeVertices); var cubeVertex = new Uint32Array(cubeVertices); - for (var j = 0; j < cubeVertices; j++) { + var i, j; + for (j = 0; j < cubeVertices; j++) { cubeN[j] = 1; } var k = n, pos = 1; // Map x_i to y_j for 0 <= i < m using the sampled function. - for (var i = 0; i < m; ++i) { + for (i = 0; i < m; ++i) { // x_i' = min(max(x_i, Domain_2i), Domain_2i+1) var domain_2i = domain[i][0]; var domain_2i_1 = domain[i][1]; @@ -2234,7 +2263,7 @@ var PDFFunction = (function PDFFunctionClosure() { var n1 = e - e0; // (e - e0) / (e1 - e0); var offset0 = e0 * k; var offset1 = offset0 + k; // e1 * k - for (var j = 0; j < cubeVertices; j++) { + for (j = 0; j < cubeVertices; j++) { if (j & pos) { cubeN[j] *= n1; cubeVertex[j] += offset1; @@ -2249,10 +2278,10 @@ var PDFFunction = (function PDFFunctionClosure() { } var y = new Float64Array(n); - for (var j = 0; j < n; ++j) { + for (j = 0; j < n; ++j) { // Sum all cube vertices' samples portions var rj = 0; - for (var i = 0; i < cubeVertices; i++) { + for (i = 0; i < cubeVertices; i++) { rj += samples[cubeVertex[i] + j] * cubeN[i]; } @@ -2813,7 +2842,7 @@ var Annotation = (function AnnotationClosure() { var data = this.data = {}; data.subtype = dict.get('Subtype').name; - var rect = dict.get('Rect'); + var rect = dict.get('Rect') || [0, 0, 0, 0]; data.rect = Util.normalizeRect(rect); data.annotationFlags = dict.get('F'); @@ -2845,7 +2874,8 @@ var Annotation = (function AnnotationClosure() { var isInvalid = false; var numPositive = 0; for (var i = 0; i < dashArrayLength; i++) { - if (!(+dashArray[i] >= 0)) { + var validNumber = (+dashArray[i] >= 0); + if (!validNumber) { isInvalid = true; break; } else if (dashArray[i] > 0) { @@ -3111,7 +3141,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { var fieldType = Util.getInheritableProperty(dict, 'FT'); data.fieldType = isName(fieldType) ? fieldType.name : ''; data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; - this.fieldResources = Util.getInheritableProperty(dict, 'DR') || new Dict(); + this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty; // Building the full field name by collecting the field and // its ancestors 'T' data and joining them using '.'. @@ -3406,10 +3436,12 @@ var TextAnnotation = (function TextAnnotationClosure() { var content = document.createElement('div'); content.className = 'annotTextContent'; content.setAttribute('hidden', true); + + var i, ii; if (item.hasBgColor) { var color = item.color; var rgb = []; - for (var i = 0; i < 3; ++i) { + for (i = 0; i < 3; ++i) { // Enlighten the color (70%) var c = Math.round(color[i] * 255); rgb[i] = Math.round((255 - c) * 0.7) + c; @@ -3426,7 +3458,7 @@ var TextAnnotation = (function TextAnnotationClosure() { } else { var e = document.createElement('span'); var lines = item.content.split(/(?:\r\n?|\n)/); - for (var i = 0, ii = lines.length; i < ii; ++i) { + for (i = 0, ii = lines.length; i < ii; ++i) { var line = lines[i]; e.appendChild(document.createTextNode(line)); if (i < (ii - 1)) { @@ -3681,6 +3713,13 @@ PDFJS.postMessageTransfers = (PDFJS.postMessageTransfers === undefined ? PDFJS.disableCreateObjectURL = (PDFJS.disableCreateObjectURL === undefined ? false : PDFJS.disableCreateObjectURL); +/** + * Disables WebGL usage. + * @var {boolean} + */ +PDFJS.disableWebGL = (PDFJS.disableWebGL === undefined ? + true : PDFJS.disableWebGL); + /** * Controls the logging level. * The constants from PDFJS.VERBOSITY_LEVELS should be used: @@ -3898,16 +3937,52 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { return PDFDocumentProxy; })(); +/** + * Page text content. + * + * @typedef {Object} TextContent + * @property {array} items - array of {@link TextItem} + * @property {Object} styles - {@link TextStyles} objects, indexed by font + * name. + */ + /** * Page text content part. * - * @typedef {Object} BidiText + * @typedef {Object} TextItem * @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. + * @property {array} transform - transformation matrix. + * @property {number} width - width in device space. + * @property {number} height - height in device space. + * @property {string} fontName - font name used by pdf.js for converted font. + */ + +/** + * Text style. + * + * @typedef {Object} TextStyle + * @property {number} ascent - font ascent. + * @property {number} descent - font descent. + * @property {boolean} vertical - text is in vertical mode. + * @property {string} fontFamily - possible font family + */ + +/** + * Page render parameters. + * + * @typedef {Object} RenderParameters + * @property {Object} canvasContext - A 2D context of a DOM Canvas object. + * @property {PageViewport} viewport - Rendering viewport obtained by + * calling of PDFPage.getViewport method. + * @property {string} intent - Rendering intent, can be 'display' or 'print' + * (default value is 'display'). + * @property {Object} imageLayer - (optional) An object that has beginLayout, + * endLayout and appendImage functions. + * @property {function} continueCallback - (optional) A function that will be + * called each time the rendering is paused. To continue + * rendering call the function that is the first argument + * to the callback. */ /** @@ -3982,20 +4057,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() { }, /** * Begins the process of rendering a page to the desired context. - * @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 - * appendText functions., - * imageLayer(optional): An object that has beginLayout, endLayout and - * appendImage functions., - * continueCallback(optional): A function that will be called each time - * the rendering is paused. To continue - * rendering call the function that is the - * first argument to the callback. - * }. - * @return {RenderTask} An extended promise that is resolved when the page - * finishes rendering (see RenderTask). + * @param {RenderParameters} params Page render parameters. + * @return {RenderTask} An object that contains the promise, which + * is resolved when the page finishes rendering. */ render: function PDFPageProxy_render(params) { var stats = this.stats; @@ -4081,8 +4145,8 @@ var PDFPageProxy = (function PDFPageProxyClosure() { return renderTask; }, /** - * @return {Promise} That is resolved with the array of {@link BidiText} - * objects that represent the page text content. + * @return {Promise} That is resolved a {@link TextContent} + * object that represent the page text content. */ getTextContent: function PDFPageProxy_getTextContent() { var promise = new PDFJS.LegacyPromise(); @@ -4139,8 +4203,9 @@ var PDFPageProxy = (function PDFPageProxyClosure() { _renderPageChunk: function PDFPageProxy_renderPageChunk(operatorListChunk, intent) { var intentState = this.intentStates[intent]; + var i, ii; // Add the new chunk to the current operator list. - for (var i = 0, ii = operatorListChunk.length; i < ii; i++) { + for (i = 0, ii = operatorListChunk.length; i < ii; i++) { intentState.operatorList.fnArray.push(operatorListChunk.fnArray[i]); intentState.operatorList.argsArray.push( operatorListChunk.argsArray[i]); @@ -4148,7 +4213,7 @@ var PDFPageProxy = (function PDFPageProxyClosure() { intentState.operatorList.lastChunk = operatorListChunk.lastChunk; // Notify all the rendering tasks there are more operators to be consumed. - for (var i = 0; i < intentState.renderTasks.length; i++) { + for (i = 0; i < intentState.renderTasks.length; i++) { intentState.renderTasks[i].operatorListChanged(); } @@ -4419,17 +4484,18 @@ var WorkerTransport = (function WorkerTransportClosure() { var pageIndex = data[1]; var type = data[2]; var pageProxy = this.pageCache[pageIndex]; + var imageData; if (pageProxy.objs.hasData(id)) { return; } switch (type) { case 'JpegStream': - var imageData = data[3]; + imageData = data[3]; loadJpegStream(id, imageData, pageProxy.objs); break; case 'Image': - var imageData = data[3]; + imageData = data[3]; pageProxy.objs.resolve(id, imageData); // heuristics that will allow not to store large data @@ -4485,15 +4551,16 @@ var WorkerTransport = (function WorkerTransportClosure() { var tmpCtx = tmpCanvas.getContext('2d'); tmpCtx.drawImage(img, 0, 0); var data = tmpCtx.getImageData(0, 0, width, height).data; + var i, j; if (components == 3) { - for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { + for (i = 0, j = 0; i < rgbaLength; i += 4, j += 3) { buf[j] = data[i]; buf[j + 1] = data[i + 1]; buf[j + 2] = data[i + 2]; } } else if (components == 1) { - for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) { + for (i = 0, j = 0; i < rgbaLength; i += 4, j++) { buf[j] = data[i]; } } @@ -4536,7 +4603,7 @@ var WorkerTransport = (function WorkerTransportClosure() { if (pageIndex in this.pagePromises) { return this.pagePromises[pageIndex]; } - var promise = new PDFJS.LegacyPromise(); + promise = new PDFJS.LegacyPromise(); this.pagePromises[pageIndex] = promise; this.messageHandler.send('GetPageRequest', { pageIndex: pageIndex }); return promise; @@ -4715,6 +4782,18 @@ var RenderTask = (function RenderTaskClosure() { cancel: function RenderTask_cancel() { this.internalRenderTask.cancel(); this.promise.reject(new Error('Rendering is cancelled')); + }, + + /** + * Registers callback to indicate the rendering task completion. + * + * @param {function} onFulfilled The callback for the rendering completion. + * @param {function} onRejected The callback for the rendering failure. + * @return {Promise} A promise that is resolved after the onFulfilled or + * onRejected callback. + */ + then: function RenderTask_then(onFulfilled, onRejected) { + return this.promise.then(onFulfilled, onRejected); } }; @@ -4759,8 +4838,7 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { var params = this.params; this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs, - this.objs, params.textLayer, - params.imageLayer); + this.objs, params.imageLayer); this.gfx.beginDrawing(params.viewport, transparency); this.operatorListIdx = 0; @@ -4915,6 +4993,7 @@ var Metadata = PDFJS.Metadata = (function MetadataClosure() { // Minimal font size that would be used during canvas fillText operations. var MIN_FONT_SIZE = 16; +var MAX_GROUP_SIZE = 4096; var COMPILE_TYPE3_GLYPHS = true; @@ -5289,7 +5368,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // before it stops and shedules a continue of execution. var EXECUTION_TIME = 15; - function CanvasGraphics(canvasCtx, commonObjs, objs, textLayer, imageLayer) { + function CanvasGraphics(canvasCtx, commonObjs, objs, imageLayer) { this.ctx = canvasCtx; this.current = new CanvasExtraState(); this.stateStack = []; @@ -5299,7 +5378,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.xobjs = null; this.commonObjs = commonObjs; this.objs = objs; - this.textLayer = textLayer; this.imageLayer = imageLayer; this.groupStack = []; this.processingType3 = null; @@ -5341,13 +5419,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var partialChunkHeight = height - fullChunks * fullChunkHeight; var chunkImgData = ctx.createImageData(width, fullChunkHeight); - var srcPos = 0; + var srcPos = 0, destPos; var src = imgData.data; var dest = chunkImgData.data; + var i, j, thisChunkHeight, elemsInThisChunk; // There are multiple forms in which the pixel data can be passed, and // imgData.kind tells us which one this is. - if (imgData.kind === ImageKind.GRAYSCALE_1BPP) { // Grayscale, 1 bit per pixel (i.e. black-and-white). var destDataLength = dest.length; @@ -5359,11 +5437,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var white = 0xFFFFFFFF; var black = (PDFJS.isLittleEndian || !PDFJS.hasCanvasTypedArrays) ? 0xFF000000 : 0x000000FF; - for (var i = 0; i < totalChunks; i++) { - var thisChunkHeight = + for (i = 0; i < totalChunks; i++) { + thisChunkHeight = (i < fullChunks) ? fullChunkHeight : partialChunkHeight; - var destPos = 0; - for (var j = 0; j < thisChunkHeight; j++) { + destPos = 0; + for (j = 0; j < thisChunkHeight; j++) { var srcDiff = srcLength - srcPos; var k = 0; var kEnd = (srcDiff > fullSrcDiff) ? width : srcDiff * 8 - 7; @@ -5398,29 +5476,27 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); } - } else if (imgData.kind === ImageKind.RGBA_32BPP) { // RGBA, 32-bits per pixel. - for (var i = 0; i < totalChunks; i++) { - var thisChunkHeight = + for (i = 0; i < totalChunks; i++) { + thisChunkHeight = (i < fullChunks) ? fullChunkHeight : partialChunkHeight; - var elemsInThisChunk = imgData.width * thisChunkHeight * 4; + elemsInThisChunk = imgData.width * thisChunkHeight * 4; dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); srcPos += elemsInThisChunk; ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); } - } else if (imgData.kind === ImageKind.RGB_24BPP) { // RGB, 24-bits per pixel. - for (var i = 0; i < totalChunks; i++) { - var thisChunkHeight = + for (i = 0; i < totalChunks; i++) { + thisChunkHeight = (i < fullChunks) ? fullChunkHeight : partialChunkHeight; - var elemsInThisChunk = imgData.width * thisChunkHeight * 3; - var destPos = 0; - for (var j = 0; j < elemsInThisChunk; j += 3) { + elemsInThisChunk = imgData.width * thisChunkHeight * 3; + destPos = 0; + for (j = 0; j < elemsInThisChunk; j += 3) { dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++]; dest[destPos++] = src[srcPos++]; @@ -5428,9 +5504,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } ctx.putImageData(chunkImgData, 0, i * fullChunkHeight); } - } else { - error('bad image kind: ' + imgData.kind); + error('bad image kind: ' + imgData.kind); } } @@ -5489,15 +5564,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } } - function composeSMask(ctx, smask, layerCtx) { - var mask = smask.canvas; - var maskCtx = smask.context; - var width = mask.width, height = mask.height; - + function genericComposeSMask(maskCtx, layerCtx, width, height, + subtype, backdrop) { var addBackdropFn; - if (smask.backdrop) { - var cs = smask.colorSpace || ColorSpace.singletons.rgb; - var backdrop = cs.getRgb(smask.backdrop, 0); + if (backdrop) { addBackdropFn = function (r0, g0, b0, bytes) { var length = bytes.length; for (var i = 3; i < length; i += 4) { @@ -5519,7 +5589,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } var composeFn; - if (smask.subtype === 'Luminosity') { + if (subtype === 'Luminosity') { composeFn = function (maskDataBytes, layerDataBytes) { var length = maskDataBytes.length; for (var i = 3; i < length; i += 4) { @@ -5540,7 +5610,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } // processing image in chunks to save memory - var chunkSize = 16; + var PIXELS_TO_PROCESS = 65536; + var chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width)); for (var row = 0; row < height; row += chunkSize) { var chunkHeight = Math.min(chunkSize, height - row); var maskData = maskCtx.getImageData(0, row, width, chunkHeight); @@ -5551,9 +5622,30 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { maskCtx.putImageData(layerData, 0, row); } + } - ctx.setTransform(1, 0, 0, 1, 0, 0); - ctx.drawImage(mask, smask.offsetX, smask.offsetY); + function composeSMask(ctx, smask, layerCtx) { + var mask = smask.canvas; + var maskCtx = smask.context; + + ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY, + smask.offsetX, smask.offsetY); + + var backdrop; + if (smask.backdrop) { + var cs = smask.colorSpace || ColorSpace.singletons.rgb; + backdrop = cs.getRgb(smask.backdrop, 0); + } + if (WebGLUtils.isEnabled) { + var composed = WebGLUtils.composeSMask(layerCtx.canvas, mask, + {subtype: smask.subtype, backdrop: backdrop}); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.drawImage(composed, smask.offsetX, smask.offsetY); + return; + } + genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height, + smask.subtype, backdrop); + ctx.drawImage(mask, 0, 0); } var LINE_CAP_STYLES = ['butt', 'round', 'square']; @@ -5588,9 +5680,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.baseTransform = this.ctx.mozCurrentTransform.slice(); - if (this.textLayer) { - this.textLayer.beginLayout(); - } if (this.imageLayer) { this.imageLayer.beginLayout(); } @@ -5670,10 +5759,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { endDrawing: function CanvasGraphics_endDrawing() { this.ctx.restore(); CachedCanvases.clear(); + WebGLUtils.clear(); - if (this.textLayer) { - this.textLayer.endLayout(); - } if (this.imageLayer) { this.imageLayer.endLayout(); } @@ -5793,6 +5880,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.ctx.save(); var groupCtx = scratchCanvas.context; + groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY); groupCtx.translate(-activeSMask.offsetX, -activeSMask.offsetY); groupCtx.transform.apply(groupCtx, currentTransform); @@ -6102,33 +6190,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { ctx.scale(-current.textHScale, 1); } }, - createTextGeometry: function CanvasGraphics_createTextGeometry() { - var geometry = {}; - var ctx = this.ctx; - var font = this.current.font; - var ctxMatrix = ctx.mozCurrentTransform; - var a = ctxMatrix[0], b = ctxMatrix[1], c = ctxMatrix[2]; - var d = ctxMatrix[3], e = ctxMatrix[4], f = ctxMatrix[5]; - var sx = (a >= 0) ? - Math.sqrt((a * a) + (b * b)) : -Math.sqrt((a * a) + (b * b)); - var sy = (d >= 0) ? - Math.sqrt((c * c) + (d * d)) : -Math.sqrt((c * c) + (d * d)); - var angle = Math.atan2(b, a); - var x = e; - var y = f; - geometry.x = x; - geometry.y = y; - geometry.hScale = sx; - geometry.vScale = sy; - geometry.angle = angle; - geometry.spaceWidth = font.spaceWidth; - geometry.fontName = font.loadedName; - geometry.fontFamily = font.fallbackName; - geometry.fontSize = this.current.fontSize; - geometry.ascent = font.ascent; - geometry.descent = font.descent; - return geometry; - }, paintChar: function (character, x, y) { var ctx = this.ctx; @@ -6200,7 +6261,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { return shadow(this, 'isFontSubpixelAAEnabled', enabled); }, - showText: function CanvasGraphics_showText(glyphs, skipTextSelection) { + showText: function CanvasGraphics_showText(glyphs) { var ctx = this.ctx; var current = this.current; var font = current.font; @@ -6211,12 +6272,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var textHScale = current.textHScale * current.fontDirection; var fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX; var glyphsLength = glyphs.length; - var textLayer = this.textLayer; - var geom; - var textSelection = textLayer && !skipTextSelection ? true : false; - var canvasWidth = 0.0; var vertical = font.vertical; var defaultVMetrics = font.defaultVMetrics; + var i, glyph, width; + var VERTICAL_TEXT_ROTATION = Math.PI / 2; + + if (fontSize === 0) { + return; + } // Type3 fonts - each glyph is a "mini-PDF" if (font.coded) { @@ -6226,15 +6289,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { ctx.scale(textHScale, 1); - if (textSelection) { - this.save(); - ctx.scale(1, -1); - geom = this.createTextGeometry(); - this.restore(); - } - for (var i = 0; i < glyphsLength; ++i) { - - var glyph = glyphs[i]; + for (i = 0; i < glyphsLength; ++i) { + glyph = glyphs[i]; if (glyph === null) { // word break this.ctx.translate(wordSpacing, 0); @@ -6250,13 +6306,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.restore(); var transformed = Util.applyTransform([glyph.width, 0], fontMatrix); - var width = (transformed[0] * fontSize + charSpacing) * - current.fontDirection; + width = ((transformed[0] * fontSize + charSpacing) * + current.fontDirection); ctx.translate(width, 0); current.x += width * textHScale; - - canvasWidth += width; } ctx.restore(); this.processingType3 = null; @@ -6273,10 +6327,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { lineWidth /= scale; } - if (textSelection) { - geom = this.createTextGeometry(); - } - if (fontSizeScale != 1.0) { ctx.scale(fontSizeScale, fontSizeScale); lineWidth /= fontSizeScale; @@ -6285,8 +6335,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { ctx.lineWidth = lineWidth; var x = 0; - for (var i = 0; i < glyphsLength; ++i) { - var glyph = glyphs[i]; + for (i = 0; i < glyphsLength; ++i) { + glyph = glyphs[i]; if (glyph === null) { // word break x += current.fontDirection * wordSpacing; @@ -6301,7 +6351,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { vx = -vx * fontSize * current.fontMatrix[0]; var vy = vmetric[2] * fontSize * current.fontMatrix[0]; } - var width = vmetric ? -vmetric[0] : glyph.width; + width = vmetric ? -vmetric[0] : glyph.width; var charWidth = width * fontSize * current.fontMatrix[0] + charSpacing * current.fontDirection; var accent = glyph.accent; @@ -6340,8 +6390,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { x += charWidth; - canvasWidth += charWidth; - if (restoreNeeded) { ctx.restore(); } @@ -6353,17 +6401,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } ctx.restore(); } - - if (textSelection) { - geom.canvasWidth = canvasWidth; - if (vertical) { - var VERTICAL_TEXT_ROTATION = Math.PI / 2; - geom.angle += VERTICAL_TEXT_ROTATION; - } - this.textLayer.appendText(geom); - } - - return canvasWidth; }, showSpacedText: function CanvasGraphics_showSpacedText(arr) { var ctx = this.ctx; @@ -6373,19 +6410,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // TJ array's number is independent from fontMatrix var textHScale = current.textHScale * 0.001 * current.fontDirection; var arrLength = arr.length; - var textLayer = this.textLayer; - var geom; - var canvasWidth = 0.0; - var textSelection = textLayer ? true : false; var vertical = font.vertical; - var spacingAccumulator = 0; - - if (textSelection) { - ctx.save(); - this.applyTextTransforms(); - geom = this.createTextGeometry(); - ctx.restore(); - } for (var i = 0; i < arrLength; ++i) { var e = arr[i]; @@ -6397,27 +6422,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { current.x += spacingLength; } - if (textSelection) { - spacingAccumulator += spacingLength; - } } else { - var shownCanvasWidth = this.showText(e, true); - - if (textSelection) { - canvasWidth += spacingAccumulator + shownCanvasWidth; - spacingAccumulator = 0; - } + this.showText(e); } } - - if (textSelection) { - geom.canvasWidth = canvasWidth; - if (vertical) { - var VERTICAL_TEXT_ROTATION = Math.PI / 2; - geom.angle += VERTICAL_TEXT_ROTATION; - } - this.textLayer.appendText(geom); - } }, nextLineShowText: function CanvasGraphics_nextLineShowText(text) { this.nextLine(); @@ -6465,6 +6473,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.current.strokeColor = color; }, getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR, cs) { + var pattern; if (IR[0] == 'TilingPattern') { var args = IR[1]; var base = cs.base; @@ -6474,10 +6483,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { color = base.getRgb(args, 0); } - var pattern = new TilingPattern(IR, color, this.ctx, this.objs, - this.commonObjs, this.baseTransform); + pattern = new TilingPattern(IR, color, this.ctx, this.objs, + this.commonObjs, this.baseTransform); } else { - var pattern = getShadingPatternFromIR(IR); + pattern = getShadingPatternFromIR(IR); } return pattern; }, @@ -6668,8 +6677,19 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0]; // Use ceil in case we're between sizes so we don't create canvas that is // too small and make the canvas at least 1x1 pixels. - var drawnWidth = Math.max(Math.ceil(bounds[2] - bounds[0]), 1); - var drawnHeight = Math.max(Math.ceil(bounds[3] - bounds[1]), 1); + var offsetX = Math.floor(bounds[0]); + var offsetY = Math.floor(bounds[1]); + var drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1); + var drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1); + var scaleX = 1, scaleY = 1; + if (drawnWidth > MAX_GROUP_SIZE) { + scaleX = drawnWidth / MAX_GROUP_SIZE; + drawnWidth = MAX_GROUP_SIZE; + } + if (drawnHeight > MAX_GROUP_SIZE) { + scaleY = drawnHeight / MAX_GROUP_SIZE; + drawnHeight = MAX_GROUP_SIZE; + } var cacheId = 'groupAt' + this.groupLevel; if (group.smask) { @@ -6682,8 +6702,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // Since we created a new canvas that is just the size of the bounding box // we have to translate the group ctx. - var offsetX = bounds[0]; - var offsetY = bounds[1]; + groupCtx.scale(1 / scaleX, 1 / scaleY); groupCtx.translate(-offsetX, -offsetY); groupCtx.transform.apply(groupCtx, currentTransform); @@ -6694,6 +6713,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { context: groupCtx, offsetX: offsetX, offsetY: offsetY, + scaleX: scaleX, + scaleY: scaleY, subtype: group.smask.subtype, backdrop: group.smask.backdrop, colorSpace: group.colorSpace && ColorSpace.fromIR(group.colorSpace) @@ -6703,6 +6724,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // right location. currentCtx.setTransform(1, 0, 0, 1, 0, 0); currentCtx.translate(offsetX, offsetY); + currentCtx.scale(scaleX, scaleY); } // The transparency group inherits all off the current graphics state // except the blend mode, soft mask, and alpha constants. @@ -6942,12 +6964,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var c = currentTransform[2], d = currentTransform[3]; var heightScale = Math.max(Math.sqrt(c * c + d * d), 1); - var imgToPaint; + var imgToPaint, tmpCanvas; // instanceof HTMLElement does not work in jsdom node.js module if (imgData instanceof HTMLElement || !imgData.data) { imgToPaint = imgData; } else { - var tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height); + tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height); var tmpCtx = tmpCanvas.context; putBinaryImageData(tmpCtx, imgData); imgToPaint = tmpCanvas.canvas; @@ -6969,8 +6991,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { newHeight = Math.ceil(paintHeight / 2); heightScale /= paintHeight / newHeight; } - var tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId, - newWidth, newHeight); + tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); tmpCtx = tmpCanvas.context; tmpCtx.clearRect(0, 0, newWidth, newHeight); tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, @@ -7109,6 +7130,416 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { +var WebGLUtils = (function WebGLUtilsClosure() { + function loadShader(gl, code, shaderType) { + var shader = gl.createShader(shaderType); + gl.shaderSource(shader, code); + gl.compileShader(shader); + var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); + if (!compiled) { + var errorMsg = gl.getShaderInfoLog(shader); + throw new Error('Error during shader compilation: ' + errorMsg); + } + return shader; + } + function createVertexShader(gl, code) { + return loadShader(gl, code, gl.VERTEX_SHADER); + } + function createFragmentShader(gl, code) { + return loadShader(gl, code, gl.FRAGMENT_SHADER); + } + function createProgram(gl, shaders) { + var program = gl.createProgram(); + for (var i = 0, ii = shaders.length; i < ii; ++i) { + gl.attachShader(program, shaders[i]); + } + gl.linkProgram(program); + var linked = gl.getProgramParameter(program, gl.LINK_STATUS); + if (!linked) { + var errorMsg = gl.getProgramInfoLog(program); + throw new Error('Error during program linking: ' + errorMsg); + } + return program; + } + function createTexture(gl, image, textureId) { + gl.activeTexture(textureId); + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + // Set the parameters so we can render any size image. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + // Upload the image into the texture. + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + return texture; + } + + var currentGL, currentCanvas; + function generageGL() { + if (currentGL) { + return; + } + currentCanvas = document.createElement('canvas'); + currentGL = currentCanvas.getContext('webgl', + { premultipliedalpha: false }); + } + + var smaskVertexShaderCode = '\ + attribute vec2 a_position; \ + attribute vec2 a_texCoord; \ + \ + uniform vec2 u_resolution; \ + \ + varying vec2 v_texCoord; \ + \ + void main() { \ + vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; \ + gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \ + \ + v_texCoord = a_texCoord; \ + } '; + + var smaskFragmentShaderCode = '\ + precision mediump float; \ + \ + uniform vec4 u_backdrop; \ + uniform int u_subtype; \ + uniform sampler2D u_image; \ + uniform sampler2D u_mask; \ + \ + varying vec2 v_texCoord; \ + \ + void main() { \ + vec4 imageColor = texture2D(u_image, v_texCoord); \ + vec4 maskColor = texture2D(u_mask, v_texCoord); \ + if (u_backdrop.a > 0.0) { \ + maskColor.rgb = maskColor.rgb * maskColor.a + \ + u_backdrop.rgb * (1.0 - maskColor.a); \ + } \ + float lum; \ + if (u_subtype == 0) { \ + lum = maskColor.a; \ + } else { \ + lum = maskColor.r * 0.3 + maskColor.g * 0.59 + \ + maskColor.b * 0.11; \ + } \ + imageColor.a *= lum; \ + imageColor.rgb *= imageColor.a; \ + gl_FragColor = imageColor; \ + } '; + + var smaskCache = null; + + function initSmaskGL() { + var canvas, gl; + + generageGL(); + canvas = currentCanvas; + currentCanvas = null; + gl = currentGL; + currentGL = null; + + // setup a GLSL program + var vertexShader = createVertexShader(gl, smaskVertexShaderCode); + var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode); + var program = createProgram(gl, [vertexShader, fragmentShader]); + gl.useProgram(program); + + var cache = {}; + cache.gl = gl; + cache.canvas = canvas; + cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution'); + cache.positionLocation = gl.getAttribLocation(program, 'a_position'); + cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop'); + cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype'); + + var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord'); + var texLayerLocation = gl.getUniformLocation(program, 'u_image'); + var texMaskLocation = gl.getUniformLocation(program, 'u_mask'); + + // provide texture coordinates for the rectangle. + var texCoordBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0.0, 0.0, + 1.0, 0.0, + 0.0, 1.0, + 0.0, 1.0, + 1.0, 0.0, + 1.0, 1.0]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(texCoordLocation); + gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); + + gl.uniform1i(texLayerLocation, 0); + gl.uniform1i(texMaskLocation, 1); + + smaskCache = cache; + } + + function composeSMask(layer, mask, properties) { + var width = layer.width, height = layer.height; + + if (!smaskCache) { + initSmaskGL(); + } + var cache = smaskCache,canvas = cache.canvas, gl = cache.gl; + canvas.width = width; + canvas.height = height; + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + gl.uniform2f(cache.resolutionLocation, width, height); + + if (properties.backdrop) { + gl.uniform4f(cache.resolutionLocation, properties.backdrop[0], + properties.backdrop[1], properties.backdrop[2], 1); + } else { + gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0); + } + gl.uniform1i(cache.subtypeLocation, + properties.subtype === 'Luminosity' ? 1 : 0); + + // Create a textures + var texture = createTexture(gl, layer, gl.TEXTURE0); + var maskTexture = createTexture(gl, mask, gl.TEXTURE1); + + + // Create a buffer and put a single clipspace rectangle in + // it (2 triangles) + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0, 0, + width, 0, + 0, height, + 0, height, + width, 0, + width, height]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(cache.positionLocation); + gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0); + + // draw + gl.clearColor(0, 0, 0, 0); + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.drawArrays(gl.TRIANGLES, 0, 6); + + gl.flush(); + + gl.deleteTexture(texture); + gl.deleteTexture(maskTexture); + gl.deleteBuffer(buffer); + + return canvas; + } + + var figuresVertexShaderCode = '\ + attribute vec2 a_position; \ + attribute vec3 a_color; \ + \ + uniform vec2 u_resolution; \ + uniform vec2 u_scale; \ + uniform vec2 u_offset; \ + \ + varying vec4 v_color; \ + \ + void main() { \ + vec2 position = (a_position + u_offset) * u_scale; \ + vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0; \ + gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \ + \ + v_color = vec4(a_color / 255.0, 1.0); \ + } '; + + var figuresFragmentShaderCode = '\ + precision mediump float; \ + \ + varying vec4 v_color; \ + \ + void main() { \ + gl_FragColor = v_color; \ + } '; + + var figuresCache = null; + + function initFiguresGL() { + var canvas, gl; + + generageGL(); + canvas = currentCanvas; + currentCanvas = null; + gl = currentGL; + currentGL = null; + + // setup a GLSL program + var vertexShader = createVertexShader(gl, figuresVertexShaderCode); + var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode); + var program = createProgram(gl, [vertexShader, fragmentShader]); + gl.useProgram(program); + + var cache = {}; + cache.gl = gl; + cache.canvas = canvas; + cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution'); + cache.scaleLocation = gl.getUniformLocation(program, 'u_scale'); + cache.offsetLocation = gl.getUniformLocation(program, 'u_offset'); + cache.positionLocation = gl.getAttribLocation(program, 'a_position'); + cache.colorLocation = gl.getAttribLocation(program, 'a_color'); + + figuresCache = cache; + } + + function drawFigures(width, height, backgroundColor, figures, context) { + if (!figuresCache) { + initFiguresGL(); + } + var cache = figuresCache, canvas = cache.canvas, gl = cache.gl; + + canvas.width = width; + canvas.height = height; + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + gl.uniform2f(cache.resolutionLocation, width, height); + + // count triangle points + var count = 0; + var i, ii, rows; + for (i = 0, ii = figures.length; i < ii; i++) { + switch (figures[i].type) { + case 'lattice': + rows = (figures[i].coords.length / figures[i].verticesPerRow) | 0; + count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6; + break; + case 'triangles': + count += figures[i].coords.length; + break; + } + } + // transfer data + var coords = new Float32Array(count * 2); + var colors = new Uint8Array(count * 3); + var coordsMap = context.coords, colorsMap = context.colors; + var pIndex = 0, cIndex = 0; + for (i = 0, ii = figures.length; i < ii; i++) { + var figure = figures[i], ps = figure.coords, cs = figure.colors; + switch (figure.type) { + case 'lattice': + var cols = figure.verticesPerRow; + rows = (ps.length / cols) | 0; + for (var row = 1; row < rows; row++) { + var offset = row * cols + 1; + for (var col = 1; col < cols; col++, offset++) { + coords[pIndex] = coordsMap[ps[offset - cols - 1]]; + coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1]; + coords[pIndex + 2] = coordsMap[ps[offset - cols]]; + coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1]; + coords[pIndex + 4] = coordsMap[ps[offset - 1]]; + coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1]; + colors[cIndex] = colorsMap[cs[offset - cols - 1]]; + colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1]; + colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2]; + colors[cIndex + 3] = colorsMap[cs[offset - cols]]; + colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1]; + colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2]; + colors[cIndex + 6] = colorsMap[cs[offset - 1]]; + colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1]; + colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2]; + + coords[pIndex + 6] = coords[pIndex + 2]; + coords[pIndex + 7] = coords[pIndex + 3]; + coords[pIndex + 8] = coords[pIndex + 4]; + coords[pIndex + 9] = coords[pIndex + 5]; + coords[pIndex + 10] = coordsMap[ps[offset]]; + coords[pIndex + 11] = coordsMap[ps[offset] + 1]; + colors[cIndex + 9] = colors[cIndex + 3]; + colors[cIndex + 10] = colors[cIndex + 4]; + colors[cIndex + 11] = colors[cIndex + 5]; + colors[cIndex + 12] = colors[cIndex + 6]; + colors[cIndex + 13] = colors[cIndex + 7]; + colors[cIndex + 14] = colors[cIndex + 8]; + colors[cIndex + 15] = colorsMap[cs[offset]]; + colors[cIndex + 16] = colorsMap[cs[offset] + 1]; + colors[cIndex + 17] = colorsMap[cs[offset] + 2]; + pIndex += 12; + cIndex += 18; + } + } + break; + case 'triangles': + for (var j = 0, jj = ps.length; j < jj; j++) { + coords[pIndex] = coordsMap[ps[j]]; + coords[pIndex + 1] = coordsMap[ps[j] + 1]; + colors[cIndex] = colorsMap[cs[i]]; + colors[cIndex + 1] = colorsMap[cs[j] + 1]; + colors[cIndex + 2] = colorsMap[cs[j] + 2]; + pIndex += 2; + cIndex += 3; + } + break; + } + } + + // draw + if (backgroundColor) { + gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255, + backgroundColor[2] / 255, 1.0); + } else { + gl.clearColor(0, 0, 0, 0); + } + gl.clear(gl.COLOR_BUFFER_BIT); + + var coordsBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer); + gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW); + gl.enableVertexAttribArray(cache.positionLocation); + gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0); + + var colorsBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + gl.enableVertexAttribArray(cache.colorLocation); + gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false, + 0, 0); + + gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY); + gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY); + + gl.drawArrays(gl.TRIANGLES, 0, count); + + gl.flush(); + + gl.deleteBuffer(coordsBuffer); + gl.deleteBuffer(colorsBuffer); + + return canvas; + } + + function cleanup() { + smaskCache = null; + figuresCache = null; + } + + return { + get isEnabled() { + if (PDFJS.disableWebGL) { + return false; + } + var enabled = false; + try { + generageGL(); + enabled = !!currentGL; + } catch (e) { } + return shadow(this, 'isEnabled', enabled); + }, + composeSMask: composeSMask, + drawFigures: drawFigures, + clear: cleanup + }; +})(); + + var ShadingIRs = {}; ShadingIRs.RadialAxial = { @@ -7145,28 +7576,27 @@ var createMeshCanvas = (function createMeshCanvasClosure() { var coords = context.coords, colors = context.colors; var bytes = data.data, rowSize = data.width * 4; var tmp; - if (coords[p1 * 2 + 1] > coords[p2 * 2 + 1]) { + if (coords[p1 + 1] > coords[p2 + 1]) { tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp; } - if (coords[p2 * 2 + 1] > coords[p3 * 2 + 1]) { + if (coords[p2 + 1] > coords[p3 + 1]) { tmp = p2; p2 = p3; p3 = tmp; tmp = c2; c2 = c3; c3 = tmp; } - if (coords[p1 * 2 + 1] > coords[p2 * 2 + 1]) { + if (coords[p1 + 1] > coords[p2 + 1]) { tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp; } - var x1 = (coords[p1 * 2] + context.offsetX) * context.scaleX; - var y1 = (coords[p1 * 2 + 1] + context.offsetY) * context.scaleY; - var x2 = (coords[p2 * 2] + context.offsetX) * context.scaleX; - var y2 = (coords[p2 * 2 + 1] + context.offsetY) * context.scaleY; - var x3 = (coords[p3 * 2] + context.offsetX) * context.scaleX; - var y3 = (coords[p3 * 2 + 1] + context.offsetY) * context.scaleY; + var x1 = (coords[p1] + context.offsetX) * context.scaleX; + var y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY; + var x2 = (coords[p2] + context.offsetX) * context.scaleX; + var y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY; + var x3 = (coords[p3] + context.offsetX) * context.scaleX; + var y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY; if (y1 >= y3) { return; } - var c1i = c1 * 3, c2i = c2 * 3, c3i = c3 * 3; - var c1r = colors[c1i], c1g = colors[c1i + 1], c1b = colors[c1i + 2]; - var c2r = colors[c2i], c2g = colors[c2i + 1], c2b = colors[c2i + 2]; - var c3r = colors[c3i], c3g = colors[c3i + 1], c3b = colors[c3i + 2]; + var c1r = colors[c1], c1g = colors[c1 + 1], c1b = colors[c1 + 2]; + var c2r = colors[c2], c2g = colors[c2 + 1], c2b = colors[c2 + 2]; + var c3r = colors[c3], c3g = colors[c3 + 1], c3b = colors[c3 + 2]; var minY = Math.round(y1), maxY = Math.round(y3); var xa, car, cag, cab; @@ -7208,12 +7638,13 @@ var createMeshCanvas = (function createMeshCanvasClosure() { function drawFigure(data, figure, context) { var ps = figure.coords; var cs = figure.colors; + var i, ii; switch (figure.type) { case 'lattice': var verticesPerRow = figure.verticesPerRow; var rows = Math.floor(ps.length / verticesPerRow) - 1; var cols = verticesPerRow - 1; - for (var i = 0; i < rows; i++) { + for (i = 0; i < rows; i++) { var q = i * verticesPerRow; for (var j = 0; j < cols; j++, q++) { drawTriangle(data, context, @@ -7226,7 +7657,7 @@ var createMeshCanvas = (function createMeshCanvasClosure() { } break; case 'triangles': - for (var i = 0, ii = ps.length; i < ii; i += 3) { + for (i = 0, ii = ps.length; i < ii; i += 3) { drawTriangle(data, context, ps[i], ps[i + 1], ps[i + 2], cs[i], cs[i + 1], cs[i + 2]); @@ -7246,39 +7677,59 @@ var createMeshCanvas = (function createMeshCanvasClosure() { // MAX_PATTERN_SIZE is used to avoid OOM situation. var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough - var boundsWidth = bounds[2] - bounds[0]; - var boundsHeight = bounds[3] - bounds[1]; + var offsetX = Math.floor(bounds[0]); + var offsetY = Math.floor(bounds[1]); + var boundsWidth = Math.ceil(bounds[2]) - offsetX; + var boundsHeight = Math.ceil(bounds[3]) - offsetY; var width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); var height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] * EXPECTED_SCALE)), MAX_PATTERN_SIZE); - var scaleX = width / boundsWidth; - var scaleY = height / boundsHeight; - - var tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false); - var tmpCtx = tmpCanvas.context; - if (backgroundColor) { - tmpCtx.fillStyle = makeCssRgb(backgroundColor); - tmpCtx.fillRect(0, 0, width, height); - } + var scaleX = boundsWidth / width; + var scaleY = boundsHeight / height; var context = { coords: coords, colors: colors, - offsetX: -bounds[0], - offsetY: -bounds[1], - scaleX: scaleX, - scaleY: scaleY + offsetX: -offsetX, + offsetY: -offsetY, + scaleX: 1 / scaleX, + scaleY: 1 / scaleY }; - var data = tmpCtx.getImageData(0, 0, width, height); - for (var i = 0; i < figures.length; i++) { - drawFigure(data, figures[i], context); - } - tmpCtx.putImageData(data, 0, 0); + var canvas, tmpCanvas, i, ii; + if (WebGLUtils.isEnabled) { + canvas = WebGLUtils.drawFigures(width, height, backgroundColor, + figures, context); - return {canvas: tmpCanvas.canvas, scaleX: 1 / scaleX, scaleY: 1 / scaleY}; + // https://bugzilla.mozilla.org/show_bug.cgi?id=972126 + tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false); + tmpCanvas.context.drawImage(canvas, 0, 0); + canvas = tmpCanvas.canvas; + } else { + tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false); + var tmpCtx = tmpCanvas.context; + + var data = tmpCtx.createImageData(width, height); + if (backgroundColor) { + var bytes = data.data; + for (i = 0, ii = bytes.length; i < ii; i += 4) { + bytes[i] = backgroundColor[0]; + bytes[i + 1] = backgroundColor[1]; + bytes[i + 2] = backgroundColor[2]; + bytes[i + 3] = 255; + } + } + for (i = 0; i < figures.length; i++) { + drawFigure(data, figures[i], context); + } + tmpCtx.putImageData(data, 0, 0); + canvas = tmpCanvas.canvas; + } + + return {canvas: canvas, offsetX: offsetX, offsetY: offsetY, + scaleX: scaleX, scaleY: scaleY}; } return createMeshCanvas; })(); @@ -7312,7 +7763,6 @@ ShadingIRs.Mesh = { // Rasterizing on the main thread since sending/queue large canvases // might cause OOM. - // TODO consider using WebGL or asm.js to perform rasterization var temporaryPatternCanvas = createMeshCanvas(bounds, combinedScale, coords, colors, figures, shadingFill ? null : background); @@ -7323,7 +7773,8 @@ ShadingIRs.Mesh = { } } - ctx.translate(bounds[0], bounds[1]); + ctx.translate(temporaryPatternCanvas.offsetX, + temporaryPatternCanvas.offsetY); ctx.scale(temporaryPatternCanvas.scaleX, temporaryPatternCanvas.scaleY); @@ -7485,7 +7936,7 @@ var TilingPattern = (function TilingPatternClosure() { getPattern: function TilingPattern_getPattern(ctx, owner) { var temporaryPatternCanvas = this.createPatternCanvas(owner); - var ctx = this.ctx; + ctx = this.ctx; ctx.setTransform.apply(ctx, this.baseTransform); ctx.transform.apply(ctx, this.matrix); this.scaleToContext(); @@ -7560,7 +8011,7 @@ var FontFace = (function FontFaceClosure() { return null; } - var data = bytesToString(this.data); + var data = bytesToString(new Uint8Array(this.data)); var fontName = this.loadedName; // Add the font-face rule to the document diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 3bd4189fc68..168dbb0169d 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -21,8 +21,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '0.8.1334'; -PDFJS.build = 'b97127a'; +PDFJS.version = '1.0.2'; +PDFJS.build = '2909225'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -182,7 +182,7 @@ var OPS = PDFJS.OPS = { paintInlineImageXObjectGroup: 87, paintImageXObjectRepeat: 88, paintImageMaskXObjectRepeat: 89, - paintSolidColorImageMask: 90, + paintSolidColorImageMask: 90 }; // A notice for devs. These are good for things that are helpful to devs, such @@ -267,9 +267,10 @@ function combineUrl(baseUrl, url) { if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) { return url; } + var i; if (url.charAt(0) == '/') { // absolute path - var i = baseUrl.indexOf('://'); + i = baseUrl.indexOf('://'); if (url.charAt(1) === '/') { ++i; } else { @@ -278,7 +279,7 @@ function combineUrl(baseUrl, url) { return baseUrl.substring(0, i) + url; } else { // relative path - var pathLength = baseUrl.length, i; + var pathLength = baseUrl.length; i = baseUrl.lastIndexOf('#'); pathLength = i >= 0 ? i : pathLength; i = baseUrl.lastIndexOf('?', pathLength); @@ -423,23 +424,43 @@ var XRefParseException = (function XRefParseExceptionClosure() { function bytesToString(bytes) { - var strBuf = []; var length = bytes.length; - for (var n = 0; n < length; ++n) { - strBuf.push(String.fromCharCode(bytes[n])); + var MAX_ARGUMENT_COUNT = 8192; + if (length < MAX_ARGUMENT_COUNT) { + return String.fromCharCode.apply(null, bytes); + } + var strBuf = []; + for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) { + var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length); + var chunk = bytes.subarray(i, chunkEnd); + strBuf.push(String.fromCharCode.apply(null, chunk)); } return strBuf.join(''); } +function stringToArray(str) { + var length = str.length; + var array = []; + for (var i = 0; i < length; ++i) { + array[i] = str.charCodeAt(i); + } + return array; +} + function stringToBytes(str) { var length = str.length; var bytes = new Uint8Array(length); - for (var n = 0; n < length; ++n) { - bytes[n] = str.charCodeAt(n) & 0xFF; + for (var i = 0; i < length; ++i) { + bytes[i] = str.charCodeAt(i) & 0xFF; } return bytes; } +function string32(value) { + return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff, + (value >> 8) & 0xff, value & 0xff); +} + // Lazy test the endianness of the platform // NOTE: This will be 'true' for simulated TypedArrays function isLittleEndian() { @@ -971,17 +992,18 @@ var StatTimer = (function StatTimerClosure() { delete this.started[name]; }, toString: function StatTimer_toString() { + var i, ii; var times = this.times; var out = ''; // Find the longest name for padding purposes. var longest = 0; - for (var i = 0, ii = times.length; i < ii; ++i) { + for (i = 0, ii = times.length; i < ii; ++i) { var name = times[i]['name']; if (name.length > longest) { longest = name.length; } } - for (var i = 0, ii = times.length; i < ii; ++i) { + for (i = 0, ii = times.length; i < ii; ++i) { var span = times[i]; var duration = span.end - span.start; out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n'; @@ -1191,10 +1213,10 @@ var ColorSpace = (function ColorSpaceClosure() { var rgbBuf = null; var numComponentColors = 1 << bpc; var needsResizing = originalHeight != height || originalWidth != width; + var i, ii; 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 @@ -1208,18 +1230,20 @@ var ColorSpace = (function ColorSpaceClosure() { // we are reparsing colorspaces too much?). var allColors = bpc <= 8 ? new Uint8Array(numComponentColors) : new Uint16Array(numComponentColors); - for (var i = 0; i < numComponentColors; i++) { + var key; + for (i = 0; i < numComponentColors; i++) { allColors[i] = i; } var colorMap = new Uint8Array(numComponentColors * 3); this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bpc, /* alpha01 = */ 0); + var destPos, rgbPos; if (!needsResizing) { // Fill in the RGB values directly into |dest|. - var destPos = 0; - for (var i = 0; i < count; ++i) { - var key = comps[i] * 3; + destPos = 0; + for (i = 0; i < count; ++i) { + key = comps[i] * 3; dest[destPos++] = colorMap[key]; dest[destPos++] = colorMap[key + 1]; dest[destPos++] = colorMap[key + 2]; @@ -1227,9 +1251,9 @@ var ColorSpace = (function ColorSpaceClosure() { } } else { rgbBuf = new Uint8Array(count * 3); - var rgbPos = 0; - for (var i = 0; i < count; ++i) { - var key = comps[i] * 3; + rgbPos = 0; + for (i = 0; i < count; ++i) { + key = comps[i] * 3; rgbBuf[rgbPos++] = colorMap[key]; rgbBuf[rgbPos++] = colorMap[key + 1]; rgbBuf[rgbPos++] = colorMap[key + 2]; @@ -1252,9 +1276,9 @@ var ColorSpace = (function ColorSpaceClosure() { rgbBuf = PDFImage.resize(rgbBuf, bpc, 3, originalWidth, originalHeight, width, height); } - var rgbPos = 0; - var destPos = 0; - for (var i = 0, ii = width * actualHeight; i < ii; i++) { + rgbPos = 0; + destPos = 0; + for (i = 0, ii = width * actualHeight; i < ii; i++) { dest[destPos++] = rgbBuf[rgbPos++]; dest[destPos++] = rgbBuf[rgbPos++]; dest[destPos++] = rgbBuf[rgbPos++]; @@ -1280,6 +1304,7 @@ var ColorSpace = (function ColorSpaceClosure() { ColorSpace.fromIR = function ColorSpace_fromIR(IR) { var name = isArray(IR) ? IR[0] : IR; + var whitePoint, blackPoint; switch (name) { case 'DeviceGrayCS': @@ -1289,8 +1314,8 @@ var ColorSpace = (function ColorSpaceClosure() { case 'DeviceCmykCS': return this.singletons.cmyk; case 'CalGrayCS': - var whitePoint = IR[1].WhitePoint; - var blackPoint = IR[1].BlackPoint; + whitePoint = IR[1].WhitePoint; + blackPoint = IR[1].BlackPoint; var gamma = IR[1].Gamma; return new CalGrayCS(whitePoint, blackPoint, gamma); case 'PatternCS': @@ -1312,8 +1337,8 @@ var ColorSpace = (function ColorSpaceClosure() { return new AlternateCS(numComps, ColorSpace.fromIR(alt), PDFFunction.fromIR(tintFnIR)); case 'LabCS': - var whitePoint = IR[1].WhitePoint; - var blackPoint = IR[1].BlackPoint; + whitePoint = IR[1].WhitePoint; + blackPoint = IR[1].BlackPoint; var range = IR[1].Range; return new LabCS(whitePoint, blackPoint, range); default: @@ -1358,6 +1383,7 @@ var ColorSpace = (function ColorSpaceClosure() { } else if (isArray(cs)) { mode = cs[0].name; this.mode = mode; + var numComps, params; switch (mode) { case 'DeviceGray': @@ -1370,14 +1396,14 @@ var ColorSpace = (function ColorSpaceClosure() { case 'CMYK': return 'DeviceCmykCS'; case 'CalGray': - var params = cs[1].getAll(); + 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'); + numComps = dict.get('N'); if (numComps == 1) { return 'DeviceGrayCS'; } else if (numComps == 3) { @@ -1404,7 +1430,7 @@ var ColorSpace = (function ColorSpaceClosure() { case 'Separation': case 'DeviceN': var name = cs[1]; - var numComps = 1; + numComps = 1; if (isName(name)) { numComps = 1; } else if (isArray(name)) { @@ -1414,7 +1440,7 @@ var ColorSpace = (function ColorSpaceClosure() { var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); return ['AlternateCS', numComps, alt, tintFnIR]; case 'Lab': - var params = cs[1].getAll(); + params = cs[1].getAll(); return ['LabCS', params]; default: error('unimplemented color space object "' + mode + '"'); @@ -1509,13 +1535,14 @@ var AlternateCS = (function AlternateCSClosure() { var numComps = this.numComps; var scaled = new Float32Array(numComps); - for (var i = 0; i < count; i++) { - for (var j = 0; j < numComps; j++) { + var i, j; + for (i = 0; i < count; i++) { + for (j = 0; j < numComps; j++) { scaled[j] = src[srcOffset++] * scale; } var tinted = tintFn(scaled); if (usesZeroToOneRange) { - for (var j = 0; j < baseNumComps; j++) { + for (j = 0; j < baseNumComps; j++) { baseBuf[pos++] = tinted[j] * 255; } } else { @@ -2050,8 +2077,9 @@ var PDFFunction = (function PDFFunctionClosure() { return { getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps, str) { + var i, ii; var length = 1; - for (var i = 0, ii = size.length; i < ii; i++) { + for (i = 0, ii = size.length; i < ii; i++) { length *= size[i]; } length *= outputSize; @@ -2064,7 +2092,7 @@ var PDFFunction = (function PDFFunctionClosure() { var strBytes = str.getBytes((length * bps + 7) / 8); var strIdx = 0; - for (var i = 0; i < length; i++) { + for (i = 0; i < length; i++) { while (codeSize < bps) { codeBuf <<= 8; codeBuf |= strBytes[strIdx++]; @@ -2207,13 +2235,14 @@ var PDFFunction = (function PDFFunctionClosure() { var cubeVertices = 1 << m; var cubeN = new Float64Array(cubeVertices); var cubeVertex = new Uint32Array(cubeVertices); - for (var j = 0; j < cubeVertices; j++) { + var i, j; + for (j = 0; j < cubeVertices; j++) { cubeN[j] = 1; } var k = n, pos = 1; // Map x_i to y_j for 0 <= i < m using the sampled function. - for (var i = 0; i < m; ++i) { + for (i = 0; i < m; ++i) { // x_i' = min(max(x_i, Domain_2i), Domain_2i+1) var domain_2i = domain[i][0]; var domain_2i_1 = domain[i][1]; @@ -2234,7 +2263,7 @@ var PDFFunction = (function PDFFunctionClosure() { var n1 = e - e0; // (e - e0) / (e1 - e0); var offset0 = e0 * k; var offset1 = offset0 + k; // e1 * k - for (var j = 0; j < cubeVertices; j++) { + for (j = 0; j < cubeVertices; j++) { if (j & pos) { cubeN[j] *= n1; cubeVertex[j] += offset1; @@ -2249,10 +2278,10 @@ var PDFFunction = (function PDFFunctionClosure() { } var y = new Float64Array(n); - for (var j = 0; j < n; ++j) { + for (j = 0; j < n; ++j) { // Sum all cube vertices' samples portions var rj = 0; - for (var i = 0; i < cubeVertices; i++) { + for (i = 0; i < cubeVertices; i++) { rj += samples[cubeVertex[i] + j] * cubeN[i]; } @@ -2813,7 +2842,7 @@ var Annotation = (function AnnotationClosure() { var data = this.data = {}; data.subtype = dict.get('Subtype').name; - var rect = dict.get('Rect'); + var rect = dict.get('Rect') || [0, 0, 0, 0]; data.rect = Util.normalizeRect(rect); data.annotationFlags = dict.get('F'); @@ -2845,7 +2874,8 @@ var Annotation = (function AnnotationClosure() { var isInvalid = false; var numPositive = 0; for (var i = 0; i < dashArrayLength; i++) { - if (!(+dashArray[i] >= 0)) { + var validNumber = (+dashArray[i] >= 0); + if (!validNumber) { isInvalid = true; break; } else if (dashArray[i] > 0) { @@ -3111,7 +3141,7 @@ var WidgetAnnotation = (function WidgetAnnotationClosure() { var fieldType = Util.getInheritableProperty(dict, 'FT'); data.fieldType = isName(fieldType) ? fieldType.name : ''; data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0; - this.fieldResources = Util.getInheritableProperty(dict, 'DR') || new Dict(); + this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty; // Building the full field name by collecting the field and // its ancestors 'T' data and joining them using '.'. @@ -3406,10 +3436,12 @@ var TextAnnotation = (function TextAnnotationClosure() { var content = document.createElement('div'); content.className = 'annotTextContent'; content.setAttribute('hidden', true); + + var i, ii; if (item.hasBgColor) { var color = item.color; var rgb = []; - for (var i = 0; i < 3; ++i) { + for (i = 0; i < 3; ++i) { // Enlighten the color (70%) var c = Math.round(color[i] * 255); rgb[i] = Math.round((255 - c) * 0.7) + c; @@ -3426,7 +3458,7 @@ var TextAnnotation = (function TextAnnotationClosure() { } else { var e = document.createElement('span'); var lines = item.content.split(/(?:\r\n?|\n)/); - for (var i = 0, ii = lines.length; i < ii; ++i) { + for (i = 0, ii = lines.length; i < ii; ++i) { var line = lines[i]; e.appendChild(document.createTextNode(line)); if (i < (ii - 1)) { @@ -3638,10 +3670,11 @@ var ChunkedStream = (function ChunkedStreamClosure() { var chunkSize = this.chunkSize; var beginChunk = Math.floor(begin / chunkSize); var endChunk = Math.floor((end - 1) / chunkSize) + 1; + var curChunk; - for (var chunk = beginChunk; chunk < endChunk; ++chunk) { - if (!(chunk in this.loadedChunks)) { - this.loadedChunks[chunk] = true; + for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) { + if (!(curChunk in this.loadedChunks)) { + this.loadedChunks[curChunk] = true; ++this.numChunksLoaded; } } @@ -3678,13 +3711,14 @@ var ChunkedStream = (function ChunkedStreamClosure() { }, nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) { - for (var chunk = beginChunk, n = this.numChunks; chunk < n; ++chunk) { + var chunk, n; + for (chunk = beginChunk, n = this.numChunks; chunk < n; ++chunk) { if (!(chunk in this.loadedChunks)) { return chunk; } } // Wrap around to beginning - for (var chunk = 0; chunk < beginChunk; ++chunk) { + for (chunk = 0; chunk < beginChunk; ++chunk) { if (!(chunk in this.loadedChunks)) { return chunk; } @@ -3879,8 +3913,9 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { var requestId = this.currRequestId++; var chunksNeeded; + var i, ii; this.chunksNeededByRequest[requestId] = chunksNeeded = {}; - for (var i = 0, ii = chunks.length; i < ii; i++) { + for (i = 0, ii = chunks.length; i < ii; i++) { if (!this.stream.hasChunk(chunks[i])) { chunksNeeded[chunks[i]] = true; } @@ -3911,7 +3946,7 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { var groupedChunksToRequest = this.groupChunks(chunksToRequest); - for (var i = 0; i < groupedChunksToRequest.length; ++i) { + for (i = 0; i < groupedChunksToRequest.length; ++i) { var groupedChunk = groupedChunksToRequest[i]; var begin = groupedChunk.beginChunk * this.chunkSize; var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length); @@ -4010,14 +4045,14 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } var loadedRequests = []; - for (var chunk = beginChunk; chunk < endChunk; ++chunk) { - + var i, requestId; + for (chunk = beginChunk; chunk < endChunk; ++chunk) { // The server might return more chunks than requested var requestIds = this.requestsByChunk[chunk] || []; delete this.requestsByChunk[chunk]; - for (var i = 0; i < requestIds.length; ++i) { - var requestId = requestIds[i]; + for (i = 0; i < requestIds.length; ++i) { + requestId = requestIds[i]; var chunksNeeded = this.chunksNeededByRequest[requestId]; if (chunk in chunksNeeded) { delete chunksNeeded[chunk]; @@ -4051,8 +4086,8 @@ var ChunkedStreamManager = (function ChunkedStreamManagerClosure() { } } - for (var i = 0; i < loadedRequests.length; ++i) { - var requestId = loadedRequests[i]; + for (i = 0; i < loadedRequests.length; ++i) { + requestId = loadedRequests[i]; var callback = this.callbacksByRequest[requestId]; delete this.callbacksByRequest[requestId]; if (callback) { @@ -4334,7 +4369,7 @@ var Page = (function PageClosure() { // present, but can be empty. Some document omit it still. In this case // return an empty dictionary: if (value === undefined) { - value = new Dict(); + value = Dict.empty; } return shadow(this, 'resources', value); }, @@ -4499,8 +4534,6 @@ var Page = (function PageClosure() { var self = this; - var textContentPromise = new LegacyPromise(); - var pdfManager = this.pdfManager; var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', []); @@ -4513,7 +4546,7 @@ var Page = (function PageClosure() { var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]); - dataPromises.then(function(data) { + return dataPromises.then(function(data) { var contentStream = data[0]; var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, @@ -4521,12 +4554,9 @@ var Page = (function PageClosure() { self.idCounters, self.fontCache); - var bidiTexts = partialEvaluator.getTextContent(contentStream, - self.resources); - textContentPromise.resolve(bidiTexts); + return partialEvaluator.getTextContent(contentStream, + self.resources); }); - - return textContentPromise; }, getAnnotationsData: function Page_getAnnotationsData() { @@ -4849,11 +4879,30 @@ var Dict = (function DictClosure() { return nonSerializable; // creating closure on some variable }; + var GETALL_DICTIONARY_TYPES_WHITELIST = { + 'Background': true, + 'ExtGState': true, + 'Halftone': true, + 'Layout': true, + 'Mask': true, + 'Pagination': true, + 'Printing': true + }; + + function isRecursionAllowedFor(dict) { + if (!isName(dict.Type)) { + return true; + } + var dictType = dict.Type.name; + return GETALL_DICTIONARY_TYPES_WHITELIST[dictType] === true; + } + // xref is optional function Dict(xref) { // Map should only be used internally, use functions below to access. this.map = Object.create(null); this.xref = xref; + this.objId = null; this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict } @@ -4917,10 +4966,52 @@ var Dict = (function DictClosure() { // creates new map and dereferences all Refs getAll: function Dict_getAll() { - var all = {}; - for (var key in this.map) { - var obj = this.get(key); - all[key] = (obj instanceof Dict ? obj.getAll() : obj); + var all = Object.create(null); + var queue = null; + var key, obj; + for (key in this.map) { + obj = this.get(key); + if (obj instanceof Dict) { + if (isRecursionAllowedFor(obj)) { + (queue || (queue = [])).push({target: all, key: key, obj: obj}); + } else { + all[key] = this.getRaw(key); + } + } else { + all[key] = obj; + } + } + if (!queue) { + return all; + } + + // trying to take cyclic references into the account + var processed = Object.create(null); + while (queue.length > 0) { + var item = queue.shift(); + var itemObj = item.obj; + var objId = itemObj.objId; + if (objId && objId in processed) { + item.target[item.key] = processed[objId]; + continue; + } + var dereferenced = Object.create(null); + for (key in itemObj.map) { + obj = itemObj.get(key); + if (obj instanceof Dict) { + if (isRecursionAllowedFor(obj)) { + queue.push({target: dereferenced, key: key, obj: obj}); + } else { + dereferenced[key] = itemObj.getRaw(key); + } + } else { + dereferenced[key] = obj; + } + } + if (objId) { + processed[objId] = dereferenced; + } + item.target[item.key] = dereferenced; } return all; }, @@ -4940,6 +5031,8 @@ var Dict = (function DictClosure() { } }; + Dict.empty = new Dict(null); + return Dict; })(); @@ -4996,6 +5089,10 @@ var RefSetCache = (function RefSetCacheClosure() { this.dict['R' + ref.num + '.' + ref.gen] = obj; }, + putAlias: function RefSetCache_putAlias(ref, aliasRef) { + this.dict['R' + ref.num + '.' + ref.gen] = this.get(aliasRef); + }, + forEach: function RefSetCache_forEach(fn, thisArg) { for (var i in this.dict) { fn.call(thisArg, this.dict[i]); @@ -5709,13 +5806,14 @@ var XRef = (function XRefClosure() { } } // reading XRef streams - for (var i = 0, ii = xrefStms.length; i < ii; ++i) { + var i, ii; + for (i = 0, ii = xrefStms.length; i < ii; ++i) { this.startXRefQueue.push(xrefStms[i]); this.readXRef(/* recoveryMode */ true); } // finding main trailer var dict; - for (var i = 0, ii = trailers.length; i < ii; ++i) { + for (i = 0, ii = trailers.length; i < ii; ++i) { stream.pos = trailers[i]; var parser = new Parser(new Lexer(stream), true, null); var obj = parser.getObj(); @@ -5848,10 +5946,15 @@ var XRef = (function XRefClosure() { } if (xrefEntry.uncompressed) { - return this.fetchUncompressed(ref, xrefEntry, suppressEncryption); + xrefEntry = this.fetchUncompressed(ref, xrefEntry, suppressEncryption); } else { - return this.fetchCompressed(xrefEntry, suppressEncryption); + xrefEntry = this.fetchCompressed(xrefEntry, suppressEncryption); } + + if (isDict(xrefEntry)) { + xrefEntry.objId = 'R' + ref.num + '.' + ref.gen; + } + return xrefEntry; }, fetchUncompressed: function XRef_fetchUncompressed(ref, xrefEntry, @@ -6048,6 +6151,7 @@ var ObjectLoader = (function() { } function addChildren(node, nodesToVisit) { + var value; if (isDict(node) || isStream(node)) { var map; if (isDict(node)) { @@ -6056,14 +6160,14 @@ var ObjectLoader = (function() { map = node.dict.map; } for (var key in map) { - var value = map[key]; + value = map[key]; if (mayHaveChildren(value)) { nodesToVisit.push(value); } } } else if (isArray(node)) { for (var i = 0, ii = node.length; i < ii; i++) { - var value = node[i]; + value = node[i]; if (mayHaveChildren(value)) { nodesToVisit.push(value); } @@ -13691,7 +13795,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() { hashData[i++] = fileId[j]; } cipher = new ARCFourCipher(encryptionKey); - var checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i)); + checkData = cipher.encryptBlock(calculateMD5(hashData, 0, i)); n = encryptionKey.length; var derivedKey = new Uint8Array(n), k; for (j = 1; j <= 19; ++j) { @@ -14030,15 +14134,16 @@ Shadings.RadialAxial = (function RadialAxialClosure() { return; } + var rgbColor; for (var i = t0; i <= t1; i += step) { - var rgbColor = cs.getRgb(fn([i]), 0); + rgbColor = cs.getRgb(fn([i]), 0); var cssColor = Util.makeCssRgb(rgbColor); colorStops.push([(i - t0) / diff, cssColor]); } var background = 'transparent'; if (dict.has('Background')) { - var rgbColor = cs.getRgb(dict.get('Background'), 0); + rgbColor = cs.getRgb(dict.get('Background'), 0); background = Util.makeCssRgb(rgbColor); } @@ -14538,6 +14643,38 @@ Shadings.Mesh = (function MeshClosure() { mesh.bounds = [minX, minY, maxX, maxY]; } + function packData(mesh) { + var i, ii, j, jj; + + var coords = mesh.coords; + var coordsPacked = new Float32Array(coords.length * 2); + for (i = 0, j = 0, ii = coords.length; i < ii; i++) { + var xy = coords[i]; + coordsPacked[j++] = xy[0]; + coordsPacked[j++] = xy[1]; + } + mesh.coords = coordsPacked; + + var colors = mesh.colors; + var colorsPacked = new Uint8Array(colors.length * 3); + for (i = 0, j = 0, ii = colors.length; i < ii; i++) { + var c = colors[i]; + colorsPacked[j++] = c[0]; + colorsPacked[j++] = c[1]; + colorsPacked[j++] = c[2]; + } + mesh.colors = colorsPacked; + + var figures = mesh.figures; + for (i = 0, ii = figures.length; i < ii; i++) { + var figure = figures[i], ps = figure.coords, cs = figure.colors; + for (j = 0, jj = ps.length; j < jj; j++) { + ps[j] *= 2; + cs[j] *= 3; + } + } + } + function Mesh(stream, matrix, xref, res) { assert(isStream(stream), 'Mesh data is not a stream'); var dict = stream.dict; @@ -14625,35 +14762,14 @@ Shadings.Mesh = (function MeshClosure() { } // calculate bounds updateBounds(this); + + packData(this); } Mesh.prototype = { getIR: function Mesh_getIR() { - var type = this.shadingType; - var i, ii, j; - var coords = this.coords; - var coordsPacked = new Float32Array(coords.length * 2); - for (i = 0, j = 0, ii = coords.length; i < ii; i++) { - var xy = coords[i]; - coordsPacked[j++] = xy[0]; - coordsPacked[j++] = xy[1]; - } - var colors = this.colors; - var colorsPacked = new Uint8Array(colors.length * 3); - for (i = 0, j = 0, ii = colors.length; i < ii; i++) { - var c = colors[i]; - colorsPacked[j++] = c[0]; - colorsPacked[j++] = c[1]; - colorsPacked[j++] = c[2]; - } - var figures = this.figures; - var bbox = this.bbox; - var bounds = this.bounds; - var matrix = this.matrix; - var background = this.background; - - return ['Mesh', type, coordsPacked, colorsPacked, figures, bounds, - matrix, bbox, background]; + return ['Mesh', this.shadingType, this.coords, this.colors, this.figures, + this.bounds, this.matrix, this.bbox, this.background]; } }; @@ -14691,9 +14807,6 @@ function getTilingPatternIR(operatorList, dict, args) { var PartialEvaluator = (function PartialEvaluatorClosure() { function PartialEvaluator(pdfManager, xref, handler, pageIndex, uniquePrefix, idCounters, fontCache) { - this.state = new EvalState(); - this.stateStack = []; - this.pdfManager = pdfManager; this.xref = xref; this.handler = handler; @@ -14711,14 +14824,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return false; } + var processed = Object.create(null); + if (resources.objId) { + processed[resources.objId] = true; + } + var nodes = [resources]; while (nodes.length) { + var key; var node = nodes.shift(); // First check the current resources for blend modes. var graphicStates = node.get('ExtGState'); if (isDict(graphicStates)) { graphicStates = graphicStates.getAll(); - for (var key in graphicStates) { + for (key in graphicStates) { var graphicState = graphicStates[key]; var bm = graphicState['BM']; if (isName(bm) && bm.name !== 'Normal') { @@ -14732,16 +14851,19 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { continue; } xObjects = xObjects.getAll(); - for (var key in xObjects) { + for (key in xObjects) { var xObject = xObjects[key]; if (!isStream(xObject)) { continue; } var xResources = xObject.dict.get('Resources'); - // Only add the resource if it's different from the current one, - // otherwise we can get stuck in an infinite loop. - if (isDict(xResources) && xResources !== node) { + // Checking objId to detect an infinite loop. + if (isDict(xResources) && + (!xResources.objId || !processed[xResources.objId])) { nodes.push(xResources); + if (xResources.objId) { + processed[xResources.objId] = true; + } } } } @@ -14751,7 +14873,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { buildFormXObject: function PartialEvaluator_buildFormXObject(resources, xobj, smask, operatorList, - state) { + initialState) { var matrix = xobj.dict.get('Matrix'); var bbox = xobj.dict.get('BBox'); var group = xobj.dict.get('Group'); @@ -14778,7 +14900,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]); this.getOperatorList(xobj, (xobj.dict.get('Resources') || resources), - operatorList, state); + operatorList, initialState); operatorList.addOp(OPS.paintFormXObjectEnd, []); if (group) { @@ -14801,6 +14923,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } var imageMask = (dict.get('ImageMask', 'IM') || false); + var imgData, args; if (imageMask) { // This depends on a tmpCanvas being filled with the // current fillStyle, such that processing the pixel @@ -14816,10 +14939,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var canTransfer = image instanceof DecodeStream; var inverseDecode = (!!decode && decode[0] > 0); - var imgData = PDFImage.createMask(imgArray, width, height, - canTransfer, inverseDecode); + imgData = PDFImage.createMask(imgArray, width, height, + canTransfer, inverseDecode); imgData.cached = true; - var args = [imgData]; + args = [imgData]; operatorList.addOp(OPS.paintImageMaskXObject, args); if (cacheKey) { cache.key = cacheKey; @@ -14840,7 +14963,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { inline, null, null); // We force the use of RGBA_32BPP images here, because we can't handle // any other kind. - var imgData = imageObj.createImageData(/* forceRGBA = */ true); + imgData = imageObj.createImageData(/* forceRGBA = */ true); operatorList.addOp(OPS.paintInlineImageXObject, [imgData]); return; } @@ -14850,7 +14973,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var uniquePrefix = (this.uniquePrefix || ''); var objId = 'img_' + uniquePrefix + (++this.idCounters.obj); operatorList.addDependency(objId); - var args = [objId, w, h]; + args = [objId, w, h]; if (!softMask && !mask && image instanceof JpegStream && image.isNativelySupported(this.xref, resources)) { @@ -14876,15 +14999,15 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, handleSMask: function PartialEvaluator_handleSmask(smask, resources, - operatorList) { + operatorList, + stateManager) { var smaskContent = smask.get('G'); var smaskOptions = { subtype: smask.get('S').name, backdrop: smask.get('BC') }; - this.buildFormXObject(resources, smaskContent, smaskOptions, - operatorList); + operatorList, stateManager.state.clone()); }, handleTilingType: @@ -14905,7 +15028,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { handleSetFont: function PartialEvaluator_handleSetFont(resources, fontArgs, fontRef, - operatorList) { + operatorList, state) { // TODO(mack): Not needed? var fontName; if (fontArgs) { @@ -14915,7 +15038,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var self = this; var font = this.loadFont(fontName, fontRef, this.xref, resources, operatorList); - this.state.font = font; + state.font = font; var loadedName = font.loadedName; if (!font.sent) { var fontData = font.translated.exportData(); @@ -14931,10 +15054,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return loadedName; }, - handleText: function PartialEvaluator_handleText(chars) { - var font = this.state.font.translated; + handleText: function PartialEvaluator_handleText(chars, state) { + var font = state.font.translated; var glyphs = font.charsToGlyphs(chars); - var isAddToPathSet = !!(this.state.textRenderingMode & + var isAddToPathSet = !!(state.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); if (font.data && (isAddToPathSet || PDFJS.disableFontFace)) { for (var i = 0; i < glyphs.length; i++) { @@ -14957,7 +15080,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, setGState: function PartialEvaluator_setGState(resources, gState, - operatorList, xref) { + operatorList, xref, + stateManager) { var self = this; // TODO(mack): This should be rewritten so that this function returns @@ -14979,7 +15103,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { break; case 'Font': var loadedName = self.handleSetFont(resources, null, value[0], - operatorList); + operatorList, + stateManager.state); operatorList.addDependency(loadedName); gStateObj.push([key, [loadedName, value[1]]]); break; @@ -14993,7 +15118,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } var dict = xref.fetchIfRef(value); if (isDict(dict)) { - self.handleSMask(dict, resources, operatorList); + self.handleSMask(dict, resources, operatorList, stateManager); gStateObj.push([key, true]); } else { warn('Unsupported SMask type'); @@ -15068,6 +15193,36 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (!isDict(font)) { return errorFont(); } + + var preEvaluatedFont = this.preEvaluateFont(font, xref); + var descriptor = preEvaluatedFont.descriptor; + var fontID = fontRef.num + '_' + fontRef.gen; + if (isDict(descriptor)) { + if (!descriptor.fontAliases) { + descriptor.fontAliases = Object.create(null); + } + + var fontAliases = descriptor.fontAliases; + var hash = preEvaluatedFont.hash; + if (fontAliases[hash]) { + var aliasFontRef = fontAliases[hash].aliasRef; + if (aliasFontRef && this.fontCache.has(aliasFontRef)) { + this.fontCache.putAlias(fontRef, aliasFontRef); + var cachedFont = this.fontCache.get(fontRef); + return cachedFont; + } + } + + if (!fontAliases[hash]) { + fontAliases[hash] = { + fontID: Font.getFontID() + }; + } + + fontAliases[hash].aliasRef = fontRef; + fontID = fontAliases[hash].fontID; + } + // Workaround for bad PDF generators that don'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), @@ -15081,12 +15236,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // Keep track of each font we translated so the caller can // load them asynchronously before calling display on a page. font.loadedName = 'g_font_' + (fontRefIsDict ? - fontName.replace(/\W/g, '') : (fontRef.num + '_' + fontRef.gen)); + fontName.replace(/\W/g, '') : fontID); if (!font.translated) { var translated; try { - translated = this.translateFont(font, xref); + translated = this.translateFont(preEvaluatedFont, xref); } catch (e) { UnsupportedManager.notify(UNSUPPORTED_FEATURES.font); translated = new ErrorFont(e instanceof Error ? e.message : e); @@ -15120,7 +15275,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { getOperatorList: function PartialEvaluator_getOperatorList(stream, resources, operatorList, - evaluatorState) { + initialState) { var self = this; var xref = this.xref; @@ -15129,19 +15284,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { operatorList = (operatorList || new OperatorList()); - resources = (resources || new Dict()); - var xobjs = (resources.get('XObject') || new Dict()); - var patterns = (resources.get('Pattern') || new Dict()); - var preprocessor = new EvaluatorPreprocessor(stream, xref); - if (evaluatorState) { - preprocessor.setState(evaluatorState); - } + resources = (resources || Dict.empty); + var xobjs = (resources.get('XObject') || Dict.empty); + var patterns = (resources.get('Pattern') || Dict.empty); + var stateManager = new StateManager(initialState || new EvalState()); + var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); var promise = new LegacyPromise(); - var operation; + var operation, i, ii; while ((operation = preprocessor.read())) { var args = operation.args; var fn = operation.fn; + var shading; switch (fn) { case OPS.setStrokeColorN: @@ -15164,10 +15318,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { args = []; continue; } else if (typeNum == SHADING_PATTERN) { - var shading = dict.get('Shading'); + shading = dict.get('Shading'); var matrix = dict.get('Matrix'); - var pattern = Pattern.parseShading(shading, matrix, xref, - resources); + pattern = Pattern.parseShading(shading, matrix, xref, + resources); args = pattern.getIR(); } else { error('Unkown PatternType ' + typeNum); @@ -15195,9 +15349,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { 'XObject should have a Name subtype'); if ('Form' == type.name) { + stateManager.save(); self.buildFormXObject(resources, xobj, null, operatorList, - preprocessor.getState()); + stateManager.state.clone()); args = []; + stateManager.restore(); continue; } else if ('Image' == type.name) { self.buildPaintImageXObject(resources, xobj, false, @@ -15212,7 +15368,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { case OPS.setFont: // eagerly collect all fonts var loadedName = self.handleSetFont(resources, args, null, - operatorList); + operatorList, + stateManager.state); operatorList.addDependency(loadedName); args[0] = loadedName; break; @@ -15227,37 +15384,26 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { operatorList, cacheKey, imageCache); args = []; continue; - case OPS.save: - var old = this.state; - this.stateStack.push(this.state); - this.state = old.clone(); - break; - case OPS.restore: - var prev = this.stateStack.pop(); - if (prev) { - this.state = prev; - } - break; case OPS.showText: - args[0] = this.handleText(args[0]); + args[0] = this.handleText(args[0], stateManager.state); break; case OPS.showSpacedText: var arr = args[0]; var arrLength = arr.length; - for (var i = 0; i < arrLength; ++i) { + for (i = 0; i < arrLength; ++i) { if (isString(arr[i])) { - arr[i] = this.handleText(arr[i]); + arr[i] = this.handleText(arr[i], stateManager.state); } } break; case OPS.nextLineShowText: - args[0] = this.handleText(args[0]); + args[0] = this.handleText(args[0], stateManager.state); break; case OPS.nextLineSetSpacingShowText: - args[2] = this.handleText(args[2]); + args[2] = this.handleText(args[2], stateManager.state); break; case OPS.setTextRenderingMode: - this.state.textRenderingMode = args[0]; + stateManager.state.textRenderingMode = args[0]; break; // Parse the ColorSpace data to a raw format. case OPS.setFillColorSpace: @@ -15270,7 +15416,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { error('No shading resource found'); } - var shading = shadingRes.get(args[0].name); + shading = shadingRes.get(args[0].name); if (!shading) { error('No shading object found'); } @@ -15290,7 +15436,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } var gState = extGState.get(dictName.name); - self.setGState(resources, gState, operatorList, xref); + self.setGState(resources, gState, operatorList, xref, + stateManager); args = []; continue; } @@ -15299,7 +15446,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // Some PDFs don't close all restores inside object/form. // Closing those for them. - for (var i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) { + for (i = 0, ii = preprocessor.savedStatesDepth; i < ii; i++) { operatorList.addOp(OPS.restore, []); } @@ -15307,40 +15454,165 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { }, getTextContent: function PartialEvaluator_getTextContent(stream, resources, - textState) { + stateManager) { - textState = (textState || new TextState()); + stateManager = (stateManager || new StateManager(new TextState())); - var bidiTexts = []; + var textContent = { + items: [], + styles: Object.create(null) + }; + var bidiTexts = textContent.items; var SPACE_FACTOR = 0.35; var MULTI_SPACE_FACTOR = 1.5; var self = this; var xref = this.xref; - function handleSetFont(fontName, fontRef) { - return self.loadFont(fontName, fontRef, xref, resources, null); - } + resources = (xref.fetchIfRef(resources) || Dict.empty); - resources = (xref.fetchIfRef(resources) || new Dict()); // The xobj is parsed iff it's needed, e.g. if there is a `DO` cmd. var xobjs = null; var xobjsCache = {}; - var preprocessor = new EvaluatorPreprocessor(stream, xref); + var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); var res = resources; - var chunkBuf = []; - var font = null; - var charSpace = 0, wordSpace = 0; var operation; + var textState; + + function newTextChunk() { + var font = textState.font; + if (!(font.loadedName in textContent.styles)) { + textContent.styles[font.loadedName] = { + fontFamily: font.fallbackName, + ascent: font.ascent, + descent: font.descent, + vertical: font.vertical + }; + } + return { + str: '', + dir: null, + width: 0, + height: 0, + transform: null, + fontName: font.loadedName + }; + } + + function runBidi(textChunk) { + var bidiResult = PDFJS.bidi(textChunk.str, -1, textState.font.vertical); + textChunk.str = bidiResult.str; + textChunk.dir = bidiResult.dir; + return textChunk; + } + + function handleSetFont(fontName, fontRef) { + var font = textState.font = self.loadFont(fontName, fontRef, xref, + resources, null).translated; + textState.fontMatrix = font.fontMatrix ? font.fontMatrix : + FONT_IDENTITY_MATRIX; + } + + function buildTextGeometry(chars, textChunk) { + var font = textState.font; + textChunk = textChunk || newTextChunk(); + if (!textChunk.transform) { + // 9.4.4 Text Space Details + var tsm = [textState.fontSize * textState.textHScale, 0, + 0, textState.fontSize, + 0, textState.textRise]; + var trm = textChunk.transform = Util.transform(textState.ctm, + Util.transform(textState.textMatrix, tsm)); + if (!font.vertical) { + textChunk.height = Math.sqrt(trm[2] * trm[2] + trm[3] * trm[3]); + } else { + textChunk.width = Math.sqrt(trm[0] * trm[0] + trm[1] * trm[1]); + } + } + var width = 0; + var height = 0; + var glyphs = font.charsToGlyphs(chars); + var defaultVMetrics = font.defaultVMetrics; + for (var i = 0; i < glyphs.length; i++) { + var glyph = glyphs[i]; + if (!glyph) { // Previous glyph was a space. + continue; + } + var vMetricX = null; + var vMetricY = null; + var glyphWidth = null; + if (font.vertical) { + if (glyph.vmetric) { + glyphWidth = glyph.vmetric[0]; + vMetricX = glyph.vmetric[1]; + vMetricY = glyph.vmetric[2]; + } else { + glyphWidth = glyph.width; + vMetricX = glyph.width * 0.5; + vMetricY = defaultVMetrics[2]; + } + } else { + glyphWidth = glyph.width; + } + + var glyphUnicode = glyph.unicode; + if (glyphUnicode in NormalizedUnicodes) { + glyphUnicode = NormalizedUnicodes[glyphUnicode]; + } + glyphUnicode = reverseIfRtl(glyphUnicode); + + // The following will calculate the x and y of the individual glyphs. + // if (font.vertical) { + // tsm[4] -= vMetricX * Math.abs(textState.fontSize) * + // textState.fontMatrix[0]; + // tsm[5] -= vMetricY * textState.fontSize * + // textState.fontMatrix[0]; + // } + // var trm = Util.transform(textState.textMatrix, tsm); + // var pt = Util.applyTransform([trm[4], trm[5]], textState.ctm); + // var x = pt[0]; + // var y = pt[1]; + + var tx = 0; + var ty = 0; + if (!font.vertical) { + var w0 = glyphWidth * textState.fontMatrix[0]; + tx = (w0 * textState.fontSize + textState.charSpacing) * + textState.textHScale; + width += tx; + } else { + var w1 = glyphWidth * textState.fontMatrix[0]; + ty = w1 * textState.fontSize + textState.charSpacing; + height += ty; + } + textState.translateTextMatrix(tx, ty); + + textChunk.str += glyphUnicode; + } + + var a = textState.textLineMatrix[0]; + var b = textState.textLineMatrix[1]; + var scaleLineX = Math.sqrt(a * a + b * b); + a = textState.ctm[0]; + b = textState.ctm[1]; + var scaleCtmX = Math.sqrt(a * a + b * b); + if (!font.vertical) { + textChunk.width += width * scaleCtmX * scaleLineX; + } else { + textChunk.height += Math.abs(height * scaleCtmX * scaleLineX); + } + return textChunk; + } + while ((operation = preprocessor.read())) { + textState = stateManager.state; var fn = operation.fn; var args = operation.args; switch (fn) { - // TODO: Add support for SAVE/RESTORE and XFORM here. case OPS.setFont: - font = handleSetFont(args[0].name).translated; + handleSetFont(args[0].name); textState.fontSize = args[1]; break; case OPS.setTextRise: @@ -15353,76 +15625,93 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { textState.leading = args[0]; break; case OPS.moveText: - textState.translateTextMatrix(args[0], args[1]); + textState.translateTextLineMatrix(args[0], args[1]); + textState.textMatrix = textState.textLineMatrix.slice(); break; case OPS.setLeadingMoveText: textState.leading = -args[1]; - textState.translateTextMatrix(args[0], args[1]); + textState.translateTextLineMatrix(args[0], args[1]); + textState.textMatrix = textState.textLineMatrix.slice(); break; case OPS.nextLine: - textState.translateTextMatrix(0, -textState.leading); + textState.carriageReturn(); break; case OPS.setTextMatrix: - textState.setTextMatrix(args[0], args[1], - args[2], args[3], args[4], args[5]); + textState.setTextMatrix(args[0], args[1], args[2], args[3], + args[4], args[5]); + textState.setTextLineMatrix(args[0], args[1], args[2], args[3], + args[4], args[5]); break; case OPS.setCharSpacing: - charSpace = args[0]; + textState.charSpace = args[0]; break; case OPS.setWordSpacing: - wordSpace = args[0]; + textState.wordSpace = args[0]; break; case OPS.beginText: - textState.initialiseTextObj(); + textState.textMatrix = IDENTITY_MATRIX.slice(); + textState.textLineMatrix = IDENTITY_MATRIX.slice(); break; case OPS.showSpacedText: var items = args[0]; + var textChunk = newTextChunk(); + var offset; for (var j = 0, jj = items.length; j < jj; j++) { if (typeof items[j] === 'string') { - chunkBuf.push(fontCharsToUnicode(items[j], font)); - } else if (items[j] < 0 && font.spaceWidth > 0) { - var fakeSpaces = -items[j] / font.spaceWidth; - if (fakeSpaces > MULTI_SPACE_FACTOR) { - fakeSpaces = Math.round(fakeSpaces); - while (fakeSpaces--) { - chunkBuf.push(' '); + buildTextGeometry(items[j], textChunk); + } else { + var val = items[j] / 1000; + if (!textState.font.vertical) { + offset = -val * textState.fontSize * textState.textHScale; + textState.translateTextMatrix(offset, 0); + textChunk.width += offset; + } else { + offset = -val * textState.fontSize; + textState.translateTextMatrix(0, offset); + textChunk.height += offset; + } + if (items[j] < 0 && textState.font.spaceWidth > 0) { + var fakeSpaces = -items[j] / textState.font.spaceWidth; + if (fakeSpaces > MULTI_SPACE_FACTOR) { + fakeSpaces = Math.round(fakeSpaces); + while (fakeSpaces--) { + textChunk.str += ' '; + } + } else if (fakeSpaces > SPACE_FACTOR) { + textChunk.str += ' '; } - } else if (fakeSpaces > SPACE_FACTOR) { - chunkBuf.push(' '); } } } + bidiTexts.push(runBidi(textChunk)); break; case OPS.showText: - chunkBuf.push(fontCharsToUnicode(args[0], font)); + bidiTexts.push(runBidi(buildTextGeometry(args[0]))); break; 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. - chunkBuf.push(fontCharsToUnicode(args[0], font)); + textState.carriageReturn(); + bidiTexts.push(runBidi(buildTextGeometry(args[0]))); break; case OPS.nextLineSetSpacingShowText: - // Note comment in "'" - chunkBuf.push(fontCharsToUnicode(args[2], font)); + textState.wordSpacing = args[0]; + textState.charSpacing = args[1]; + textState.carriageReturn(); + bidiTexts.push(runBidi(buildTextGeometry(args[2]))); break; case OPS.paintXObject: - // Set the chunk such that the following if won't add something - // to the state. - chunkBuf.length = 0; - if (args[0].code) { break; } if (!xobjs) { - xobjs = (resources.get('XObject') || new Dict()); + xobjs = (resources.get('XObject') || Dict.empty); } var name = args[0].name; if (xobjsCache.key === name) { if (xobjsCache.texts) { - Util.concatenateToArray(bidiTexts, xobjsCache.texts); + Util.concatenateToArray(bidiTexts, xobjsCache.texts.items); + Util.extendObj(textContent.styles, xobjsCache.texts.styles); } break; } @@ -15443,11 +15732,23 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { break; } - var formTexts = this.getTextContent(xobj, - (xobj.dict.get('Resources') || resources), textState); + stateManager.save(); + var matrix = xobj.dict.get('Matrix'); + if (isArray(matrix) && matrix.length === 6) { + stateManager.transform(matrix); + } + + var formTextContent = this.getTextContent( + xobj, + xobj.dict.get('Resources') || resources, + stateManager + ); + Util.concatenateToArray(bidiTexts, formTextContent.items); + Util.extendObj(textContent.styles, formTextContent.styles); + stateManager.restore(); + xobjsCache.key = name; - xobjsCache.texts = formTexts; - Util.concatenateToArray(bidiTexts, formTexts); + xobjsCache.texts = formTextContent; break; case OPS.setGState: var dictName = args[0]; @@ -15461,36 +15762,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { for (var i = 0; i < gsState.length; i++) { if (gsState[i] === 'Font') { - font = handleSetFont(args[0].name).translated; + handleSetFont(args[0].name); } } break; - } + } // switch + } // while - if (chunkBuf.length > 0) { - var chunk = chunkBuf.join(''); - var bidiText = PDFJS.bidi(chunk, -1, font.vertical); - var renderParams = textState.calcRenderParams(preprocessor.ctm); - 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); - - chunkBuf.length = 0; - } - } - - return bidiTexts; + return textContent; }, extractDataStructures: function @@ -15526,8 +15805,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // differences to be merged in here not require us to hold on to it. var differences = []; var baseEncodingName = null; + var encoding; if (dict.has('Encoding')) { - var encoding = dict.get('Encoding'); + encoding = dict.get('Encoding'); if (isDict(encoding)) { baseEncodingName = encoding.get('BaseEncoding'); baseEncodingName = (isName(baseEncodingName) ? @@ -15562,9 +15842,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { if (baseEncodingName) { properties.defaultEncoding = Encodings[baseEncodingName].slice(); } else { - var encoding = (properties.type === 'TrueType' ? - Encodings.WinAnsiEncoding : - Encodings.StandardEncoding); + encoding = (properties.type === 'TrueType' ? + 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 if (!!(properties.flags & FontFlags.Symbolic)) { @@ -15633,21 +15912,22 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { var defaultWidth = 0; var glyphsVMetrics = []; var defaultVMetrics; + var i, ii, j, jj, start, code, widths; if (properties.composite) { defaultWidth = dict.get('DW') || 1000; - var widths = dict.get('W'); + widths = dict.get('W'); if (widths) { - for (var i = 0, ii = widths.length; i < ii; i++) { - var start = widths[i++]; - var code = xref.fetchIfRef(widths[i]); + for (i = 0, ii = widths.length; i < ii; i++) { + start = widths[i++]; + code = xref.fetchIfRef(widths[i]); if (isArray(code)) { - for (var j = 0, jj = code.length; j < jj; j++) { + for (j = 0, jj = code.length; j < jj; j++) { glyphsWidths[start++] = code[j]; } } else { var width = widths[++i]; - for (var j = start; j <= code; j++) { + for (j = start; j <= code; j++) { glyphsWidths[j] = width; } } @@ -15659,16 +15939,16 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { defaultVMetrics = [vmetrics[1], defaultWidth * 0.5, vmetrics[0]]; vmetrics = dict.get('W2'); if (vmetrics) { - for (var i = 0, ii = vmetrics.length; i < ii; i++) { - var start = vmetrics[i++]; - var code = xref.fetchIfRef(vmetrics[i]); + for (i = 0, ii = vmetrics.length; i < ii; i++) { + start = vmetrics[i++]; + code = xref.fetchIfRef(vmetrics[i]); if (isArray(code)) { - for (var j = 0, jj = code.length; j < jj; j++) { + for (j = 0, jj = code.length; j < jj; j++) { glyphsVMetrics[start++] = [code[j++], code[j++], code[j]]; } } else { var vmetric = [vmetrics[++i], vmetrics[++i], vmetrics[++i]]; - for (var j = start; j <= code; j++) { + for (j = start; j <= code; j++) { glyphsVMetrics[j] = vmetric; } } @@ -15677,10 +15957,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } } else { var firstChar = properties.firstChar; - var widths = dict.get('Widths'); + widths = dict.get('Widths'); if (widths) { - var j = firstChar; - for (var i = 0, ii = widths.length; i < ii; i++) { + j = firstChar; + for (i = 0, ii = widths.length; i < ii; i++) { glyphsWidths[j++] = widths[i]; } defaultWidth = (parseFloat(descriptor.get('MissingWidth')) || 0); @@ -15782,12 +16062,13 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { return widths; }, - translateFont: function PartialEvaluator_translateFont(dict, xref) { + preEvaluateFont: function PartialEvaluator_preEvaluateFont(dict, xref) { var baseDict = dict; var type = dict.get('Subtype'); assertWellFormed(isName(type), 'invalid font Subtype'); var composite = false; + var uint8array; if (type.name == 'Type0') { // If font is a composite // - get the descendant font @@ -15803,14 +16084,61 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { assertWellFormed(isName(type), 'invalid font Subtype'); composite = true; } - var maxCharIndex = (composite ? 0xFFFF : 0xFF); var descriptor = dict.get('FontDescriptor'); + if (descriptor) { + var hash = new MurmurHash3_64(); + var encoding = baseDict.getRaw('Encoding'); + if (isName(encoding)) { + hash.update(encoding.name); + } else if (isRef(encoding)) { + hash.update(encoding.num + '_' + encoding.gen); + } + + var toUnicode = dict.get('ToUnicode') || baseDict.get('ToUnicode'); + if (isStream(toUnicode)) { + var stream = toUnicode.str || toUnicode; + uint8array = stream.buffer ? + new Uint8Array(stream.buffer.buffer, 0, stream.bufferLength) : + new Uint8Array(stream.bytes.buffer, + stream.start, stream.end - stream.start); + hash.update(uint8array); + + } else if (isName(toUnicode)) { + hash.update(toUnicode.name); + } + + var widths = dict.get('Widths') || baseDict.get('Widths'); + if (widths) { + uint8array = new Uint8Array(new Uint32Array(widths).buffer); + hash.update(uint8array); + } + } + + return { + descriptor: descriptor, + dict: dict, + baseDict: baseDict, + composite: composite, + hash: hash ? hash.hexdigest() : '' + }; + }, + + translateFont: function PartialEvaluator_translateFont(preEvaluatedFont, + xref) { + var baseDict = preEvaluatedFont.baseDict; + var dict = preEvaluatedFont.dict; + var composite = preEvaluatedFont.composite; + var descriptor = preEvaluatedFont.descriptor; + var type = dict.get('Subtype'); + var maxCharIndex = (composite ? 0xFFFF : 0xFF); + var properties; + if (!descriptor) { if (type.name == 'Type3') { // FontDescriptor is only required for Type3 fonts when the document // is a tagged pdf. Create a barbebones one to get by. - descriptor = new Dict(); + descriptor = new Dict(null); descriptor.set('FontName', Name.get(type.name)); } else { // Before PDF 1.5 if the font was one of the base 14 fonts, having a @@ -15833,7 +16161,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { (symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic : FontFlags.Nonsymbolic); - var properties = { + properties = { type: type.name, name: baseFontName, widths: metrics.widths, @@ -15874,6 +16202,12 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { info('The FontDescriptor\'s FontName is "' + fontNameStr + '" but should be the same as the Font\'s BaseFont "' + baseFontStr + '"'); + // Workaround for cases where e.g. fontNameStr = 'Arial' and + // baseFontStr = 'Arial,Bold' (needed when no font file is embedded). + if (fontNameStr && baseFontStr && + baseFontStr.search(fontNameStr) === 0) { + fontName = baseFont; + } } } fontName = (fontName || baseFont); @@ -15892,7 +16226,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } } - var properties = { + properties = { type: type.name, name: fontName.name, subtype: subtype, @@ -16040,64 +16374,89 @@ var OperatorList = (function OperatorListClosure() { return OperatorList; })(); +var StateManager = (function StateManagerClosure() { + function StateManager(initialState) { + this.state = initialState; + this.stateStack = []; + } + StateManager.prototype = { + save: function () { + var old = this.state; + this.stateStack.push(this.state); + this.state = old.clone(); + }, + restore: function () { + var prev = this.stateStack.pop(); + if (prev) { + this.state = prev; + } + }, + transform: function (args) { + this.state.ctm = Util.transform(this.state.ctm, args); + } + }; + return StateManager; +})(); + var TextState = (function TextStateClosure() { function TextState() { + this.ctm = new Float32Array(IDENTITY_MATRIX); this.fontSize = 0; - this.textMatrix = [1, 0, 0, 1, 0, 0]; - this.stateStack = []; - //textState variables + this.font = null; + this.fontMatrix = FONT_IDENTITY_MATRIX; + this.textMatrix = IDENTITY_MATRIX.slice(); + this.textLineMatrix = IDENTITY_MATRIX.slice(); + this.charSpacing = 0; + this.wordSpacing = 0; this.leading = 0; this.textHScale = 1; this.textRise = 0; } TextState.prototype = { - 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; - }, setTextMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) { var m = this.textMatrix; m[0] = a; m[1] = b; m[2] = c; m[3] = d; m[4] = e; m[5] = f; }, + setTextLineMatrix: function TextState_setTextMatrix(a, b, c, d, e, f) { + var m = this.textLineMatrix; + m[0] = a; m[1] = b; m[2] = c; m[3] = d; m[4] = e; m[5] = f; + }, 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(cm) { - var tm = this.textMatrix; - var a = this.fontSize; - var b = a * this.textHScale; - var c = this.textRise; - var vScale = Math.sqrt((tm[2] * tm[2]) + (tm[3] * tm[3])); - var angle = Math.atan2(tm[1], tm[0]); - var m0 = tm[0] * cm[0] + tm[1] * cm[2]; - var m1 = tm[0] * cm[1] + tm[1] * cm[3]; - var m2 = tm[2] * cm[0] + tm[3] * cm[2]; - var m3 = tm[2] * cm[1] + tm[3] * cm[3]; - var m4 = tm[4] * cm[0] + tm[5] * cm[2] + cm[4]; - var m5 = tm[4] * cm[1] + tm[5] * cm[3] + cm[5]; - var renderMatrix = [ - b * m0, - b * m1, - a * m2, - a * m3, - c * m2 + m4, - c * m3 + m5 - ]; - return { - renderMatrix: renderMatrix, - vScale: vScale, - angle: angle - }; + translateTextLineMatrix: function TextState_translateTextMatrix(x, y) { + var m = this.textLineMatrix; + m[4] = m[0] * x + m[2] * y + m[4]; + m[5] = m[1] * x + m[3] * y + m[5]; }, + calcRenderMatrix: function TextState_calcRendeMatrix(ctm) { + // 9.4.4 Text Space Details + var tsm = [this.fontSize * this.textHScale, 0, + 0, this.fontSize, + 0, this.textRise]; + return Util.transform(ctm, Util.transform(this.textMatrix, tsm)); + }, + carriageReturn: function TextState_carriageReturn() { + this.translateTextLineMatrix(0, -this.leading); + this.textMatrix = this.textLineMatrix.slice(); + }, + clone: function TextState_clone() { + var clone = Object.create(this); + clone.textMatrix = this.textMatrix.slice(); + clone.textLineMatrix = this.textLineMatrix.slice(); + clone.fontMatrix = this.fontMatrix.slice(); + return clone; + } }; return TextState; })(); var EvalState = (function EvalStateClosure() { function EvalState() { + this.ctm = new Float32Array(IDENTITY_MATRIX); this.font = null; this.textRenderingMode = TextRenderingMode.FILL; } @@ -16109,7 +16468,7 @@ var EvalState = (function EvalStateClosure() { return EvalState; })(); -var EvaluatorPreprocessor = (function EvaluatorPreprocessor() { +var EvaluatorPreprocessor = (function EvaluatorPreprocessorClosure() { // Specifies properties for each command // // If variableArgs === true: [0, `numArgs`] expected @@ -16223,17 +16582,16 @@ var EvaluatorPreprocessor = (function EvaluatorPreprocessor() { 'null': null }; - function EvaluatorPreprocessor(stream, xref) { + function EvaluatorPreprocessor(stream, xref, stateManager) { // 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 = []; + this.stateManager = stateManager; } EvaluatorPreprocessor.prototype = { get savedStatesDepth() { - return this.savedStates.length; + return this.stateManager.stateStack.length; }, read: function EvaluatorPreprocessor_read() { @@ -16290,38 +16648,17 @@ var EvaluatorPreprocessor = (function EvaluatorPreprocessor() { } }, - 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()); + this.stateManager.save(); break; case OPS.restore: - var previousState = this.savedStates.pop(); - if (previousState) { - this.setState(previousState); - } + this.stateManager.restore(); 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; + this.stateManager.transform(args); break; } } @@ -16385,7 +16722,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() { var maxX = 0; var map = [], maxLineHeight = 0; var currentX = IMAGE_PADDING, currentY = IMAGE_PADDING; - for (var q = 0; q < count; q++) { + var q; + for (q = 0; q < count; q++) { var transform = argsArray[j + (q << 2) + 1]; var img = argsArray[j + (q << 2) + 2][0]; if (currentX + img.width > MAX_WIDTH) { @@ -16407,7 +16745,7 @@ var QueueOptimizer = (function QueueOptimizerClosure() { var imgHeight = currentY + maxLineHeight + IMAGE_PADDING; var imgData = new Uint8Array(imgWidth * imgHeight * 4); var imgRowSize = imgWidth << 2; - for (var q = 0; q < count; q++) { + for (q = 0; q < count; q++) { var data = argsArray[j + (q << 2) + 2][0].data; // copy image by lines and extends pixels into padding var rowSize = map[q].w << 2; @@ -16451,7 +16789,7 @@ var QueueOptimizer = (function QueueOptimizerClosure() { var fnArray = context.fnArray, argsArray = context.argsArray; var j = context.currentOperation - 3, i = j + 4; - var ii = fnArray.length; + var ii = fnArray.length, q; for (; i < ii && fnArray[i - 4] === fnArray[i]; i++) {} var count = (i - j) >> 2; @@ -16462,12 +16800,13 @@ var QueueOptimizer = (function QueueOptimizerClosure() { } var isSameImage = false; + var transformArgs; if (argsArray[j + 1][1] === 0 && argsArray[j + 1][2] === 0) { i = j + 4; isSameImage = true; - for (var q = 1; q < count; q++, i += 4) { + for (q = 1; q < count; q++, i += 4) { var prevTransformArgs = argsArray[i - 3]; - var transformArgs = argsArray[i + 1]; + transformArgs = argsArray[i + 1]; if (argsArray[i - 2][0] !== argsArray[i + 2][0] || prevTransformArgs[0] !== transformArgs[0] || prevTransformArgs[1] !== transformArgs[1] || @@ -16487,8 +16826,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() { count = Math.min(count, MAX_SAME_IMAGES_IN_MASKS_BLOCK); var positions = new Float32Array(count * 2); i = j + 1; - for (var q = 0; q < count; q++) { - var transformArgs = argsArray[i]; + for (q = 0; q < count; q++) { + transformArgs = argsArray[i]; positions[(q << 1)] = transformArgs[4]; positions[(q << 1) + 1] = transformArgs[5]; i += 4; @@ -16503,8 +16842,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() { } else { count = Math.min(count, MAX_IMAGES_IN_MASKS_BLOCK); var images = []; - for (var q = 0; q < count; q++) { - var transformArgs = argsArray[j + (q << 2) + 1]; + for (q = 0; q < count; q++) { + transformArgs = argsArray[j + (q << 2) + 1]; var maskParams = argsArray[j + (q << 2) + 2][0]; images.push({ data: maskParams.data, width: maskParams.width, height: maskParams.height, @@ -16531,6 +16870,7 @@ var QueueOptimizer = (function QueueOptimizerClosure() { return; } var ii = fnArray.length; + var transformArgs; for (; i + 3 < ii && fnArray[i - 4] === fnArray[i]; i += 4) { if (fnArray[i - 3] !== fnArray[i + 1] || fnArray[i - 2] !== fnArray[i + 2] || @@ -16541,7 +16881,7 @@ var QueueOptimizer = (function QueueOptimizerClosure() { break; // different image } var prevTransformArgs = argsArray[i - 3]; - var transformArgs = argsArray[i + 1]; + transformArgs = argsArray[i + 1]; if (prevTransformArgs[0] !== transformArgs[0] || prevTransformArgs[1] !== transformArgs[1] || prevTransformArgs[2] !== transformArgs[2] || @@ -16558,7 +16898,7 @@ var QueueOptimizer = (function QueueOptimizerClosure() { var positions = new Float32Array(count * 2); i = j + 1; for (var q = 0; q < count; q++) { - var transformArgs = argsArray[i]; + transformArgs = argsArray[i]; positions[(q << 1)] = transformArgs[4]; positions[(q << 1) + 1] = transformArgs[5]; i += 4; @@ -16651,6 +16991,841 @@ var QueueOptimizer = (function QueueOptimizerClosure() { })(); +var BUILT_IN_CMAPS = [ +// << Start unicode maps. +'Adobe-GB1-UCS2', +'Adobe-CNS1-UCS2', +'Adobe-Japan1-UCS2', +'Adobe-Korea1-UCS2', +// >> End unicode maps. +'78-EUC-H', +'78-EUC-V', +'78-H', +'78-RKSJ-H', +'78-RKSJ-V', +'78-V', +'78ms-RKSJ-H', +'78ms-RKSJ-V', +'83pv-RKSJ-H', +'90ms-RKSJ-H', +'90ms-RKSJ-V', +'90msp-RKSJ-H', +'90msp-RKSJ-V', +'90pv-RKSJ-H', +'90pv-RKSJ-V', +'Add-H', +'Add-RKSJ-H', +'Add-RKSJ-V', +'Add-V', +'Adobe-CNS1-0', +'Adobe-CNS1-1', +'Adobe-CNS1-2', +'Adobe-CNS1-3', +'Adobe-CNS1-4', +'Adobe-CNS1-5', +'Adobe-CNS1-6', +'Adobe-GB1-0', +'Adobe-GB1-1', +'Adobe-GB1-2', +'Adobe-GB1-3', +'Adobe-GB1-4', +'Adobe-GB1-5', +'Adobe-Japan1-0', +'Adobe-Japan1-1', +'Adobe-Japan1-2', +'Adobe-Japan1-3', +'Adobe-Japan1-4', +'Adobe-Japan1-5', +'Adobe-Japan1-6', +'Adobe-Korea1-0', +'Adobe-Korea1-1', +'Adobe-Korea1-2', +'B5-H', +'B5-V', +'B5pc-H', +'B5pc-V', +'CNS-EUC-H', +'CNS-EUC-V', +'CNS1-H', +'CNS1-V', +'CNS2-H', +'CNS2-V', +'ETHK-B5-H', +'ETHK-B5-V', +'ETen-B5-H', +'ETen-B5-V', +'ETenms-B5-H', +'ETenms-B5-V', +'EUC-H', +'EUC-V', +'Ext-H', +'Ext-RKSJ-H', +'Ext-RKSJ-V', +'Ext-V', +'GB-EUC-H', +'GB-EUC-V', +'GB-H', +'GB-V', +'GBK-EUC-H', +'GBK-EUC-V', +'GBK2K-H', +'GBK2K-V', +'GBKp-EUC-H', +'GBKp-EUC-V', +'GBT-EUC-H', +'GBT-EUC-V', +'GBT-H', +'GBT-V', +'GBTpc-EUC-H', +'GBTpc-EUC-V', +'GBpc-EUC-H', +'GBpc-EUC-V', +'H', +'HKdla-B5-H', +'HKdla-B5-V', +'HKdlb-B5-H', +'HKdlb-B5-V', +'HKgccs-B5-H', +'HKgccs-B5-V', +'HKm314-B5-H', +'HKm314-B5-V', +'HKm471-B5-H', +'HKm471-B5-V', +'HKscs-B5-H', +'HKscs-B5-V', +'Hankaku', +'Hiragana', +'KSC-EUC-H', +'KSC-EUC-V', +'KSC-H', +'KSC-Johab-H', +'KSC-Johab-V', +'KSC-V', +'KSCms-UHC-H', +'KSCms-UHC-HW-H', +'KSCms-UHC-HW-V', +'KSCms-UHC-V', +'KSCpc-EUC-H', +'KSCpc-EUC-V', +'Katakana', +'NWP-H', +'NWP-V', +'RKSJ-H', +'RKSJ-V', +'Roman', +'UniCNS-UCS2-H', +'UniCNS-UCS2-V', +'UniCNS-UTF16-H', +'UniCNS-UTF16-V', +'UniCNS-UTF32-H', +'UniCNS-UTF32-V', +'UniCNS-UTF8-H', +'UniCNS-UTF8-V', +'UniGB-UCS2-H', +'UniGB-UCS2-V', +'UniGB-UTF16-H', +'UniGB-UTF16-V', +'UniGB-UTF32-H', +'UniGB-UTF32-V', +'UniGB-UTF8-H', +'UniGB-UTF8-V', +'UniJIS-UCS2-H', +'UniJIS-UCS2-HW-H', +'UniJIS-UCS2-HW-V', +'UniJIS-UCS2-V', +'UniJIS-UTF16-H', +'UniJIS-UTF16-V', +'UniJIS-UTF32-H', +'UniJIS-UTF32-V', +'UniJIS-UTF8-H', +'UniJIS-UTF8-V', +'UniJIS2004-UTF16-H', +'UniJIS2004-UTF16-V', +'UniJIS2004-UTF32-H', +'UniJIS2004-UTF32-V', +'UniJIS2004-UTF8-H', +'UniJIS2004-UTF8-V', +'UniJISPro-UCS2-HW-V', +'UniJISPro-UCS2-V', +'UniJISPro-UTF8-V', +'UniJISX0213-UTF32-H', +'UniJISX0213-UTF32-V', +'UniJISX02132004-UTF32-H', +'UniJISX02132004-UTF32-V', +'UniKS-UCS2-H', +'UniKS-UCS2-V', +'UniKS-UTF16-H', +'UniKS-UTF16-V', +'UniKS-UTF32-H', +'UniKS-UTF32-V', +'UniKS-UTF8-H', +'UniKS-UTF8-V', +'V', +'WP-Symbol']; + +// CMap, not to be confused with TrueType's cmap. +var CMap = (function CMapClosure() { + function CMap(builtInCMap) { + // Codespace ranges are stored as follows: + // [[1BytePairs], [2BytePairs], [3BytePairs], [4BytePairs]] + // where nBytePairs are ranges e.g. [low1, high1, low2, high2, ...] + this.codespaceRanges = [[], [], [], []]; + this.numCodespaceRanges = 0; + this.map = []; + this.vertical = false; + this.useCMap = null; + this.builtInCMap = builtInCMap; + } + CMap.prototype = { + addCodespaceRange: function(n, low, high) { + this.codespaceRanges[n - 1].push(low, high); + this.numCodespaceRanges++; + }, + + mapRange: function(low, high, dstLow) { + var lastByte = dstLow.length - 1; + while (low <= high) { + this.map[low] = dstLow; + // Only the last byte has to be incremented. + dstLow = dstLow.substr(0, lastByte) + + String.fromCharCode(dstLow.charCodeAt(lastByte) + 1); + ++low; + } + }, + + mapRangeToArray: function(low, high, array) { + var i = 0; + while (low <= high) { + this.map[low] = array[i++]; + ++low; + } + }, + + mapOne: function(src, dst) { + this.map[src] = dst; + }, + + lookup: function(code) { + return this.map[code]; + }, + + readCharCode: function(str, offset) { + var c = 0; + var codespaceRanges = this.codespaceRanges; + var codespaceRangesLen = this.codespaceRanges.length; + // 9.7.6.2 CMap Mapping + // The code length is at most 4. + for (var n = 0; n < codespaceRangesLen; n++) { + c = ((c << 8) | str.charCodeAt(offset + n)) >>> 0; + // Check each codespace range to see if it falls within. + var codespaceRange = codespaceRanges[n]; + for (var k = 0, kk = codespaceRange.length; k < kk;) { + var low = codespaceRange[k++]; + var high = codespaceRange[k++]; + if (c >= low && c <= high) { + return [c, n + 1]; + } + } + } + + return [0, 1]; + } + + }; + return CMap; +})(); + +var IdentityCMap = (function IdentityCMapClosure() { + function IdentityCMap(vertical, n) { + CMap.call(this); + this.vertical = vertical; + this.addCodespaceRange(n, 0, 0xffff); + this.mapRange(0, 0xffff, '\u0000'); + } + Util.inherit(IdentityCMap, CMap, {}); + + return IdentityCMap; +})(); + +var BinaryCMapReader = (function BinaryCMapReaderClosure() { + function fetchBinaryData(url) { + var nonBinaryRequest = PDFJS.disableWorker; + var request = new XMLHttpRequest(); + request.open('GET', url, false); + if (!nonBinaryRequest) { + try { + request.responseType = 'arraybuffer'; + nonBinaryRequest = request.responseType !== 'arraybuffer'; + } catch (e) { + nonBinaryRequest = true; + } + } + if (nonBinaryRequest && request.overrideMimeType) { + request.overrideMimeType('text/plain; charset=x-user-defined'); + } + request.send(null); + if (request.status === 0 && /^https?:/i.test(url)) { + error('Unable to get binary cMap at: ' + url); + } + if (nonBinaryRequest) { + var data = Array.prototype.map.call(request.responseText, function (ch) { + return ch.charCodeAt(0) & 255; + }); + return new Uint8Array(data); + } + return new Uint8Array(request.response); + } + + function hexToInt(a, size) { + var n = 0; + for (var i = 0; i <= size; i++) { + n = (n << 8) | a[i]; + } + return n >>> 0; + } + + function hexToStr(a, size) { + return String.fromCharCode.apply(null, a.subarray(0, size + 1)); + } + + function addHex(a, b, size) { + var c = 0; + for (var i = size; i >= 0; i--) { + c += a[i] + b[i]; + a[i] = c & 255; + c >>= 8; + } + } + + function incHex(a, size) { + var c = 1; + for (var i = size; i >= 0 && c > 0; i--) { + c += a[i]; + a[i] = c & 255; + c >>= 8; + } + } + + var MAX_NUM_SIZE = 16; + var MAX_ENCODED_NUM_SIZE = 19; // ceil(MAX_NUM_SIZE * 7 / 8) + + function BinaryCMapStream(data) { + this.buffer = data; + this.pos = 0; + this.end = data.length; + this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE); + } + + BinaryCMapStream.prototype = { + readByte: function () { + if (this.pos >= this.end) { + return -1; + } + return this.buffer[this.pos++]; + }, + readNumber: function () { + var n = 0; + var last; + do { + var b = this.readByte(); + if (b < 0) { + error('unexpected EOF in bcmap'); + } + last = !(b & 0x80); + n = (n << 7) | (b & 0x7F); + } while (!last); + return n; + }, + readSigned: function () { + var n = this.readNumber(); + return (n & 1) ? ~(n >>> 1) : n >>> 1; + }, + readHex: function (num, size) { + num.set(this.buffer.subarray(this.pos, + this.pos + size + 1)); + this.pos += size + 1; + }, + readHexNumber: function (num, size) { + var last; + var stack = this.tmpBuf, sp = 0; + do { + var b = this.readByte(); + if (b < 0) { + error('unexpected EOF in bcmap'); + } + last = !(b & 0x80); + stack[sp++] = b & 0x7F; + } while (!last); + var i = size, buffer = 0, bufferSize = 0; + while (i >= 0) { + while (bufferSize < 8 && stack.length > 0) { + buffer = (stack[--sp] << bufferSize) | buffer; + bufferSize += 7; + } + num[i] = buffer & 255; + i--; + buffer >>= 8; + bufferSize -= 8; + } + }, + readHexSigned: function (num, size) { + this.readHexNumber(num, size); + var sign = num[size] & 1 ? 255 : 0; + var c = 0; + for (var i = 0; i <= size; i++) { + c = ((c & 1) << 8) | num[i]; + num[i] = (c >> 1) ^ sign; + } + }, + readString: function () { + var len = this.readNumber(); + var s = ''; + for (var i = 0; i < len; i++) { + s += String.fromCharCode(this.readNumber()); + } + return s; + } + }; + + function processBinaryCMap(url, cMap, extend) { + var data = fetchBinaryData(url); + var stream = new BinaryCMapStream(data); + + var header = stream.readByte(); + cMap.vertical = !!(header & 1); + + var useCMap = null; + var start = new Uint8Array(MAX_NUM_SIZE); + var end = new Uint8Array(MAX_NUM_SIZE); + var char = new Uint8Array(MAX_NUM_SIZE); + var charCode = new Uint8Array(MAX_NUM_SIZE); + var tmp = new Uint8Array(MAX_NUM_SIZE); + var code; + + var b; + while ((b = stream.readByte()) >= 0) { + var type = b >> 5; + if (type === 7) { // metadata, e.g. comment or usecmap + switch (b & 0x1F) { + case 0: + stream.readString(); // skipping comment + break; + case 1: + useCMap = stream.readString(); + break; + } + continue; + } + var sequence = !!(b & 0x10); + var dataSize = b & 15; + + assert(dataSize + 1 <= MAX_NUM_SIZE); + + var ucs2DataSize = 1; + var subitemsCount = stream.readNumber(); + var i; + switch (type) { + case 0: // codespacerange + stream.readHex(start, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), + hexToInt(end, dataSize)); + for (i = 1; i < subitemsCount; i++) { + incHex(end, dataSize); + stream.readHexNumber(start, dataSize); + addHex(start, end, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), + hexToInt(end, dataSize)); + } + break; + case 1: // notdefrange + stream.readHex(start, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + code = stream.readNumber(); + // undefined range, skipping + for (i = 1; i < subitemsCount; i++) { + incHex(end, dataSize); + stream.readHexNumber(start, dataSize); + addHex(start, end, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + code = stream.readNumber(); + // nop + } + break; + case 2: // cidchar + stream.readHex(char, dataSize); + code = stream.readNumber(); + cMap.mapOne(hexToInt(char, dataSize), String.fromCharCode(code)); + for (i = 1; i < subitemsCount; i++) { + incHex(char, dataSize); + if (!sequence) { + stream.readHexNumber(tmp, dataSize); + addHex(char, tmp, dataSize); + } + code = stream.readSigned() + (code + 1); + cMap.mapOne(hexToInt(char, dataSize), String.fromCharCode(code)); + } + break; + case 3: // cidrange + stream.readHex(start, dataSize); + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + code = stream.readNumber(); + cMap.mapRange(hexToInt(start, dataSize), hexToInt(end, dataSize), + String.fromCharCode(code)); + for (i = 1; i < subitemsCount; i++) { + incHex(end, dataSize); + if (!sequence) { + stream.readHexNumber(start, dataSize); + addHex(start, end, dataSize); + } else { + start.set(end); + } + stream.readHexNumber(end, dataSize); + addHex(end, start, dataSize); + code = stream.readNumber(); + cMap.mapRange(hexToInt(start, dataSize), hexToInt(end, dataSize), + String.fromCharCode(code)); + } + break; + case 4: // bfchar + stream.readHex(char, ucs2DataSize); + stream.readHex(charCode, dataSize); + cMap.mapOne(hexToInt(char, ucs2DataSize), + hexToStr(charCode, dataSize)); + for (i = 1; i < subitemsCount; i++) { + incHex(char, ucs2DataSize); + if (!sequence) { + stream.readHexNumber(tmp, ucs2DataSize); + addHex(char, tmp, ucs2DataSize); + } + incHex(charCode, dataSize); + stream.readHexSigned(tmp, dataSize); + addHex(charCode, tmp, dataSize); + cMap.mapOne(hexToInt(char, ucs2DataSize), + hexToStr(charCode, dataSize)); + } + break; + case 5: // bfrange + stream.readHex(start, ucs2DataSize); + stream.readHexNumber(end, ucs2DataSize); + addHex(end, start, ucs2DataSize); + stream.readHex(charCode, dataSize); + cMap.mapRange(hexToInt(start, ucs2DataSize), + hexToInt(end, ucs2DataSize), + hexToStr(charCode, dataSize)); + for (i = 1; i < subitemsCount; i++) { + incHex(end, ucs2DataSize); + if (!sequence) { + stream.readHexNumber(start, ucs2DataSize); + addHex(start, end, ucs2DataSize); + } else { + start.set(end); + } + stream.readHexNumber(end, ucs2DataSize); + addHex(end, start, ucs2DataSize); + stream.readHex(charCode, dataSize); + cMap.mapRange(hexToInt(start, ucs2DataSize), + hexToInt(end, ucs2DataSize), + hexToStr(charCode, dataSize)); + } + break; + default: + error('Unknown type: ' + type); + break; + } + } + + if (useCMap) { + extend(useCMap); + } + return cMap; + } + + function BinaryCMapReader() {} + + BinaryCMapReader.prototype = { + read: processBinaryCMap + }; + + return BinaryCMapReader; +})(); + +var CMapFactory = (function CMapFactoryClosure() { + function strToInt(str) { + var a = 0; + for (var i = 0; i < str.length; i++) { + a = (a << 8) | str.charCodeAt(i); + } + return a >>> 0; + } + + function expectString(obj) { + if (!isString(obj)) { + error('Malformed CMap: expected string.'); + } + } + + function expectInt(obj) { + if (!isInt(obj)) { + error('Malformed CMap: expected int.'); + } + } + + function parseBfChar(cMap, lexer) { + while (true) { + var obj = lexer.getObj(); + if (isEOF(obj)) { + break; + } + if (isCmd(obj, 'endbfchar')) { + return; + } + expectString(obj); + var src = strToInt(obj); + obj = lexer.getObj(); + // TODO are /dstName used? + expectString(obj); + var dst = obj; + cMap.mapOne(src, dst); + } + } + + function parseBfRange(cMap, lexer) { + while (true) { + var obj = lexer.getObj(); + if (isEOF(obj)) { + break; + } + if (isCmd(obj, 'endbfrange')) { + return; + } + expectString(obj); + var low = strToInt(obj); + obj = lexer.getObj(); + expectString(obj); + var high = strToInt(obj); + obj = lexer.getObj(); + if (isInt(obj) || isString(obj)) { + var dstLow = isInt(obj) ? String.fromCharCode(obj) : obj; + cMap.mapRange(low, high, dstLow); + } else if (isCmd(obj, '[')) { + obj = lexer.getObj(); + var array = []; + while (!isCmd(obj, ']') && !isEOF(obj)) { + array.push(obj); + obj = lexer.getObj(); + } + cMap.mapRangeToArray(low, high, array); + } else { + break; + } + } + error('Invalid bf range.'); + } + + function parseCidChar(cMap, lexer) { + while (true) { + var obj = lexer.getObj(); + if (isEOF(obj)) { + break; + } + if (isCmd(obj, 'endcidchar')) { + return; + } + expectString(obj); + var src = strToInt(obj); + obj = lexer.getObj(); + expectInt(obj); + var dst = String.fromCharCode(obj); + cMap.mapOne(src, dst); + } + } + + function parseCidRange(cMap, lexer) { + while (true) { + var obj = lexer.getObj(); + if (isEOF(obj)) { + break; + } + if (isCmd(obj, 'endcidrange')) { + return; + } + expectString(obj); + var low = strToInt(obj); + obj = lexer.getObj(); + expectString(obj); + var high = strToInt(obj); + obj = lexer.getObj(); + expectInt(obj); + var dstLow = String.fromCharCode(obj); + cMap.mapRange(low, high, dstLow); + } + } + + function parseCodespaceRange(cMap, lexer) { + while (true) { + var obj = lexer.getObj(); + if (isEOF(obj)) { + break; + } + if (isCmd(obj, 'endcodespacerange')) { + return; + } + if (!isString(obj)) { + break; + } + var low = strToInt(obj); + obj = lexer.getObj(); + if (!isString(obj)) { + break; + } + var high = strToInt(obj); + cMap.addCodespaceRange(obj.length, low, high); + } + error('Invalid codespace range.'); + } + + function parseWMode(cMap, lexer) { + var obj = lexer.getObj(); + if (isInt(obj)) { + cMap.vertical = !!obj; + } + } + + function parseCMap(cMap, lexer, builtInCMapParams, useCMap) { + var previous; + var embededUseCMap; + objLoop: while (true) { + var obj = lexer.getObj(); + if (isEOF(obj)) { + break; + } else if (isName(obj)) { + if (obj.name === 'WMode') { + parseWMode(cMap, lexer); + } + previous = obj; + } else if (isCmd(obj)) { + switch (obj.cmd) { + case 'endcmap': + break objLoop; + case 'usecmap': + if (isName(previous)) { + embededUseCMap = previous.name; + } + break; + case 'begincodespacerange': + parseCodespaceRange(cMap, lexer); + break; + case 'beginbfchar': + parseBfChar(cMap, lexer); + break; + case 'begincidchar': + parseCidChar(cMap, lexer); + break; + case 'beginbfrange': + parseBfRange(cMap, lexer); + break; + case 'begincidrange': + parseCidRange(cMap, lexer); + break; + } + } + } + + if (!useCMap && embededUseCMap) { + // Load the usecmap definition from the file only if there wasn't one + // specified. + useCMap = embededUseCMap; + } + if (useCMap) { + extendCMap(cMap, builtInCMapParams, useCMap); + } + } + + function extendCMap(cMap, builtInCMapParams, useCMap) { + cMap.useCMap = createBuiltInCMap(useCMap, builtInCMapParams); + // If there aren't any code space ranges defined clone all the parent ones + // into this cMap. + if (cMap.numCodespaceRanges === 0) { + var useCodespaceRanges = cMap.useCMap.codespaceRanges; + for (var i = 0; i < useCodespaceRanges.length; i++) { + cMap.codespaceRanges[i] = useCodespaceRanges[i].slice(); + } + cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges; + } + // Merge the map into the current one, making sure not to override + // any previously defined entries. + for (var key in cMap.useCMap.map) { + if (key in cMap.map) { + continue; + } + cMap.map[key] = cMap.useCMap.map[key]; + } + } + + function parseBinaryCMap(name, builtInCMapParams) { + var url = builtInCMapParams.url + name + '.bcmap'; + var cMap = new CMap(true); + new BinaryCMapReader().read(url, cMap, function (useCMap) { + extendCMap(cMap, builtInCMapParams, useCMap); + }); + return cMap; + } + + function createBuiltInCMap(name, builtInCMapParams) { + if (name === 'Identity-H') { + return new IdentityCMap(false, 2); + } else if (name === 'Identity-V') { + return new IdentityCMap(true, 2); + } + if (BUILT_IN_CMAPS.indexOf(name) === -1) { + error('Unknown cMap name: ' + name); + } + assert (builtInCMapParams, 'buildin cmap parameters are not provided'); + + if (builtInCMapParams.packed) { + return parseBinaryCMap(name, builtInCMapParams); + } + + var request = new XMLHttpRequest(); + var url = builtInCMapParams.url + name; + request.open('GET', url, false); + request.send(null); + if (request.status === 0 && /^https?:/i.test(url)) { + error('Unable to get cMap at: ' + url); + } + var cMap = new CMap(true); + var lexer = new Lexer(new StringStream(request.responseText)); + parseCMap(cMap, lexer, builtInCMapParams, null); + return cMap; + } + + return { + create: function (encoding, builtInCMapParams, useCMap) { + if (isName(encoding)) { + return createBuiltInCMap(encoding.name, builtInCMapParams); + } else if (isStream(encoding)) { + var cMap = new CMap(); + var lexer = new Lexer(encoding); + try { + parseCMap(cMap, lexer, builtInCMapParams, useCMap); + } catch (e) { + warn('Invalid CMap data. ' + e); + } + return cMap; + } + error('Encoding required.'); + } + }; +})(); + + // Unicode Private Use Area var PRIVATE_USE_OFFSET_START = 0xE000; var PRIVATE_USE_OFFSET_END = 0xF8FF; @@ -16786,7 +17961,7 @@ var Encodings = { 'summation', 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', - 'guillemotright', 'ellipsis', '', 'Agrave', 'Atilde', 'Otilde', 'OE', + 'guillemotright', 'ellipsis', 'space', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', @@ -16838,7 +18013,7 @@ var Encodings = { 'guilsinglleft', 'OE', 'bullet', 'Zcaron', 'bullet', 'bullet', 'quoteleft', 'quoteright', 'quotedblleft', 'quotedblright', 'bullet', 'endash', 'emdash', 'tilde', 'trademark', 'scaron', 'guilsinglright', 'oe', 'bullet', - 'zcaron', 'Ydieresis', '', 'exclamdown', 'cent', 'sterling', + 'zcaron', 'Ydieresis', 'space', 'exclamdown', 'cent', 'sterling', 'currency', 'yen', 'brokenbar', 'section', 'dieresis', 'copyright', 'ordfeminine', 'guillemotleft', 'logicalnot', 'hyphen', 'registered', 'macron', 'degree', 'plusminus', 'twosuperior', 'threesuperior', 'acute', @@ -17000,7 +18175,7 @@ var nonStdFontMap = { 'MS-PMincho': 'MS PMincho', 'MS-PMincho-Bold': 'MS PMincho-Bold', 'MS-PMincho-BoldItalic': 'MS PMincho-BoldItalic', - 'MS-PMincho-Italic': 'MS PMincho-Italic', + 'MS-PMincho-Italic': 'MS PMincho-Italic' }; var serifFonts = { @@ -18730,23 +19905,6 @@ function reverseIfRtl(chars) { return s; } -function fontCharsToUnicode(charCodes, font) { - var glyphs = font.charsToGlyphs(charCodes); - var result = ''; - for (var i = 0, ii = glyphs.length; i < ii; i++) { - var glyph = glyphs[i]; - if (!glyph) { - continue; - } - var glyphUnicode = glyph.unicode; - if (glyphUnicode in NormalizedUnicodes) { - glyphUnicode = NormalizedUnicodes[glyphUnicode]; - } - result += reverseIfRtl(glyphUnicode); - } - return result; -} - function adjustWidths(properties) { if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) { return; @@ -18793,6 +19951,7 @@ var Glyph = (function GlyphClosure() { */ var Font = (function FontClosure() { function Font(name, file, properties) { + var charCode; this.name = name; this.loadedName = properties.loadedName; @@ -18832,7 +19991,7 @@ var Font = (function FontClosure() { this.toFontChar = []; if (properties.type == 'Type3') { - for (var charCode = 0; charCode < 256; charCode++) { + for (charCode = 0; charCode < 256; charCode++) { this.toFontChar[charCode] = (this.differences[charCode] || properties.defaultEncoding[charCode]); } @@ -18876,7 +20035,7 @@ var Font = (function FontClosure() { this.toUnicode = map; } else if (/Symbol/i.test(fontName)) { var symbols = Encodings.SymbolSetEncoding; - for (var charCode in symbols) { + for (charCode in symbols) { var fontChar = GlyphsUnicode[symbols[charCode]]; if (!fontChar) { continue; @@ -18885,13 +20044,13 @@ var Font = (function FontClosure() { } } else if (isStandardFont) { this.toFontChar = []; - for (var charCode in properties.defaultEncoding) { + for (charCode in properties.defaultEncoding) { var glyphName = properties.differences[charCode] || properties.defaultEncoding[charCode]; this.toFontChar[charCode] = GlyphsUnicode[glyphName]; } } else { - for (var charCode in this.toUnicode) { + for (charCode in this.toUnicode) { this.toFontChar[charCode] = this.toUnicode[charCode].charCodeAt(0); } } @@ -18956,21 +20115,12 @@ var Font = (function FontClosure() { this.loading = true; } - function stringToArray(str) { - var array = []; - for (var i = 0, ii = str.length; i < ii; ++i) { - array[i] = str.charCodeAt(i); - } - return array; - } - - function arrayToString(arr) { - var strBuf = []; - for (var i = 0, ii = arr.length; i < ii; ++i) { - strBuf.push(String.fromCharCode(arr[i])); - } - return strBuf.join(''); - } + Font.getFontID = (function () { + var ID = 1; + return function Font_getFontID() { + return String(ID++); + }; + })(); function int16(b0, b1) { return (b0 << 8) + b1; @@ -18996,22 +20146,13 @@ var Font = (function FontClosure() { } function string16(value) { - return (String.fromCharCode((value >> 8) & 0xff) + - String.fromCharCode(value & 0xff)); + return String.fromCharCode((value >> 8) & 0xff, value & 0xff); } function safeString16(value) { // clamp value to the 16-bit int range value = (value > 0x7FFF ? 0x7FFF : (value < -0x8000 ? -0x8000 : value)); - return (String.fromCharCode((value >> 8) & 0xff) + - String.fromCharCode(value & 0xff)); - } - - function string32(value) { - return (String.fromCharCode((value >> 24) & 0xff) + - String.fromCharCode((value >> 16) & 0xff) + - String.fromCharCode((value >> 8) & 0xff) + - String.fromCharCode(value & 0xff)); + return String.fromCharCode((value >> 8) & 0xff, value & 0xff); } function createOpenTypeHeader(sfnt, file, numTables) { @@ -19133,7 +20274,8 @@ var Font = (function FontClosure() { } return { toFontChar: toFontChar, - charCodeToGlyphId: newMap + charCodeToGlyphId: newMap, + nextAvailableFontCharCode: nextAvailableFontCharCode }; } @@ -19179,7 +20321,8 @@ var Font = (function FontClosure() { '\x00\x01' + // encodingID string32(4 + numTables * 8); // start of the table record - for (var i = ranges.length - 1; i >= 0; --i) { + var i, ii, j, jj; + for (i = ranges.length - 1; i >= 0; --i) { if (ranges[i][0] <= 0xFFFF) { break; } } var bmpLength = i + 1; @@ -19201,16 +20344,17 @@ var Font = (function FontClosure() { var idRangeOffsets = ''; var glyphsIds = ''; var bias = 0; - - for (var i = 0, ii = bmpLength; i < ii; i++) { - var range = ranges[i]; - var start = range[0]; - var end = range[1]; + + var range, start, end, codes; + for (i = 0, ii = bmpLength; i < ii; i++) { + range = ranges[i]; + start = range[0]; + end = range[1]; startCount += string16(start); endCount += string16(end); - var codes = range[2]; + codes = range[2]; var contiguous = true; - for (var j = 1, jj = codes.length; j < jj; ++j) { + for (j = 1, jj = codes.length; j < jj; ++j) { if (codes[j] !== codes[j - 1] + 1) { contiguous = false; break; @@ -19223,7 +20367,7 @@ var Font = (function FontClosure() { idDeltas += string16(0); idRangeOffsets += string16(offset); - for (var j = 0, jj = codes.length; j < jj; ++j) { + for (j = 0, jj = codes.length; j < jj; ++j) { glyphsIds += string16(codes[j]); } } else { @@ -19257,14 +20401,14 @@ var Font = (function FontClosure() { string32(4 + numTables * 8 + 4 + format314.length); // start of the table record format31012 = ''; - for (var i = 0, ii = ranges.length; i < ii; i++) { - var range = ranges[i]; - var start = range[0]; - var codes = range[2]; + for (i = 0, ii = ranges.length; i < ii; i++) { + range = ranges[i]; + start = range[0]; + codes = range[2]; var code = codes[0]; - for (var j = 1, jj = codes.length; j < jj; ++j) { + for (j = 1, jj = codes.length; j < jj; ++j) { if (codes[j] !== codes[j - 1] + 1) { - var end = range[0] + j - 1; + end = range[0] + j - 1; format31012 += string32(start) + // startCharCode string32(end) + // endCharCode string32(code); // startGlyphID @@ -19456,11 +20600,12 @@ var Font = (function FontClosure() { // Mac want 1-byte per character strings while Windows want // 2-bytes per character, so duplicate the names table var stringsUnicode = []; - for (var i = 0, ii = strings.length; i < ii; i++) { - var str = proto[1][i] || strings[i]; + var i, ii, j, jj, str; + for (i = 0, ii = strings.length; i < ii; i++) { + str = proto[1][i] || strings[i]; var strBufUnicode = []; - for (var j = 0, jj = str.length; j < jj; j++) { + for (j = 0, jj = str.length; j < jj; j++) { strBufUnicode.push(string16(str.charCodeAt(j))); } stringsUnicode.push(strBufUnicode.join('')); @@ -19479,10 +20624,10 @@ var Font = (function FontClosure() { // Build the name records field var strOffset = 0; - for (var i = 0, ii = platforms.length; i < ii; i++) { + for (i = 0, ii = platforms.length; i < ii; i++) { var strs = names[i]; - for (var j = 0, jj = strs.length; j < jj; j++) { - var str = strs[j]; + for (j = 0, jj = strs.length; j < jj; j++) { + str = strs[j]; var nameRecord = platforms[i] + // platform ID encodings[i] + // encoding ID @@ -19521,11 +20666,7 @@ var Font = (function FontClosure() { checkAndRepair: function Font_checkAndRepair(name, font, properties) { function readTableEntry(file) { - var tag = file.getBytes(4); - tag = String.fromCharCode(tag[0]) + - String.fromCharCode(tag[1]) + - String.fromCharCode(tag[2]) + - String.fromCharCode(tag[3]); + var tag = bytesToString(file.getBytes(4)); var checksum = file.getUint32(); var offset = file.getUint32(); @@ -19555,7 +20696,7 @@ var Font = (function FontClosure() { function readOpenTypeHeader(ttf) { return { - version: arrayToString(ttf.getBytes(4)), + version: bytesToString(ttf.getBytes(4)), numTables: ttf.getUint16(), searchRange: ttf.getUint16(), entrySelector: ttf.getUint16(), @@ -19568,6 +20709,7 @@ var Font = (function FontClosure() { * PDF spec */ function readCmapTable(cmap, font, isSymbolicFont) { + var segment; var start = (font.start ? font.start : 0) + cmap.offset; font.pos = start; @@ -19624,10 +20766,11 @@ var Font = (function FontClosure() { var hasShortCmap = false; var mappings = []; + var j, glyphId; // TODO(mack): refactor this cmap subtable reading logic out if (format === 0) { - for (var j = 0; j < 256; j++) { + for (j = 0; j < 256; j++) { var index = font.getByte(); if (!index) { continue; @@ -19658,7 +20801,7 @@ var Font = (function FontClosure() { var offsetsCount = 0; for (segIndex = 0; segIndex < segCount; segIndex++) { - var segment = segments[segIndex]; + segment = segments[segIndex]; var rangeOffset = font.getUint16(); if (!rangeOffset) { segment.offsetIndex = -1; @@ -19672,22 +20815,24 @@ var Font = (function FontClosure() { } var offsets = []; - for (var j = 0; j < offsetsCount; j++) { + for (j = 0; j < offsetsCount; j++) { offsets.push(font.getUint16()); } for (segIndex = 0; segIndex < segCount; segIndex++) { - var segment = segments[segIndex]; - var start = segment.start, end = segment.end; - var delta = segment.delta, offsetIndex = segment.offsetIndex; + segment = segments[segIndex]; + start = segment.start; + var end = segment.end; + var delta = segment.delta; + offsetIndex = segment.offsetIndex; - for (var j = start; j <= end; j++) { + for (j = start; j <= end; j++) { if (j == 0xFFFF) { continue; } - var glyphId = (offsetIndex < 0 ? - j : offsets[offsetIndex + j - start]); + glyphId = (offsetIndex < 0 ? + j : offsets[offsetIndex + j - start]); glyphId = (glyphId + delta) & 0xFFFF; if (glyphId === 0) { continue; @@ -19709,8 +20854,8 @@ var Font = (function FontClosure() { var glyphs = []; var ids = []; - for (var j = 0; j < entryCount; j++) { - var glyphId = font.getUint16(); + for (j = 0; j < entryCount; j++) { + glyphId = font.getUint16(); var charCode = firstCode + j; mappings.push({ @@ -19726,7 +20871,7 @@ var Font = (function FontClosure() { mappings.sort(function (a, b) { return a.charCode - b.charCode; }); - for (var i = 1; i < mappings.length; i++) { + for (i = 1; i < mappings.length; i++) { if (mappings[i - 1].charCode === mappings[i].charCode) { mappings.splice(i, 1); i--; @@ -19766,13 +20911,14 @@ var Font = (function FontClosure() { var numMissing = numOfSidebearings - ((metrics.length - numOfMetrics * 4) >> 1); + var i, ii; if (numMissing > 0) { font.pos = (font.start ? font.start : 0) + metrics.offset; var entries = ''; - for (var i = 0, ii = metrics.length; i < ii; i++) { + for (i = 0, ii = metrics.length; i < ii; i++) { entries += String.fromCharCode(font.getByte()); } - for (var i = 0; i < numMissing; i++) { + for (i = 0; i < numMissing; i++) { entries += '\x00\x00'; } metrics.data = stringToArray(entries); @@ -19793,8 +20939,8 @@ var Font = (function FontClosure() { return glyf.length; } - var j = 10, flagsCount = 0; - for (var i = 0; i < contoursCount; i++) { + var i, j = 10, flagsCount = 0; + for (i = 0; i < contoursCount; i++) { var endPoint = (glyf[j] << 8) | glyf[j + 1]; flagsCount = endPoint + 1; j += 2; @@ -19806,7 +20952,7 @@ var Font = (function FontClosure() { var instructionsEnd = j; // validating flags var coordinatesLength = 0; - for (var i = 0; i < flagsCount; i++) { + for (i = 0; i < flagsCount; i++) { var flag = glyf[j++]; if (flag & 0xC0) { // reserved flags must be zero, cleaning up @@ -19937,7 +21083,8 @@ var Font = (function FontClosure() { var startOffset = itemDecode(locaData, 0); var writeOffset = 0; itemEncode(locaData, 0, writeOffset); - for (var i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { + var i, j; + for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { var endOffset = itemDecode(locaData, j); if (endOffset > oldGlyfDataLength && ((oldGlyfDataLength + 3) & ~3) === endOffset) { @@ -19964,7 +21111,7 @@ var Font = (function FontClosure() { // to have single glyph with one point var simpleGlyph = new Uint8Array( [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]); - for (var i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { + for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { itemEncode(locaData, j, simpleGlyph.length); } glyf.data = simpleGlyph; @@ -19998,6 +21145,8 @@ var Font = (function FontClosure() { var glyphNames; var valid = true; + var i; + switch (version) { case 0x00010000: glyphNames = MacStandardGlyphOrdering; @@ -20009,7 +21158,7 @@ var Font = (function FontClosure() { break; } var glyphNameIndexes = []; - for (var i = 0; i < numGlyphs; ++i) { + for (i = 0; i < numGlyphs; ++i) { var index = font.getUint16(); if (index >= 32768) { valid = false; @@ -20024,13 +21173,13 @@ var Font = (function FontClosure() { while (font.pos < end) { var stringLength = font.getByte(); var string = ''; - for (var i = 0; i < stringLength; ++i) { + for (i = 0; i < stringLength; ++i) { string += String.fromCharCode(font.getByte()); } customNames.push(string); } glyphNames = []; - for (var i = 0; i < numGlyphs; ++i) { + for (i = 0; i < numGlyphs; ++i) { var j = glyphNameIndexes[i]; if (j < 258) { glyphNames.push(MacStandardGlyphOrdering[j]); @@ -20066,7 +21215,9 @@ var Font = (function FontClosure() { var stringsStart = font.getUint16(); var records = []; var NAME_RECORD_LENGTH = 12; - for (var i = 0; i < numRecords && + var i, ii; + + for (i = 0; i < numRecords && font.pos + NAME_RECORD_LENGTH <= end; i++) { var r = { platform: font.getUint16(), @@ -20082,7 +21233,7 @@ var Font = (function FontClosure() { records.push(r); } } - for (var i = 0, ii = records.length; i < ii; i++) { + for (i = 0, ii = records.length; i < ii; i++) { var record = records[i]; var pos = start + stringsStart + record.offset; if (pos + record.length > end) { @@ -20119,7 +21270,7 @@ var Font = (function FontClosure() { function sanitizeTTProgram(table, ttContext) { var data = table.data; - var i = 0, n, lastEndf = 0, lastDeff = 0; + var i = 0, j, n, b, funcId, pc, lastEndf = 0, lastDeff = 0; var stack = []; var callstack = []; var functionsCalled = []; @@ -20135,7 +21286,7 @@ var Font = (function FontClosure() { if (inFDEF || inELSE) { i += n; } else { - for (var j = 0; j < n; j++) { + for (j = 0; j < n; j++) { stack.push(data[i++]); } } @@ -20144,8 +21295,8 @@ var Font = (function FontClosure() { if (inFDEF || inELSE) { i += n * 2; } else { - for (var j = 0; j < n; j++) { - var b = data[i++]; + for (j = 0; j < n; j++) { + b = data[i++]; stack.push((b << 8) | data[i++]); } } @@ -20154,7 +21305,7 @@ var Font = (function FontClosure() { if (inFDEF || inELSE) { i += n; } else { - for (var j = 0; j < n; j++) { + for (j = 0; j < n; j++) { stack.push(data[i++]); } } @@ -20163,15 +21314,15 @@ var Font = (function FontClosure() { if (inFDEF || inELSE) { i += n * 2; } else { - for (var j = 0; j < n; j++) { - var b = data[i++]; + for (j = 0; j < n; j++) { + b = data[i++]; stack.push((b << 8) | data[i++]); } } } else if (op === 0x2B && !tooComplexToFollowFunctions) { // CALL if (!inFDEF && !inELSE) { // collecting inforamtion about which functions are used - var funcId = stack[stack.length - 1]; + funcId = stack[stack.length - 1]; ttContext.functionsUsed[funcId] = true; if (funcId in ttContext.functionsStackDeltas) { stack.length += ttContext.functionsStackDeltas[funcId]; @@ -20179,7 +21330,7 @@ var Font = (function FontClosure() { functionsCalled.indexOf(funcId) < 0) { callstack.push({data: data, i: i, stackTop: stack.length - 1}); functionsCalled.push(funcId); - var pc = ttContext.functionsDefined[funcId]; + pc = ttContext.functionsDefined[funcId]; if (!pc) { warn('TT: CALL non-existent function'); ttContext.hintsValid = false; @@ -20197,20 +21348,20 @@ var Font = (function FontClosure() { inFDEF = true; // collecting inforamtion about which functions are defined lastDeff = i; - var funcId = stack.pop(); + funcId = stack.pop(); ttContext.functionsDefined[funcId] = {data: data, i: i}; } else if (op === 0x2D) { // ENDF - end of function if (inFDEF) { inFDEF = false; lastEndf = i; } else { - var pc = callstack.pop(); + pc = callstack.pop(); if (!pc) { warn('TT: ENDF bad stack'); ttContext.hintsValid = false; return; } - var funcId = functionsCalled.pop(); + funcId = functionsCalled.pop(); data = pc.data; i = pc.i; ttContext.functionsStackDeltas[funcId] = @@ -20303,13 +21454,14 @@ var Font = (function FontClosure() { if (content.length > 1) { // concatenating the content items var newLength = 0; - for (var j = 0, jj = content.length; j < jj; j++) { + var j, jj; + for (j = 0, jj = content.length; j < jj; j++) { newLength += content[j].length; } newLength = (newLength + 3) & ~3; var result = new Uint8Array(newLength); var pos = 0; - for (var j = 0, jj = content.length; j < jj; j++) { + for (j = 0, jj = content.length; j < jj; j++) { result.set(content[j], pos); pos += content[j].length; } @@ -20351,11 +21503,13 @@ var Font = (function FontClosure() { var header = readOpenTypeHeader(font); var numTables = header.numTables; + var cff, cffFile; var tables = { 'OS/2': null, cmap: null, head: null, hhea: null, hmtx: null, maxp: null, name: null, post: null }; + var table, tableData; for (var i = 0; i < numTables; i++) { - var table = readTableEntry(font); + table = readTableEntry(font); if (VALID_TABLES.indexOf(table.tag) < 0) { continue; // skipping table if it's not a required or optional table } @@ -20370,8 +21524,8 @@ var Font = (function FontClosure() { // OpenType font if (!tables.head || !tables.hhea || !tables.maxp || !tables.post) { // no major tables: throwing everything at CFFFont - var cffFile = new Stream(tables['CFF '].data); - var cff = new CFFFont(cffFile, properties); + cffFile = new Stream(tables['CFF '].data); + cff = new CFFFont(cffFile, properties); return this.convert(name, cff, properties); } @@ -20479,11 +21633,11 @@ var Font = (function FontClosure() { } } - var charCodeToGlyphId = []; + var charCodeToGlyphId = [], charCode; if (properties.type == 'CIDFontType2') { var cidToGidMap = properties.cidToGidMap || []; var cMap = properties.cMap.map; - for (var charCode in cMap) { + for (charCode in cMap) { charCode |= 0; var cid = cMap[charCode]; assert(cid.length === 1, 'Max size of CID is 65,535'); @@ -20523,7 +21677,7 @@ var Font = (function FontClosure() { properties.baseEncodingName === 'WinAnsiEncoding') { baseEncoding = Encodings[properties.baseEncodingName]; } - for (var charCode = 0; charCode < 256; charCode++) { + for (charCode = 0; charCode < 256; charCode++) { var glyphName; if (this.differences && charCode in this.differences) { glyphName = this.differences[charCode]; @@ -20545,7 +21699,7 @@ var Font = (function FontClosure() { } var found = false; - for (var i = 0; i < cmapMappingsLength; ++i) { + for (i = 0; i < cmapMappingsLength; ++i) { if (cmapMappings[i].charCode === unicodeOrCharCode) { charCodeToGlyphId[charCode] = cmapMappings[i].glyphId; found = true; @@ -20555,7 +21709,7 @@ var Font = (function FontClosure() { if (!found && properties.glyphNames) { // Try to map using the post table. There are currently no known // pdfs that this fixes. - var glyphId = properties.glyphNames.indexOf(glyphName); + glyphId = properties.glyphNames.indexOf(glyphName); if (glyphId > 0) { charCodeToGlyphId[charCode] = glyphId; } @@ -20575,8 +21729,8 @@ var Font = (function FontClosure() { // associated glyph descriptions from the subtable'. This means // charcodes in the cmap will be single bytes, so no-op since // glyph.charCode & 0xFF === glyph.charCode - for (var i = 0; i < cmapMappingsLength; ++i) { - var charCode = cmapMappings[i].charCode & 0xFF; + for (i = 0; i < cmapMappingsLength; ++i) { + charCode = cmapMappings[i].charCode & 0xFF; charCodeToGlyphId[charCode] = cmapMappings[i].glyphId; } } @@ -20625,9 +21779,9 @@ var Font = (function FontClosure() { if (!isTrueType) { try { // Trying to repair CFF file - var cffFile = new Stream(tables['CFF '].data); + cffFile = new Stream(tables['CFF '].data); var parser = new CFFParser(cffFile, properties); - var cff = parser.parse(); + cff = parser.parse(); var compiler = new CFFCompiler(cff); tables['CFF '].data = compiler.compile(); } catch (e) { @@ -20648,11 +21802,11 @@ var Font = (function FontClosure() { } // rewrite the tables but tweak offsets - for (var i = 0; i < numTables; i++) { - var table = tables[tablesNames[i]]; + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; var data = []; - var tableData = table.data; + tableData = table.data; for (var j = 0, jj = tableData.length; j < jj; j++) { data.push(tableData[j]); } @@ -20660,10 +21814,10 @@ var Font = (function FontClosure() { } // Add the table datas - for (var i = 0; i < numTables; i++) { - var table = tables[tablesNames[i]]; - var tableData = table.data; - ttf.file += arrayToString(tableData); + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; + tableData = table.data; + ttf.file += bytesToString(new Uint8Array(tableData)); // 4-byte aligned data while (ttf.file.length & 3) { @@ -20696,18 +21850,25 @@ var Font = (function FontClosure() { this.toFontChar = newMapping.toFontChar; var numGlyphs = font.numGlyphs; + function getCharCode(charCodeToGlyphId, glyphId, addMap) { + for (var charCode in charCodeToGlyphId) { + if (glyphId === charCodeToGlyphId[charCode]) { + return charCode | 0; + } + } + if (addMap) { + newMapping.charCodeToGlyphId[newMapping.nextAvailableFontCharCode] = + glyphId; + return newMapping.nextAvailableFontCharCode++; + } + return null; + } + var seacs = font.seacs; if (SEAC_ANALYSIS_ENABLED && seacs && seacs.length) { var matrix = properties.fontMatrix || FONT_IDENTITY_MATRIX; var charset = font.getCharset(); - var charCodeToGlyphId = mapping; - var toFontChar = newMapping.toFontChar; - var seacs = font.seacs; var seacMap = Object.create(null); - var glyphIdToCharCode = Object.create(null); - for (var charCode in charCodeToGlyphId) { - glyphIdToCharCode[charCodeToGlyphId[charCode]] = charCode | 0; - } for (var glyphId in seacs) { glyphId |= 0; var seac = seacs[glyphId]; @@ -20722,10 +21883,23 @@ var Font = (function FontClosure() { x: seac[0] * matrix[0] + seac[1] * matrix[2] + matrix[4], y: seac[0] * matrix[1] + seac[1] * matrix[3] + matrix[5] }; - var charCode = glyphIdToCharCode[glyphId]; + + var charCode = getCharCode(mapping, glyphId); + if (charCode === null) { + // There's no point in mapping it if the char code was never mapped + // to begin with. + continue; + } + // Find a fontCharCode that maps to the base and accent glyphs. If one + // doesn't exists, create it. + var charCodeToGlyphId = newMapping.charCodeToGlyphId; + var baseFontCharCode = getCharCode(charCodeToGlyphId, baseGlyphId, + true); + var accentFontCharCode = getCharCode(charCodeToGlyphId, accentGlyphId, + true); seacMap[charCode] = { - baseFontCharCode: toFontChar[glyphIdToCharCode[baseGlyphId]], - accentFontCharCode: toFontChar[glyphIdToCharCode[accentGlyphId]], + baseFontCharCode: baseFontCharCode, + accentFontCharCode: accentFontCharCode, accentOffset: accentOffset }; } @@ -20818,12 +21992,13 @@ var Font = (function FontClosure() { 'post': stringToArray(createPostTable(properties)) }; - for (var field in fields) { + var field; + for (field in fields) { createTableEntry(otf, field, fields[field]); } - for (var field in fields) { + for (field in fields) { var table = fields[field]; - otf.file += arrayToString(table); + otf.file += bytesToString(new Uint8Array(table)); } return stringToArray(otf.file); @@ -20842,7 +22017,7 @@ var Font = (function FontClosure() { toUnicode: null }; // Section 9.10.2 Mapping Character Codes to Unicode Values - if (properties.toUnicode) { + if (properties.toUnicode && properties.toUnicode.length !== 0) { map.toUnicode = properties.toUnicode; return map; } @@ -20851,20 +22026,36 @@ var Font = (function FontClosure() { // the differences array only contains adobe standard or symbol set names, // in pratice it seems better to always try to create a toUnicode // map based of the default encoding. + var toUnicode, charcode; if (!properties.composite /* is simple font */) { - var toUnicode = []; + toUnicode = []; var encoding = properties.defaultEncoding.slice(); // Merge in the differences array. var differences = properties.differences; - for (var charcode in differences) { + for (charcode in differences) { encoding[charcode] = differences[charcode]; } - for (var charcode in encoding) { + for (charcode in encoding) { // a) Map the character code to a character name. var glyphName = encoding[charcode]; // b) Look up the character name in the Adobe Glyph List (see the // Bibliography) to obtain the corresponding Unicode value. if (glyphName === '' || !(glyphName in GlyphsUnicode)) { + // (undocumented) c) Few heuristics to recognize unknown glyphs + // NOTE: Adobe Reader does not do this step, but OSX Preview does + var code; + // Gxx glyph + if (glyphName.length === 3 && + glyphName[0] === 'G' && + (code = parseInt(glyphName.substr(1), 16))) { + toUnicode[charcode] = String.fromCharCode(code); + } + // Cddd glyph + if (glyphName.length >= 3 && + glyphName[0] === 'C' && + (code = +glyphName.substr(1))) { + toUnicode[charcode] = String.fromCharCode(code); + } continue; } toUnicode[charcode] = String.fromCharCode(GlyphsUnicode[glyphName]); @@ -20901,8 +22092,8 @@ var Font = (function FontClosure() { var ucs2CMap = CMapFactory.create(ucs2CMapName, { url: PDFJS.cMapUrl, packed: PDFJS.cMapPacked }, null); var cMap = properties.cMap; - var toUnicode = []; - for (var charcode in cMap.map) { + toUnicode = []; + for (charcode in cMap.map) { var cid = cMap.map[charcode]; assert(cid.length === 1, 'Max size of CID is 65,535'); // e) Map the CID obtained in step (a) according to the CMap obtained @@ -20919,9 +22110,9 @@ var Font = (function FontClosure() { } // The viewer's choice, just use an identity map. - var toUnicode = []; + toUnicode = []; var firstChar = properties.firstChar, lastChar = properties.lastChar; - for (var i = firstChar, ii = lastChar; i <= ii; i++) { + for (var i = firstChar; i <= lastChar; i++) { toUnicode[i] = String.fromCharCode(i); } map.isIdentity = true; @@ -20980,7 +22171,7 @@ var Font = (function FontClosure() { if (this.cMap && charcode in this.cMap.map) { widthCode = this.cMap.map[charcode].charCodeAt(0); } - var width = this.widths[widthCode]; + width = this.widths[widthCode]; width = isNum(width) ? width : this.defaultWidth; var vmetric = this.vmetrics && this.vmetrics[widthCode]; @@ -21026,7 +22217,7 @@ var Font = (function FontClosure() { charsToGlyphs: function Font_charsToGlyphs(chars) { var charsCache = this.charsCache; - var glyphs; + var glyphs, glyph, charcode; // if we translated this string before, just grab it from the cache if (charsCache) { @@ -21043,17 +22234,17 @@ var Font = (function FontClosure() { glyphs = []; var charsCacheKey = chars; + var i = 0, ii; if (this.cMap) { - var i = 0; // composite fonts have multi-byte strings convert the string from // single-byte to multi-byte while (i < chars.length) { var c = this.cMap.readCharCode(chars, i); - var charcode = c[0]; + charcode = c[0]; var length = c[1]; i += length; - var glyph = this.charToGlyph(charcode); + glyph = this.charToGlyph(charcode); glyphs.push(glyph); // placing null after each word break charcode (ASCII SPACE) // Ignore occurences of 0x20 in multiple-byte codes. @@ -21062,9 +22253,9 @@ var Font = (function FontClosure() { } } } else { - for (var i = 0, ii = chars.length; i < ii; ++i) { - var charcode = chars.charCodeAt(i); - var glyph = this.charToGlyph(charcode); + for (i = 0, ii = chars.length; i < ii; ++i) { + charcode = chars.charCodeAt(i); + glyph = this.charToGlyph(charcode); glyphs.push(glyph); if (charcode == 0x20) { glyphs.push(null); @@ -21108,12 +22299,14 @@ var ErrorFont = (function ErrorFontClosure() { */ function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) { var charCodeToGlyphId = Object.create(null); + var glyphId, charCode, baseEncoding; + if (properties.baseEncodingName) { // If a valid base encoding name was used, the mapping is initialized with // that. - var baseEncoding = Encodings[properties.baseEncodingName]; - for (var charCode = 0; charCode < baseEncoding.length; charCode++) { - var glyphId = glyphNames.indexOf(baseEncoding[charCode]); + baseEncoding = Encodings[properties.baseEncodingName]; + for (charCode = 0; charCode < baseEncoding.length; charCode++) { + glyphId = glyphNames.indexOf(baseEncoding[charCode]); if (glyphId >= 0) { charCodeToGlyphId[charCode] = glyphId; } @@ -21121,15 +22314,15 @@ function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) { } else if (!!(properties.flags & FontFlags.Symbolic)) { // For a symbolic font the encoding should be the fonts built-in // encoding. - for (var charCode in builtInEncoding) { + for (charCode in builtInEncoding) { charCodeToGlyphId[charCode] = builtInEncoding[charCode]; } } else { // For non-symbolic fonts that don't have a base encoding the standard // encoding should be used. - var baseEncoding = Encodings.StandardEncoding; - for (var charCode = 0; charCode < baseEncoding.length; charCode++) { - var glyphId = glyphNames.indexOf(baseEncoding[charCode]); + baseEncoding = Encodings.StandardEncoding; + for (charCode = 0; charCode < baseEncoding.length; charCode++) { + glyphId = glyphNames.indexOf(baseEncoding[charCode]); if (glyphId >= 0) { charCodeToGlyphId[charCode] = glyphId; } @@ -21139,9 +22332,9 @@ function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) { // Lastly, merge in the differences. var differences = properties.differences; if (differences) { - for (var charCode in differences) { + for (charCode in differences) { var glyphName = differences[charCode]; - var glyphId = glyphNames.indexOf(glyphName); + glyphId = glyphNames.indexOf(glyphName); if (glyphId >= 0) { charCodeToGlyphId[charCode] = glyphId; } @@ -21219,6 +22412,7 @@ var Type1CharString = (function Type1CharStringClosure() { convert: function Type1CharString_convert(encoded, subrs) { var count = encoded.length; var error = false; + var wx, sbx, subrNumber; for (var i = 0; i < count; i++) { var value = encoded[i]; if (value < 32) { @@ -21276,7 +22470,7 @@ var Type1CharString = (function Type1CharStringClosure() { error = true; break; } - var subrNumber = this.stack.pop(); + subrNumber = this.stack.pop(); error = this.convert(subrs[subrNumber], subrs); break; case 11: // return @@ -21288,8 +22482,8 @@ var Type1CharString = (function Type1CharStringClosure() { } // To convert to type2 we have to move the width value to the // first part of the charstring and then use hmoveto with lsb. - var wx = this.stack.pop(); - var sbx = this.stack.pop(); + wx = this.stack.pop(); + sbx = this.stack.pop(); this.lsb = sbx; this.width = wx; this.stack.push(sbx); @@ -21362,9 +22556,9 @@ var Type1CharString = (function Type1CharStringClosure() { // (dx, dy). The height argument will not be used for vmtx and // vhea tables reconstruction -- ignoring it. var wy = this.stack.pop(); - var wx = this.stack.pop(); + wx = this.stack.pop(); var sby = this.stack.pop(); - var sbx = this.stack.pop(); + sbx = this.stack.pop(); this.lsb = sbx; this.width = wx; this.stack.push(sbx, sby); @@ -21384,7 +22578,7 @@ var Type1CharString = (function Type1CharStringClosure() { error = true; break; } - var subrNumber = this.stack.pop(); + subrNumber = this.stack.pop(); var numArgs = this.stack.pop(); if (subrNumber === 0 && numArgs === 3) { var flexArgs = this.stack.splice(this.stack.length - 17, 17); @@ -21606,7 +22800,7 @@ var Type1Parser = (function Type1ParserClosure() { } } }; - var token; + var token, length, data, lenIV, encoded; while ((token = this.getToken()) !== null) { if (token !== '/') { continue; @@ -21630,12 +22824,11 @@ var Type1Parser = (function Type1ParserClosure() { continue; } var glyph = this.getToken(); - var length = this.readInt(); + length = this.readInt(); this.getToken(); // read in 'RD' or '-|' - var data = stream.makeSubStream(stream.pos, length); - var lenIV = program.properties.privateData['lenIV']; - var encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, - lenIV); + data = stream.makeSubStream(stream.pos, length); + lenIV = program.properties.privateData['lenIV']; + encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, lenIV); // Skip past the required space and binary data. stream.skip(length); this.nextChar(); @@ -21654,12 +22847,11 @@ var Type1Parser = (function Type1ParserClosure() { this.getToken(); // read in 'array' while ((token = this.getToken()) === 'dup') { var index = this.readInt(); - var length = this.readInt(); + length = this.readInt(); this.getToken(); // read in 'RD' or '-|' - var data = stream.makeSubStream(stream.pos, length); - var lenIV = program.properties.privateData['lenIV']; - var encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, - lenIV); + data = stream.makeSubStream(stream.pos, length); + lenIV = program.properties.privateData['lenIV']; + encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, lenIV); // Skip past the required space and binary data. stream.skip(length); this.nextChar(); @@ -21706,8 +22898,8 @@ var Type1Parser = (function Type1ParserClosure() { } for (var i = 0; i < charstrings.length; i++) { - var glyph = charstrings[i].glyph; - var encoded = charstrings[i].encoded; + glyph = charstrings[i].glyph; + encoded = charstrings[i].encoded; var charString = new Type1CharString(); var error = charString.convert(encoded, subrs); var output = charString.output; @@ -21753,7 +22945,7 @@ var Type1Parser = (function Type1ParserClosure() { this.getToken(); // read in 'array' for (var j = 0; j < size; j++) { - var token = this.getToken(); + token = this.getToken(); // skipping till first dup or def (e.g. ignoring for statement) while (token !== 'dup' && token !== 'def') { token = this.getToken(); @@ -21921,15 +23113,15 @@ Type1Font.prototype = { getGlyphMapping: function Type1Font_getGlyphMapping(properties) { var charstrings = this.charstrings; - var glyphNames = ['.notdef']; - for (var glyphId = 0; glyphId < charstrings.length; glyphId++) { + var glyphNames = ['.notdef'], glyphId; + for (glyphId = 0; glyphId < charstrings.length; glyphId++) { glyphNames.push(charstrings[glyphId].glyphName); } var encoding = properties.builtInEncoding; if (encoding) { var builtInEncoding = {}; for (var charCode in encoding) { - var glyphId = glyphNames.indexOf(encoding[charCode]); + glyphId = glyphNames.indexOf(encoding[charCode]); if (glyphId >= 0) { builtInEncoding[charCode] = glyphId; } @@ -21974,11 +23166,12 @@ Type1Font.prototype = { // Add a bunch of empty subrs to deal with the Type2 bias var type2Subrs = []; - for (var i = 0; i < bias; i++) { + var i; + for (i = 0; i < bias; i++) { type2Subrs.push([0x0B]); } - for (var i = 0; i < count; i++) { + for (i = 0; i < count; i++) { type2Subrs.push(type1Subrs[i]); } @@ -22019,7 +23212,8 @@ Type1Font.prototype = { var count = glyphs.length; var charsetArray = [0]; - for (var i = 0; i < count; i++) { + var i, ii; + for (i = 0; i < count; i++) { var index = CFFStandardStrings.indexOf(charstrings[i].glyphName); // TODO: Insert the string and correctly map it. Previously it was // thought mapping names that aren't in the standard strings to .notdef @@ -22034,7 +23228,7 @@ Type1Font.prototype = { var charStringsIndex = new CFFIndex(); charStringsIndex.add([0x8B, 0x0E]); // .notdef - for (var i = 0; i < count; i++) { + for (i = 0; i < count; i++) { charStringsIndex.add(glyphs[i]); } cff.charStrings = charStringsIndex; @@ -22057,7 +23251,7 @@ Type1Font.prototype = { 'StdHW', 'StdVW' ]; - for (var i = 0, ii = fields.length; i < ii; i++) { + for (i = 0, ii = fields.length; i < ii; i++) { var field = fields[i]; if (!properties.privateData.hasOwnProperty(field)) { continue; @@ -22075,7 +23269,7 @@ Type1Font.prototype = { cff.topDict.privateDict = privateDict; var subrIndex = new CFFIndex(); - for (var i = 0, ii = subrs.length; i < ii; i++) { + for (i = 0, ii = subrs.length; i < ii; i++) { subrIndex.add(subrs[i]); } privateDict.subrsIndex = subrIndex; @@ -22112,22 +23306,25 @@ var CFFFont = (function CFFFontClosure() { }, getGlyphMapping: function CFFFont_getGlyphMapping() { var cff = this.cff; + var properties = this.properties; var charsets = cff.charset.charset; - var charCodeToGlyphId = Object.create(null); + var charCodeToGlyphId; + var glyphId; - if (this.properties.composite) { - if (this.cff.isCIDFont) { + if (properties.composite) { + charCodeToGlyphId = Object.create(null); + if (cff.isCIDFont) { // If the font is actually a CID font then we should use the charset // to map CIDs to GIDs. - for (var glyphId = 0; glyphId < charsets.length; glyphId++) { + for (glyphId = 0; glyphId < charsets.length; glyphId++) { var cidString = String.fromCharCode(charsets[glyphId]); - var charCode = this.properties.cMap.map.indexOf(cidString); + var charCode = properties.cMap.map.indexOf(cidString); charCodeToGlyphId[charCode] = glyphId; } } else { // If it is NOT actually a CID font then CIDs should be mapped // directly to GIDs. - for (var glyphId = 0; glyphId < cff.charStrings.count; glyphId++) { + for (glyphId = 0; glyphId < cff.charStrings.count; glyphId++) { charCodeToGlyphId[glyphId] = glyphId; } } @@ -22135,7 +23332,8 @@ var CFFFont = (function CFFFontClosure() { } var encoding = cff.encoding ? cff.encoding.encoding : null; - return type1FontGlyphMapping(this.properties, encoding, charsets); + charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets); + return charCodeToGlyphId; } }; @@ -22395,7 +23593,7 @@ var CFFParser = (function CFFParserClosure() { var operands = []; var entries = []; - var pos = 0; + pos = 0; var end = dict.length; while (pos < end) { var b = dict[pos]; @@ -22419,13 +23617,14 @@ var CFFParser = (function CFFParserClosure() { var offsets = []; var start = pos; var end = pos; + var i, ii; if (count !== 0) { var offsetSize = bytes[pos++]; // add 1 for offset to determine size of last object var startPos = pos + ((count + 1) * offsetSize) - 1; - for (var i = 0, ii = count + 1; i < ii; ++i) { + for (i = 0, ii = count + 1; i < ii; ++i) { var offset = 0; for (var j = 0; j < offsetSize; ++j) { offset <<= 8; @@ -22435,7 +23634,7 @@ var CFFParser = (function CFFParserClosure() { } end = offsets[count]; } - for (var i = 0, ii = offsets.length - 1; i < ii; ++i) { + for (i = 0, ii = offsets.length - 1; i < ii; ++i) { var offsetStart = offsets[i]; var offsetEnd = offsets[i + 1]; cffIndex.add(bytes.subarray(offsetStart, offsetEnd)); @@ -22465,7 +23664,7 @@ var CFFParser = (function CFFParserClosure() { } data[j] = c; } - names.push(String.fromCharCode.apply(null, data)); + names.push(bytesToString(data)); } return names; }, @@ -22473,7 +23672,7 @@ var CFFParser = (function CFFParserClosure() { var strings = new CFFStrings(); for (var i = 0, ii = index.count; i < ii; ++i) { var data = index.get(i); - strings.add(String.fromCharCode.apply(null, data)); + strings.add(bytesToString(data)); } return strings; }, @@ -22651,31 +23850,32 @@ var CFFParser = (function CFFParserClosure() { var start = pos; var format = bytes[pos++]; var charset = ['.notdef']; + var id, count, i; // subtract 1 for the .notdef glyph length -= 1; switch (format) { case 0: - for (var i = 0; i < length; i++) { - var id = (bytes[pos++] << 8) | bytes[pos++]; + for (i = 0; i < length; i++) { + id = (bytes[pos++] << 8) | bytes[pos++]; charset.push(cid ? id : strings.get(id)); } break; case 1: while (charset.length <= length) { - var id = (bytes[pos++] << 8) | bytes[pos++]; - var count = bytes[pos++]; - for (var i = 0; i <= count; i++) { + id = (bytes[pos++] << 8) | bytes[pos++]; + count = bytes[pos++]; + for (i = 0; i <= count; i++) { charset.push(cid ? id++ : strings.get(id++)); } } break; case 2: while (charset.length <= length) { - var id = (bytes[pos++] << 8) | bytes[pos++]; - var count = (bytes[pos++] << 8) | bytes[pos++]; - for (var i = 0; i <= count; i++) { + id = (bytes[pos++] << 8) | bytes[pos++]; + count = (bytes[pos++] << 8) | bytes[pos++]; + for (i = 0; i <= count; i++) { charset.push(cid ? id++ : strings.get(id++)); } } @@ -22697,12 +23897,12 @@ var CFFParser = (function CFFParserClosure() { var bytes = this.bytes; var predefined = false; var hasSupplement = false; - var format; + var format, i, ii; var raw = null; function readSupplement() { var supplementsCount = bytes[pos++]; - for (var i = 0; i < supplementsCount; i++) { + for (i = 0; i < supplementsCount; i++) { var code = bytes[pos++]; var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff); encoding[code] = charset.indexOf(strings.get(sid)); @@ -22714,7 +23914,7 @@ var CFFParser = (function CFFParserClosure() { format = pos; var baseEncoding = pos ? Encodings.ExpertEncoding : Encodings.StandardEncoding; - for (var i = 0, ii = charset.length; i < ii; i++) { + for (i = 0, ii = charset.length; i < ii; i++) { var index = baseEncoding.indexOf(charset[i]); if (index != -1) { encoding[index] = i; @@ -22722,11 +23922,11 @@ var CFFParser = (function CFFParserClosure() { } } else { var dataStart = pos; - var format = bytes[pos++]; + format = bytes[pos++]; switch (format & 0x7f) { case 0: var glyphsCount = bytes[pos++]; - for (var i = 1; i <= glyphsCount; i++) { + for (i = 1; i <= glyphsCount; i++) { encoding[bytes[pos++]] = i; } break; @@ -22734,7 +23934,7 @@ var CFFParser = (function CFFParserClosure() { case 1: var rangesCount = bytes[pos++]; var gid = 1; - for (var i = 0; i < rangesCount; i++) { + for (i = 0; i < rangesCount; i++) { var start = bytes[pos++]; var left = bytes[pos++]; for (var j = start; j <= start + left; j++) { @@ -22768,16 +23968,18 @@ var CFFParser = (function CFFParserClosure() { var bytes = this.bytes; var format = bytes[pos++]; var fdSelect = []; + var i; + switch (format) { case 0: - for (var i = 0; i < length; ++i) { + for (i = 0; i < length; ++i) { var id = bytes[pos++]; fdSelect.push(id); } break; case 3: var rangesCount = (bytes[pos++] << 8) | bytes[pos++]; - for (var i = 0; i < rangesCount; ++i) { + for (i = 0; i < rangesCount; ++i) { var first = (bytes[pos++] << 8) | bytes[pos++]; var fdIndex = bytes[pos++]; var next = (bytes[pos] << 8) | bytes[pos + 1]; @@ -23141,13 +24343,6 @@ var CFFOffsetTracker = (function CFFOffsetTrackerClosure() { // Takes a CFF and converts it to the binary representation. var CFFCompiler = (function CFFCompilerClosure() { - function stringToArray(str) { - var array = []; - for (var i = 0, ii = str.length; i < ii; ++i) { - array[i] = str.charCodeAt(i); - } - return array; - } function CFFCompiler(cff) { this.cff = cff; } @@ -23244,7 +24439,7 @@ var CFFCompiler = (function CFFCompilerClosure() { output.add(fdSelect); // It is unclear if the sub font dictionary can have CID related // dictionary keys, but the sanitizer doesn't like them so remove them. - var compiled = this.compileTopDicts(cff.fdArray, output.length, true); + compiled = this.compileTopDicts(cff.fdArray, output.length, true); topDictTracker.setEntryLocation('FDArray', [output.length], output); output.add(compiled.output); var fontDictTrackers = compiled.trackers; @@ -23278,7 +24473,8 @@ var CFFCompiler = (function CFFCompilerClosure() { } var nibbles = ''; - for (var i = 0, ii = value.length; i < ii; ++i) { + var i, ii; + for (i = 0, ii = value.length; i < ii; ++i) { var a = value[i]; if (a === 'e') { nibbles += value[++i] === '-' ? 'c' : 'b'; @@ -23292,7 +24488,7 @@ var CFFCompiler = (function CFFCompilerClosure() { } nibbles += (nibbles.length & 1) ? 'f' : 'ff'; var out = [30]; - for (var i = 0, ii = nibbles.length; i < ii; i += 2) { + for (i = 0, ii = nibbles.length; i < ii; i += 2) { out.push(parseInt(nibbles.substr(i, 2), 16)); } return out; @@ -23495,8 +24691,8 @@ var CFFCompiler = (function CFFCompilerClosure() { var data = [(count >> 8) & 0xFF, count & 0xff]; - var lastOffset = 1; - for (var i = 0; i < count; ++i) { + var lastOffset = 1, i; + for (i = 0; i < count; ++i) { lastOffset += objects[i].length; } @@ -23516,7 +24712,7 @@ var CFFCompiler = (function CFFCompilerClosure() { // Add another offset after this one because we need a new offset var relativeOffset = 1; - for (var i = 0; i < count + 1; i++) { + for (i = 0; i < count + 1; i++) { if (offsetSize === 1) { data.push(relativeOffset & 0xFF); } else if (offsetSize === 2) { @@ -23539,7 +24735,7 @@ var CFFCompiler = (function CFFCompilerClosure() { } var offset = data.length; - for (var i = 0; i < count; i++) { + for (i = 0; i < count; i++) { // Notify the tracker where the object will be offset in the data. if (trackers[i]) { trackers[i].offset(data.length); @@ -23585,22 +24781,23 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { var offset = (getUshort(data, start + 2) === 1 ? getLong(data, start + 8) : getLong(data, start + 16)); var format = getUshort(data, start + offset); + var length, ranges, p, i; if (format === 4) { - var length = getUshort(data, start + offset + 2); + length = getUshort(data, start + offset + 2); var segCount = getUshort(data, start + offset + 6) >> 1; - var p = start + offset + 14; - var ranges = []; - for (var i = 0; i < segCount; i++, p += 2) { + p = start + offset + 14; + ranges = []; + for (i = 0; i < segCount; i++, p += 2) { ranges[i] = {end: getUshort(data, p)}; } p += 2; - for (var i = 0; i < segCount; i++, p += 2) { + for (i = 0; i < segCount; i++, p += 2) { ranges[i].start = getUshort(data, p); } - for (var i = 0; i < segCount; i++, p += 2) { + for (i = 0; i < segCount; i++, p += 2) { ranges[i].idDelta = getUshort(data, p); } - for (var i = 0; i < segCount; i++, p += 2) { + for (i = 0; i < segCount; i++, p += 2) { var idOffset = getUshort(data, p); if (idOffset === 0) { continue; @@ -23613,11 +24810,11 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { } return ranges; } else if (format === 12) { - var length = getLong(data, start + offset + 4); + length = getLong(data, start + offset + 4); var groups = getLong(data, start + offset + 12); - var p = start + offset + 16; - var ranges = []; - for (var i = 0; i < groups; i++) { + p = start + offset + 16; + ranges = []; + for (i = 0; i < groups; i++) { ranges.push({ start: getLong(data, p), end: getLong(data, p + 4), @@ -23703,12 +24900,13 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { var yMin = ((code[i + 4] << 24) | (code[i + 5] << 16)) >> 16; var xMax = ((code[i + 6] << 24) | (code[i + 7] << 16)) >> 16; var yMax = ((code[i + 8] << 24) | (code[i + 9] << 16)) >> 16; + var flags; + var x = 0, y = 0; i += 10; if (numberOfContours < 0) { // composite glyph - var x = 0, y = 0; do { - var flags = (code[i] << 8) | code[i + 1]; + flags = (code[i] << 8) | code[i + 1]; var glyphIndex = (code[i + 2] << 8) | code[i + 3]; i += 4; var arg1, arg2; @@ -23753,7 +24951,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { } else { // simple glyph var endPtsOfContours = []; - for (var j = 0; j < numberOfContours; j++) { + var j, jj; + for (j = 0; j < numberOfContours; j++) { endPtsOfContours.push((code[i] << 8) | code[i + 1]); i += 2; } @@ -23762,7 +24961,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { var numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1; var points = []; while (points.length < numberOfPoints) { - var flags = code[i++], repeat = 1; + flags = code[i++]; + var repeat = 1; if ((flags & 0x08)) { repeat += code[i++]; } @@ -23770,8 +24970,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { points.push({flags: flags}); } } - var x = 0, y = 0; - for (var j = 0; j < numberOfPoints; j++) { + for (j = 0; j < numberOfPoints; j++) { switch (points[j].flags & 0x12) { case 0x00: x += ((code[i] << 24) | (code[i + 1] << 16)) >> 16; @@ -23786,7 +24985,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { } points[j].x = x; } - for (var j = 0; j < numberOfPoints; j++) { + for (j = 0; j < numberOfPoints; j++) { switch (points[j].flags & 0x24) { case 0x00: y += ((code[i] << 24) | (code[i + 1] << 16)) >> 16; @@ -23803,7 +25002,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { } var startPoint = 0; - for (var i = 0; i < numberOfContours; i++) { + for (i = 0; i < numberOfContours; i++) { var endPoint = endPtsOfContours[i]; // contours might have implicit points, which is located in the middle // between two neighboring off-curve points @@ -23824,7 +25023,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { contour.push(p); } moveTo(contour[0].x, contour[0].y); - for (var j = 1, jj = contour.length; j < jj; j++) { + for (j = 1, jj = contour.length; j < jj; j++) { if ((contour[j].flags & 1)) { lineTo(contour[j].x, contour[j].y); } else if ((contour[j + 1].flags & 1)){ @@ -23863,6 +25062,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { while (i < code.length) { var stackClean = false; var v = code[i++]; + var xa, xb, ya, yb, y1, y2, y3, n, subrCode; switch (v) { case 1: // hstem stems += stack.length >> 1; @@ -23908,15 +25108,15 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { break; case 8: // rrcurveto while (stack.length > 0) { - var xa = x + stack.shift(), ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + stack.shift(); bezierCurveTo(xa, ya, xb, yb, x, y); } break; case 10: // callsubr - var n = stack.pop() + font.subrsBias; - var subrCode = font.subrs[n]; + n = stack.pop() + font.subrsBias; + subrCode = font.subrs[n]; if (subrCode) { parse(subrCode); } @@ -23927,44 +25127,44 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { v = code[i++]; switch (v) { case 34: // flex - var xa = x + stack.shift(); - var xb = xa + stack.shift(), y1 = y + stack.shift(); + xa = x + stack.shift(); + xb = xa + stack.shift(); y1 = y + stack.shift(); x = xb + stack.shift(); bezierCurveTo(xa, y, xb, y1, x, y1); - var xa = x + stack.shift(); - var xb = xa + stack.shift(); + xa = x + stack.shift(); + xb = xa + stack.shift(); x = xb + stack.shift(); bezierCurveTo(xa, y1, xb, y, x, y); break; case 35: // flex - var xa = x + stack.shift(), ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + stack.shift(); bezierCurveTo(xa, ya, xb, yb, x, y); - var xa = x + stack.shift(), ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + stack.shift(); bezierCurveTo(xa, ya, xb, yb, x, y); stack.pop(); // fd break; case 36: // hflex1 - var xa = x + stack.shift(), y1 = y + stack.shift(); - var xb = xa + stack.shift(), y2 = y1 + stack.shift(); + xa = x + stack.shift(); y1 = y + stack.shift(); + xb = xa + stack.shift(); y2 = y1 + stack.shift(); x = xb + stack.shift(); bezierCurveTo(xa, y1, xb, y2, x, y2); - var xa = x + stack.shift(); - var xb = xa + stack.shift(), y3 = y2 + stack.shift(); + xa = x + stack.shift(); + xb = xa + stack.shift(); y3 = y2 + stack.shift(); x = xb + stack.shift(); bezierCurveTo(xa, y2, xb, y3, x, y); break; case 37: // flex1 var x0 = x, y0 = y; - var xa = x + stack.shift(), ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + stack.shift(); bezierCurveTo(xa, ya, xb, yb, x, y); - var xa = x + stack.shift(), ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb; y = yb; if (Math.abs(x - x0) > Math.abs(y - y0)) { x += stack.shift(); @@ -24026,8 +25226,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { break; case 24: // rcurveline while (stack.length > 2) { - var xa = x + stack.shift(), ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + stack.shift(); bezierCurveTo(xa, ya, xb, yb, x, y); } @@ -24041,8 +25241,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { y += stack.shift(); lineTo(x, y); } - var xa = x + stack.shift(), ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + stack.shift(); bezierCurveTo(xa, ya, xb, yb, x, y); break; @@ -24051,8 +25251,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { x += stack.shift(); } while (stack.length > 0) { - var xa = x, ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x; ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb; y = yb + stack.shift(); bezierCurveTo(xa, ya, xb, yb, x, y); } @@ -24062,8 +25262,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { y += stack.shift(); } while (stack.length > 0) { - var xa = x + stack.shift(), ya = y; - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y; + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb; bezierCurveTo(xa, ya, xb, yb, x, y); } @@ -24073,16 +25273,16 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { i += 2; break; case 29: // callgsubr - var n = stack.pop() + font.gsubrsBias; - var subrCode = font.gsubrs[n]; + n = stack.pop() + font.gsubrsBias; + subrCode = font.gsubrs[n]; if (subrCode) { parse(subrCode); } break; case 30: // vhcurveto while (stack.length > 0) { - var xa = x, ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x; ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + (stack.length === 1 ? stack.shift() : 0); bezierCurveTo(xa, ya, xb, yb, x, y); @@ -24090,8 +25290,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { break; } - var xa = x + stack.shift(), ya = y; - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y; + xb = xa + stack.shift(); yb = ya + stack.shift(); y = yb + stack.shift(); x = xb + (stack.length === 1 ? stack.shift() : 0); bezierCurveTo(xa, ya, xb, yb, x, y); @@ -24099,8 +25299,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { break; case 31: // hvcurveto while (stack.length > 0) { - var xa = x + stack.shift(), ya = y; - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x + stack.shift(); ya = y; + xb = xa + stack.shift(); yb = ya + stack.shift(); y = yb + stack.shift(); x = xb + (stack.length === 1 ? stack.shift() : 0); bezierCurveTo(xa, ya, xb, yb, x, y); @@ -24108,8 +25308,8 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { break; } - var xa = x, ya = y + stack.shift(); - var xb = xa + stack.shift(), yb = ya + stack.shift(); + xa = x; ya = y + stack.shift(); + xb = xa + stack.shift(); yb = ya + stack.shift(); x = xb + stack.shift(); y = yb + (stack.length === 1 ? stack.shift() : 0); bezierCurveTo(xa, ya, xb, yb, x, y); @@ -24228,7 +25428,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() { var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm; var numTables = getUshort(data, 4); for (var i = 0, p = 12; i < numTables; i++, p += 16) { - var tag = String.fromCharCode.apply(null, data.subarray(p, p + 4)); + var tag = bytesToString(data.subarray(p, p + 4)); var offset = getLong(data, p + 8); var length = getLong(data, p + 12); switch (tag) { @@ -28506,16 +29706,23 @@ var PDFImage = (function PDFImageClosure() { } function PDFImage(xref, res, image, inline, smask, mask, isMask) { this.image = image; - if (image.getParams) { - // JPX/JPEG2000 streams directly contain bits per component - // and color space mode information. - warn('get params from actual stream'); - // var bits = ... - // var colorspace = ... + var dict = image.dict; + if (dict.has('Filter')) { + var filter = dict.get('Filter').name; + if (filter === 'JPXDecode') { + info('get image params from JPX stream'); + var jpxImage = new JpxImage(); + jpxImage.parseImageProperties(image.stream); + image.stream.reset(); + image.bitsPerComponent = jpxImage.bitsPerComponent; + image.numComps = jpxImage.componentsCount; + } else if (filter === 'JBIG2Decode') { + image.bitsPerComponent = 1; + image.numComps = 1; + } } // TODO cache rendered images? - var dict = image.dict; this.width = dict.get('Width', 'W'); this.height = dict.get('Height', 'H'); @@ -28544,8 +29751,19 @@ var PDFImage = (function PDFImageClosure() { if (!this.imageMask) { var colorSpace = dict.get('ColorSpace', 'CS'); if (!colorSpace) { - warn('JPX images (which do not require color spaces)'); - colorSpace = Name.get('DeviceRGB'); + info('JPX images (which do not require color spaces)'); + switch (image.numComps) { + case 1: + colorSpace = Name.get('DeviceGray'); + break; + case 3: + colorSpace = Name.get('DeviceRGB'); + break; + default: + // TODO: Find out how four color channels are handled. CMYK? Alpha? + error('JPX images with ' + this.numComps + + ' color components not supported.'); + } } this.colorSpace = ColorSpace.parse(colorSpace, xref, res); this.numComps = this.colorSpace.numComps; @@ -28705,20 +29923,20 @@ var PDFImage = (function PDFImageClosure() { var decodeMap = this.decode; var numComps = this.numComps; - var decodeAddends, decodeCoefficients; var decodeAddends = this.decodeAddends; var decodeCoefficients = this.decodeCoefficients; var max = (1 << bpc) - 1; + var i, ii; if (bpc === 1) { // If the buffer needed decode that means it just needs to be inverted. - for (var i = 0, ii = buffer.length; i < ii; i++) { + for (i = 0, ii = buffer.length; i < ii; i++) { buffer[i] = +!(buffer[i]); } return; } var index = 0; - for (var i = 0, ii = this.width * this.height; i < ii; i++) { + for (i = 0, ii = this.width * this.height; i < ii; i++) { for (var j = 0; j < numComps; j++) { buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j], decodeCoefficients[j], max); @@ -28745,10 +29963,11 @@ var PDFImage = (function PDFImageClosure() { var rowComps = width * numComps; var max = (1 << bpc) - 1; + var i = 0, ii, buf; if (bpc === 1) { // Optimization for reading 1 bpc images. - var i = 0, buf, mask, loop1End, loop2End; + var mask, loop1End, loop2End; for (var j = 0; j < height; j++) { loop1End = i + (rowComps & ~7); loop2End = i + rowComps; @@ -28779,8 +29998,9 @@ var PDFImage = (function PDFImageClosure() { } } else { // The general case that handles all other bpc values. - var bits = 0, buf = 0; - for (var i = 0, ii = length; i < ii; ++i) { + var bits = 0; + buf = 0; + for (i = 0, ii = length; i < ii; ++i) { if (i % rowComps === 0) { buf = 0; bits = 0; @@ -28804,11 +30024,11 @@ var PDFImage = (function PDFImageClosure() { actualHeight, image) { var smask = this.smask; var mask = this.mask; - var alphaBuf; + var alphaBuf, sw, sh, i, ii, j; if (smask) { - var sw = smask.width; - var sh = smask.height; + sw = smask.width; + sh = smask.height; alphaBuf = new Uint8Array(sw * sh); smask.fillGrayBuffer(alphaBuf); if (sw != width || sh != height) { @@ -28817,14 +30037,14 @@ var PDFImage = (function PDFImageClosure() { } } else if (mask) { if (mask instanceof PDFImage) { - var sw = mask.width; - var sh = mask.height; + sw = mask.width; + sh = mask.height; alphaBuf = new Uint8Array(sw * sh); mask.numComps = 1; mask.fillGrayBuffer(alphaBuf); // Need to invert values in rgbaBuf - for (var i = 0, ii = sw * sh; i < ii; ++i) { + for (i = 0, ii = sw * sh; i < ii; ++i) { alphaBuf[i] = 255 - alphaBuf[i]; } @@ -28837,10 +30057,10 @@ var PDFImage = (function PDFImageClosure() { // then they should be painted. alphaBuf = new Uint8Array(width * height); var numComps = this.numComps; - for (var i = 0, ii = width * height; i < ii; ++i) { + for (i = 0, ii = width * height; i < ii; ++i) { var opacity = 0; var imageOffset = i * numComps; - for (var j = 0; j < numComps; ++j) { + for (j = 0; j < numComps; ++j) { var color = image[imageOffset + j]; var maskOffset = j * 2; if (color < mask[maskOffset] || color > mask[maskOffset + 1]) { @@ -28856,12 +30076,12 @@ var PDFImage = (function PDFImageClosure() { } if (alphaBuf) { - for (var i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { + for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { rgbaBuf[j] = alphaBuf[i]; } } else { // No mask. - for (var i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { + for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) { rgbaBuf[j] = 255; } } @@ -28997,18 +30217,19 @@ var PDFImage = (function PDFImageClosure() { var imgArray = this.getImageBytes(height * rowBytes); var comps = this.getComponents(imgArray); + var i, length; if (bpc === 1) { // inline decoding (= inversion) for 1 bpc images - var length = width * height; + length = width * height; if (this.needsDecode) { // invert and scale to {0, 255} - for (var i = 0; i < length; ++i) { + for (i = 0; i < length; ++i) { buffer[i] = (comps[i] - 1) & 255; } } else { // scale to {0, 255} - for (var i = 0; i < length; ++i) { + for (i = 0; i < length; ++i) { buffer[i] = (-comps[i]) & 255; } } @@ -29018,10 +30239,10 @@ var PDFImage = (function PDFImageClosure() { if (this.needsDecode) { this.decodeBuffer(comps); } - var length = width * height; + length = width * height; // we aren't using a colorspace so we need to scale the value var scale = 255 / ((1 << bpc) - 1); - for (var i = 0; i < length; ++i) { + for (i = 0; i < length; ++i) { buffer[i] = (scale * comps[i]) | 0; } }, @@ -32088,7 +33309,7 @@ var Parser = (function ParserClosure() { var stream = lexer.stream; // parse dictionary - var dict = new Dict(); + var dict = new Dict(null); while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) { if (!isName(this.buf1)) { error('Dictionary key must be a name object'); @@ -32149,7 +33370,7 @@ var Parser = (function ParserClosure() { var a = 1; var b = 0; - for (var i = 0, ii = imageBytes.length; i < ii; ++i) { + for (i = 0, ii = imageBytes.length; i < ii; ++i) { a = (a + (imageBytes[i] & 0xff)) % 65521; b = (b + a) % 65521; } @@ -32218,11 +33439,11 @@ var Parser = (function ParserClosure() { var ENDSTREAM_SIGNATURE_LENGTH = 9; var ENDSTREAM_SIGNATURE = [0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6D]; - var skipped = 0, found = false; + var skipped = 0, found = false, i, j; while (stream.pos < stream.end) { var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE); var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH; - var found = false, i, j; + found = false; for (i = 0, j = 0; i < scanLength; i++) { var b = scanBytes[i]; if (b !== ENDSTREAM_SIGNATURE[j]) { @@ -33456,7 +34677,8 @@ var FlateStream = (function FlateStreamClosure() { // find max code length var maxLen = 0; - for (var i = 0; i < n; ++i) { + var i; + for (i = 0; i < n; ++i) { if (lengths[i] > maxLen) { maxLen = lengths[i]; } @@ -33473,13 +34695,13 @@ var FlateStream = (function FlateStreamClosure() { // bit-reverse the code var code2 = 0; var t = code; - for (var i = 0; i < len; ++i) { + for (i = 0; i < len; ++i) { code2 = (code2 << 1) | (t & 1); t >>= 1; } // fill the table entries - for (var i = code2; i < size; i += skip) { + for (i = code2; i < size; i += skip) { codes[i] = (len << 16) | val; } ++code; @@ -33491,6 +34713,7 @@ var FlateStream = (function FlateStreamClosure() { }; FlateStream.prototype.readBlock = function FlateStream_readBlock() { + var buffer, len; var str = this.str; // read block header var hdr = this.getBits(3); @@ -33528,7 +34751,7 @@ var FlateStream = (function FlateStreamClosure() { this.codeSize = 0; var bufferLength = this.bufferLength; - var buffer = this.ensureBuffer(bufferLength + blockLen); + buffer = this.ensureBuffer(bufferLength + blockLen); var end = bufferLength + blockLen; this.bufferLength = end; if (blockLen === 0) { @@ -33560,24 +34783,26 @@ var FlateStream = (function FlateStreamClosure() { // build the code lengths code table var codeLenCodeLengths = new Uint8Array(codeLenCodeMap.length); - for (var i = 0; i < numCodeLenCodes; ++i) { + var i; + for (i = 0; i < numCodeLenCodes; ++i) { codeLenCodeLengths[codeLenCodeMap[i]] = this.getBits(3); } var codeLenCodeTab = this.generateHuffmanTable(codeLenCodeLengths); // build the literal and distance code tables - var len = 0; - var i = 0; + len = 0; + i = 0; var codes = numLitCodes + numDistCodes; var codeLengths = new Uint8Array(codes); + var bitsLength, bitsOffset, what; while (i < codes) { var code = this.getCode(codeLenCodeTab); if (code == 16) { - var bitsLength = 2, bitsOffset = 3, what = len; + bitsLength = 2; bitsOffset = 3; what = len; } else if (code == 17) { - var bitsLength = 3, bitsOffset = 3, what = (len = 0); + bitsLength = 3; bitsOffset = 3; what = (len = 0); } else if (code == 18) { - var bitsLength = 7, bitsOffset = 11, what = (len = 0); + bitsLength = 7; bitsOffset = 11; what = (len = 0); } else { codeLengths[i++] = len = code; continue; @@ -33597,7 +34822,7 @@ var FlateStream = (function FlateStreamClosure() { error('Unknown block type in flate stream'); } - var buffer = this.buffer; + buffer = this.buffer; var limit = buffer ? buffer.length : 0; var pos = this.bufferLength; while (true) { @@ -33620,7 +34845,7 @@ var FlateStream = (function FlateStreamClosure() { if (code2 > 0) { code2 = this.getBits(code2); } - var len = (code1 & 0xffff) + code2; + len = (code1 & 0xffff) + code2; code1 = this.getCode(distCodeTable); code1 = distDecode[code1]; code2 = code1 >> 16; @@ -33693,9 +34918,10 @@ var PredictorStream = (function PredictorStreamClosure() { var inbuf = 0, outbuf = 0; var inbits = 0, outbits = 0; var pos = bufferLength; + var i; if (bits === 1) { - for (var i = 0; i < rowBytes; ++i) { + for (i = 0; i < rowBytes; ++i) { var c = rawBytes[i]; inbuf = (inbuf << 8) | c; // bitwise addition is exclusive or @@ -33705,7 +34931,7 @@ var PredictorStream = (function PredictorStreamClosure() { inbuf &= 0xFFFF; } } else if (bits === 8) { - for (var i = 0; i < colors; ++i) { + for (i = 0; i < colors; ++i) { buffer[pos++] = rawBytes[i]; } for (; i < rowBytes; ++i) { @@ -33717,7 +34943,7 @@ var PredictorStream = (function PredictorStreamClosure() { var bitMask = (1 << bits) - 1; var j = 0, k = bufferLength; var columns = this.columns; - for (var i = 0; i < columns; ++i) { + for (i = 0; i < columns; ++i) { for (var kk = 0; kk < colors; ++kk) { if (inbits < bits) { inbuf = (inbuf << 8) | (rawBytes[j++] & 0xFF); @@ -33763,15 +34989,15 @@ var PredictorStream = (function PredictorStreamClosure() { prevRow = new Uint8Array(rowBytes); } - var j = bufferLength; + var i, j = bufferLength, up, c; switch (predictor) { case 0: - for (var i = 0; i < rowBytes; ++i) { + for (i = 0; i < rowBytes; ++i) { buffer[j++] = rawBytes[i]; } break; case 1: - for (var i = 0; i < pixBytes; ++i) { + for (i = 0; i < pixBytes; ++i) { buffer[j++] = rawBytes[i]; } for (; i < rowBytes; ++i) { @@ -33780,12 +35006,12 @@ var PredictorStream = (function PredictorStreamClosure() { } break; case 2: - for (var i = 0; i < rowBytes; ++i) { + for (i = 0; i < rowBytes; ++i) { buffer[j++] = (prevRow[i] + rawBytes[i]) & 0xFF; } break; case 3: - for (var i = 0; i < pixBytes; ++i) { + for (i = 0; i < pixBytes; ++i) { buffer[j++] = (prevRow[i] >> 1) + rawBytes[i]; } for (; i < rowBytes; ++i) { @@ -33797,13 +35023,13 @@ var PredictorStream = (function PredictorStreamClosure() { case 4: // we need to save the up left pixels values. the simplest way // is to create a new buffer - for (var i = 0; i < pixBytes; ++i) { - var up = prevRow[i]; - var c = rawBytes[i]; + for (i = 0; i < pixBytes; ++i) { + up = prevRow[i]; + c = rawBytes[i]; buffer[j++] = up + c; } for (; i < rowBytes; ++i) { - var up = prevRow[i]; + up = prevRow[i]; var upLeft = prevRow[i - pixBytes]; var left = buffer[j - pixBytes]; var p = left + up - upLeft; @@ -33821,7 +35047,7 @@ var PredictorStream = (function PredictorStreamClosure() { pc = -pc; } - var c = rawBytes[i]; + c = rawBytes[i]; if (pa <= pb && pa <= pc) { buffer[j++] = left + c; } else if (pb <= pc) { @@ -33961,6 +35187,7 @@ var JpxStream = (function JpxStreamClosure() { var tileTop = tileCompoments[0].top; var dataPosition, sourcePosition, data0, data1, data2, data3, rowFeed; + var i, j; switch (componentsCount) { case 1: data0 = tileCompoments[0].items; @@ -33968,8 +35195,8 @@ var JpxStream = (function JpxStreamClosure() { dataPosition = width * tileTop + tileLeft; rowFeed = width - tileWidth; sourcePosition = 0; - for (var j = 0; j < tileHeight; j++) { - for (var i = 0; i < tileWidth; i++) { + for (j = 0; j < tileHeight; j++) { + for (i = 0; i < tileWidth; i++) { data[dataPosition++] = data0[sourcePosition++]; } dataPosition += rowFeed; @@ -33983,8 +35210,8 @@ var JpxStream = (function JpxStreamClosure() { dataPosition = (width * tileTop + tileLeft) * 3; rowFeed = (width - tileWidth) * 3; sourcePosition = 0; - for (var j = 0; j < tileHeight; j++) { - for (var i = 0; i < tileWidth; i++) { + for (j = 0; j < tileHeight; j++) { + for (i = 0; i < tileWidth; i++) { data[dataPosition++] = data0[sourcePosition]; data[dataPosition++] = data1[sourcePosition]; data[dataPosition++] = data2[sourcePosition]; @@ -34002,8 +35229,8 @@ var JpxStream = (function JpxStreamClosure() { dataPosition = (width * tileTop + tileLeft) * 4; rowFeed = (width - tileWidth) * 4; sourcePosition = 0; - for (var j = 0; j < tileHeight; j++) { - for (var i = 0; i < tileWidth; i++) { + for (j = 0; j < tileHeight; j++) { + for (i = 0; i < tileWidth; i++) { data[dataPosition++] = data0[sourcePosition]; data[dataPosition++] = data1[sourcePosition]; data[dataPosition++] = data2[sourcePosition]; @@ -34166,18 +35393,19 @@ var Ascii85Stream = (function Ascii85StreamClosure() { } var bufferLength = this.bufferLength, buffer; + var i; // special code for z if (c == Z_LOWER_CHAR) { buffer = this.ensureBuffer(bufferLength + 4); - for (var i = 0; i < 4; ++i) { + for (i = 0; i < 4; ++i) { buffer[bufferLength + i] = 0; } this.bufferLength += 4; } else { var input = this.input; input[0] = c; - for (var i = 1; i < 5; ++i) { + for (i = 1; i < 5; ++i) { c = str.getByte(); while (Lexer.isSpace(c)) { c = str.getByte(); @@ -34200,11 +35428,11 @@ var Ascii85Stream = (function Ascii85StreamClosure() { this.eof = true; } var t = 0; - for (var i = 0; i < 5; ++i) { + for (i = 0; i < 5; ++i) { t = t * 85 + (input[i] - 0x21); } - for (var i = 3; i >= 0; --i) { + for (i = 3; i >= 0; --i) { buffer[bufferLength + i] = t & 0xFF; t >>= 8; } @@ -34297,11 +35525,12 @@ var RunLengthStream = (function RunLengthStreamClosure() { return; } + var buffer; var bufferLength = this.bufferLength; var n = repeatHeader[0]; if (n < 128) { // copy n bytes - var buffer = this.ensureBuffer(bufferLength + n + 1); + buffer = this.ensureBuffer(bufferLength + n + 1); buffer[bufferLength++] = repeatHeader[1]; if (n > 0) { var source = this.str.getBytes(n); @@ -34311,7 +35540,7 @@ var RunLengthStream = (function RunLengthStreamClosure() { } else { n = 257 - n; var b = repeatHeader[1]; - var buffer = this.ensureBuffer(bufferLength + n + 1); + buffer = this.ensureBuffer(bufferLength + n + 1); for (var i = 0; i < n; i++) { buffer[bufferLength++] = b; } @@ -34754,7 +35983,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { this.str = str; this.dict = str.dict; - params = params || new Dict(); + params = params || Dict.empty; this.encoding = params.get('K') || 0; this.eoline = params.get('EndOfLine') || false; @@ -34861,7 +36090,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { var codingLine = this.codingLine; var columns = this.columns; - var refPos, blackPixels, bits; + var refPos, blackPixels, bits, i; if (this.outputBits === 0) { if (this.eof) { @@ -34871,7 +36100,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { var code1, code2, code3; if (this.nextLine2D) { - for (var i = 0; codingLine[i] < columns; ++i) { + for (i = 0; codingLine[i] < columns; ++i) { refLine[i] = codingLine[i]; } refLine[i++] = columns; @@ -35073,7 +36302,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { this.eatBits(1); } if (this.encoding >= 0) { - for (var i = 0; i < 4; ++i) { + for (i = 0; i < 4; ++i) { code1 = this.lookBits(12); if (code1 != 1) { info('bad rtc code: ' + code1); @@ -35124,7 +36353,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() { codingLine[this.codingPos - 1]); } } else { - var bits = 8; + bits = 8; c = 0; do { if (this.outputBits > bits) { @@ -36065,10 +37294,6 @@ var JpxImage = (function JpxImageClosure() { 'HL': 1, 'HH': 2 }; - var TransformType = { - IRREVERSIBLE: 0, - REVERSIBLE: 1 - }; function JpxImage() { this.failOnCorruptedImage = false; } @@ -36141,6 +37366,39 @@ var JpxImage = (function JpxImageClosure() { } } }, + parseImageProperties: function JpxImage_parseImageProperties(stream) { + try { + var newByte = stream.getByte(); + while (newByte >= 0) { + var oldByte = newByte; + newByte = stream.getByte(); + var code = (oldByte << 8) | newByte; + // Image and tile size (SIZ) + if (code == 0xFF51) { + stream.skip(4); + var Xsiz = stream.getUint32(); // Byte 4 + var Ysiz = stream.getUint32(); // Byte 8 + var XOsiz = stream.getUint32(); // Byte 12 + var YOsiz = stream.getUint32(); // Byte 16 + stream.skip(16); + var Csiz = stream.getUint16(); // Byte 36 + this.width = Xsiz - XOsiz; + this.height = Ysiz - YOsiz; + this.componentsCount = Csiz; + // Results are always returned as UInt8Arrays + this.bitsPerComponent = 8; + return; + } + } + throw 'No size marker found in JPX stream'; + } catch (e) { + if (this.failOnCorruptedImage) { + error('JPX error: ' + e); + } else { + warn('JPX error: ' + e + '. Trying to recover'); + } + } + }, parseCodestream: function JpxImage_parseCodestream(data, start, end) { var context = {}; try { @@ -36149,7 +37407,7 @@ var JpxImage = (function JpxImageClosure() { var code = readUint16(data, position); position += 2; - var length = 0, j; + var length = 0, j, sqcd, spqcds, spqcdSize, scalarExpounded, tile; switch (code) { case 0xFF4F: // Start of codestream (SOC) context.mainHeader = true; @@ -36191,8 +37449,7 @@ var JpxImage = (function JpxImageClosure() { length = readUint16(data, position); var qcd = {}; j = position + 2; - var sqcd = data[j++]; - var spqcdSize, scalarExpounded; + sqcd = data[j++]; switch (sqcd & 0x1F) { case 0: spqcdSize = 8; @@ -36212,7 +37469,7 @@ var JpxImage = (function JpxImageClosure() { qcd.noQuantization = (spqcdSize == 8); qcd.scalarExpounded = scalarExpounded; qcd.guardBits = sqcd >> 5; - var spqcds = []; + spqcds = []; while (j < length + position) { var spqcd = {}; if (spqcdSize == 8) { @@ -36244,8 +37501,7 @@ var JpxImage = (function JpxImageClosure() { cqcc = readUint16(data, j); j += 2; } - var sqcd = data[j++]; - var spqcdSize, scalarExpounded; + sqcd = data[j++]; switch (sqcd & 0x1F) { case 0: spqcdSize = 8; @@ -36265,9 +37521,9 @@ var JpxImage = (function JpxImageClosure() { qcc.noQuantization = (spqcdSize == 8); qcc.scalarExpounded = scalarExpounded; qcc.guardBits = sqcd >> 5; - var spqcds = []; + spqcds = []; while (j < (length + position)) { - var spqcd = {}; + spqcd = {}; if (spqcdSize == 8) { spqcd.epsilon = data[j++] >> 3; spqcd.mu = 0; @@ -36309,7 +37565,7 @@ var JpxImage = (function JpxImageClosure() { cod.verticalyStripe = !!(blockStyle & 8); cod.predictableTermination = !!(blockStyle & 16); cod.segmentationSymbolUsed = !!(blockStyle & 32); - cod.transformation = data[j++]; + cod.reversibleTransformation = data[j++]; if (cod.entropyCoderWithCustomPrecincts) { var precinctsSizes = []; while (j < length + position) { @@ -36340,7 +37596,7 @@ var JpxImage = (function JpxImageClosure() { break; case 0xFF90: // Start of tile-part (SOT) length = readUint16(data, position); - var tile = {}; + tile = {}; tile.index = readUint16(data, position + 2); tile.length = readUint32(data, position + 4); tile.dataEnd = tile.length + position - 2; @@ -36358,7 +37614,7 @@ var JpxImage = (function JpxImageClosure() { context.currentTile = tile; break; case 0xFF93: // Start of data (SOD) - var tile = context.currentTile; + tile = context.currentTile; if (tile.partIndex === 0) { initializeTile(context, tile.index); buildPackets(context); @@ -36372,6 +37628,8 @@ var JpxImage = (function JpxImageClosure() { length = readUint16(data, position); // skipping content break; + case 0xFF53: // Coding style component (COC) + throw 'Codestream code 0xFF53 (COC) is not implemented'; default: throw 'Unknown codestream code: ' + code.toString(16); } @@ -36417,12 +37675,12 @@ var JpxImage = (function JpxImageClosure() { function calculateTileGrids(context, components) { var siz = context.SIZ; // Section B.3 Division into tile and tile-components - var tiles = []; + var tile, tiles = []; var numXtiles = Math.ceil((siz.Xsiz - siz.XTOsiz) / siz.XTsiz); var numYtiles = Math.ceil((siz.Ysiz - siz.YTOsiz) / siz.YTsiz); for (var q = 0; q < numYtiles; q++) { for (var p = 0; p < numXtiles; p++) { - var tile = {}; + tile = {}; tile.tx0 = Math.max(siz.XTOsiz + p * siz.XTsiz, siz.XOsiz); tile.ty0 = Math.max(siz.YTOsiz + q * siz.YTsiz, siz.YOsiz); tile.tx1 = Math.min(siz.XTOsiz + (p + 1) * siz.XTsiz, siz.Xsiz); @@ -36440,7 +37698,8 @@ var JpxImage = (function JpxImageClosure() { var component = components[i]; var tileComponents = []; for (var j = 0, jj = tiles.length; j < jj; j++) { - var tileComponent = {}, tile = tiles[j]; + var tileComponent = {}; + tile = tiles[j]; tileComponent.tcx0 = Math.ceil(tile.tx0 / component.XRsiz); tileComponent.tcy0 = Math.ceil(tile.ty0 / component.YRsiz); tileComponent.tcx1 = Math.ceil(tile.tx1 / component.XRsiz); @@ -36499,16 +37758,17 @@ var JpxImage = (function JpxImageClosure() { var ycb_ = dimensions.ycb_; var codeblockWidth = 1 << xcb_; var codeblockHeight = 1 << ycb_; - var cbx0 = Math.floor(subband.tbx0 / codeblockWidth); - var cby0 = Math.floor(subband.tby0 / codeblockHeight); - var cbx1 = Math.ceil(subband.tbx1 / codeblockWidth); - var cby1 = Math.ceil(subband.tby1 / codeblockHeight); + var cbx0 = subband.tbx0 >> xcb_; + var cby0 = subband.tby0 >> ycb_; + var cbx1 = (subband.tbx1 + codeblockWidth - 1) >> xcb_; + var cby1 = (subband.tby1 + codeblockHeight - 1) >> ycb_; var precinctParameters = subband.resolution.precinctParameters; var codeblocks = []; var precincts = []; - for (var j = cby0; j < cby1; j++) { - for (var i = cbx0; i < cbx1; i++) { - var codeblock = { + var i, ii, j, codeblock, precinctNumber; + for (j = cby0; j < cby1; j++) { + for (i = cbx0; i < cbx1; i++) { + codeblock = { cbx: i, cby: j, tbx0: codeblockWidth * i, @@ -36523,8 +37783,7 @@ var JpxImage = (function JpxImageClosure() { var pj = Math.floor((codeblock.tby0 - precinctParameters.precinctYOffset) / precinctParameters.precinctHeight); - var precinctNumber = pj + - pi * precinctParameters.numprecinctswide; + precinctNumber = pj + pi * precinctParameters.numprecinctswide; codeblock.tbx0_ = Math.max(subband.tbx0, codeblock.tbx0); codeblock.tby0_ = Math.max(subband.tby0, codeblock.tby0); codeblock.tbx1_ = Math.min(subband.tbx1, codeblock.tbx1); @@ -36536,13 +37795,18 @@ var JpxImage = (function JpxImageClosure() { codeblock.Lblock = 3; codeblocks.push(codeblock); // building precinct for the sub-band - var precinct; - if (precinctNumber in precincts) { - precinct = precincts[precinctNumber]; - precinct.cbxMin = Math.min(precinct.cbxMin, i); - precinct.cbyMin = Math.min(precinct.cbyMin, j); - precinct.cbxMax = Math.max(precinct.cbxMax, i); - precinct.cbyMax = Math.max(precinct.cbyMax, j); + var precinct = precincts[precinctNumber]; + if (precinct !== undefined) { + if (i < precinct.cbxMin) { + precinct.cbxMin = i; + } else if (i > precinct.cbxMax) { + precinct.cbxMax = i; + } + if (j < precinct.cbyMin) { + precinct.cbxMin = j; + } else if (j > precinct.cbyMax) { + precinct.cbyMax = j; + } } else { precincts[precinctNumber] = precinct = { cbxMin: i, @@ -36561,10 +37825,6 @@ var JpxImage = (function JpxImageClosure() { numcodeblockhigh: cby1 - cby1 + 1 }; subband.codeblocks = codeblocks; - for (var i = 0, ii = codeblocks.length; i < ii; i++) { - var codeblock = codeblocks[i]; - var precinctNumber = codeblock.precinctNumber; - } subband.precincts = precincts; } function createPacket(resolution, precinctNumber, layerNumber) { @@ -36823,19 +38083,20 @@ var JpxImage = (function JpxImageClosure() { continue; } var layerNumber = packet.layerNumber; - var queue = []; + var queue = [], codeblock; for (var i = 0, ii = packet.codeblocks.length; i < ii; i++) { - var codeblock = packet.codeblocks[i]; + codeblock = packet.codeblocks[i]; var precinct = codeblock.precinct; var codeblockColumn = codeblock.cbx - precinct.cbxMin; var codeblockRow = codeblock.cby - precinct.cbyMin; var codeblockIncluded = false; var firstTimeInclusion = false; + var valueReady; if ('included' in codeblock) { codeblockIncluded = !!readBits(1); } else { // reading inclusion tree - var precinct = codeblock.precinct; + precinct = codeblock.precinct; var inclusionTree, zeroBitPlanesTree; if ('inclusionTree' in precinct) { inclusionTree = precinct.inclusionTree; @@ -36852,7 +38113,7 @@ var JpxImage = (function JpxImageClosure() { if (inclusionTree.reset(codeblockColumn, codeblockRow, layerNumber)) { while (true) { if (readBits(1)) { - var valueReady = !inclusionTree.nextLevel(); + valueReady = !inclusionTree.nextLevel(); if (valueReady) { codeblock.included = true; codeblockIncluded = firstTimeInclusion = true; @@ -36873,7 +38134,7 @@ var JpxImage = (function JpxImageClosure() { zeroBitPlanesTree.reset(codeblockColumn, codeblockRow); while (true) { if (readBits(1)) { - var valueReady = !zeroBitPlanesTree.nextLevel(); + valueReady = !zeroBitPlanesTree.nextLevel(); if (valueReady) { break; } @@ -36901,7 +38162,7 @@ var JpxImage = (function JpxImageClosure() { alignToByte(); while (queue.length > 0) { var packetItem = queue.shift(); - var codeblock = packetItem.codeblock; + codeblock = packetItem.codeblock; if (!('data' in codeblock)) { codeblock.data = []; } @@ -36917,7 +38178,7 @@ var JpxImage = (function JpxImageClosure() { return position; } function copyCoefficients(coefficients, x0, y0, width, height, - delta, mb, codeblocks, transformation, + delta, mb, codeblocks, reversible, segmentationSymbolUsed) { for (var i = 0, ii = codeblocks.length; i < ii; ++i) { var codeblock = codeblocks[i]; @@ -36937,14 +38198,15 @@ var JpxImage = (function JpxImageClosure() { // collect data var data = codeblock.data, totalLength = 0, codingpasses = 0; - for (var q = 0, qq = data.length; q < qq; q++) { - var dataItem = data[q]; + var q, qq, dataItem; + for (q = 0, qq = data.length; q < qq; q++) { + dataItem = data[q]; totalLength += dataItem.end - dataItem.start; codingpasses += dataItem.codingpasses; } var encodedData = new Uint8Array(totalLength), k = 0; - for (var q = 0, qq = data.length; q < qq; q++) { - var dataItem = data[q]; + for (q = 0, qq = data.length; q < qq; q++) { + dataItem = data[q]; var chunk = dataItem.data.subarray(dataItem.start, dataItem.end); encodedData.set(chunk, k); k += chunk.length; @@ -36953,7 +38215,7 @@ var JpxImage = (function JpxImageClosure() { var decoder = new ArithmeticDecoder(encodedData, 0, totalLength); bitModel.setDecoder(decoder); - for (var q = 0; q < codingpasses; q++) { + for (q = 0; q < codingpasses; q++) { switch (currentCodingpassType) { case 0: bitModel.runSignificancePropogationPass(); @@ -36973,16 +38235,27 @@ var JpxImage = (function JpxImageClosure() { var offset = (codeblock.tbx0_ - x0) + (codeblock.tby0_ - y0) * width; var n, nb, correction, position = 0; - var irreversible = (transformation === TransformType.IRREVERSIBLE); + var irreversible = !reversible; var sign = bitModel.coefficentsSign; var magnitude = bitModel.coefficentsMagnitude; var bitsDecoded = bitModel.bitsDecoded; + var magnitudeCorrection = reversible ? 0 : 0.5; for (var j = 0; j < blockHeight; j++) { - for (var k = 0; k < blockWidth; k++) { - n = (sign[position] ? -1 : 1) * magnitude[position]; - nb = bitsDecoded[position]; - correction = (irreversible || mb > nb) ? 1 << (mb - nb) : 1; - coefficients[offset++] = n * correction * delta; + for (k = 0; k < blockWidth; k++) { + n = magnitude[position]; + if (n !== 0) { + n = (n + magnitudeCorrection) * delta; + if (sign[position] !== 0) { + n = -n; + } + nb = bitsDecoded[position]; + if (irreversible || mb > nb) { + coefficients[offset] = n * (1 << (mb - nb)); + } else { + coefficients[offset] = n; + } + } + offset++; position++; } offset += width - blockWidth; @@ -36998,16 +38271,15 @@ var JpxImage = (function JpxImageClosure() { var spqcds = quantizationParameters.SPqcds; var scalarExpounded = quantizationParameters.scalarExpounded; var guardBits = quantizationParameters.guardBits; - var transformation = codingStyleParameters.transformation; var segmentationSymbolUsed = codingStyleParameters.segmentationSymbolUsed; var precision = context.components[c].precision; - var transformation = codingStyleParameters.transformation; - var transform = (transformation === TransformType.IRREVERSIBLE ? - new IrreversibleTransform() : new ReversibleTransform()); + var reversible = codingStyleParameters.reversibleTransformation; + var transform = (reversible ? new ReversibleTransform() : + new IrreversibleTransform()); var subbandCoefficients = []; - var k = 0, b = 0; + var b = 0; for (var i = 0; i <= decompositionLevelsCount; i++) { var resolution = component.resolutions[i]; @@ -37028,13 +38300,13 @@ var JpxImage = (function JpxImageClosure() { var gainLog2 = SubbandsGainLog2[subband.type]; // calulate quantization coefficient (Section E.1.1.1) - var delta = (transformation === TransformType.IRREVERSIBLE ? - Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048) : 1); + var delta = (reversible ? 1 : + Math.pow(2, precision + gainLog2 - epsilon) * (1 + mu / 2048)); var mb = (guardBits + epsilon - 1); var coefficients = new Float32Array(width * height); copyCoefficients(coefficients, subband.tbx0, subband.tby0, - width, height, delta, mb, subband.codeblocks, transformation, + width, height, delta, mb, subband.codeblocks, reversible, segmentationSymbolUsed); subbandCoefficients.push({ @@ -37065,33 +38337,35 @@ var JpxImage = (function JpxImageClosure() { for (var i = 0, ii = context.tiles.length; i < ii; i++) { var tile = context.tiles[i]; var result = []; - for (var c = 0; c < componentsCount; c++) { + var c; + for (c = 0; c < componentsCount; c++) { var image = transformTile(context, tile, c); result.push(image); } // Section G.2.2 Inverse multi component transform + var y0items, y1items, y2items, j, jj, y0, y1, y2; + var component, offset, tileImage, items; if (tile.codingStyleDefaultParameters.multipleComponentTransform) { var component0 = tile.components[0]; - var transformation = component0.codingStyleParameters.transformation; - if (transformation === TransformType.IRREVERSIBLE) { + if (!component0.codingStyleParameters.reversibleTransformation) { // inverse irreversible multiple component transform - var y0items = result[0].items; - var y1items = result[1].items; - var y2items = result[2].items; - for (var j = 0, jj = y0items.length; j < jj; ++j) { - var y0 = y0items[j], y1 = y1items[j], y2 = y2items[j]; - y0items[j] = y0 + 1.402 * y2 + 0.5; - y1items[j] = y0 - 0.34413 * y1 - 0.71414 * y2 + 0.5; - y2items[j] = y0 + 1.772 * y1 + 0.5; + y0items = result[0].items; + y1items = result[1].items; + y2items = result[2].items; + for (j = 0, jj = y0items.length; j < jj; ++j) { + y0 = y0items[j] + 0.5; y1 = y1items[j]; y2 = y2items[j]; + y0items[j] = y0 + 1.402 * y2; + y1items[j] = y0 - 0.34413 * y1 - 0.71414 * y2; + y2items[j] = y0 + 1.772 * y1; } } else { // inverse reversible multiple component transform - var y0items = result[0].items; - var y1items = result[1].items; - var y2items = result[2].items; - for (var j = 0, jj = y0items.length; j < jj; ++j) { - var y0 = y0items[j], y1 = y1items[j], y2 = y2items[j]; + y0items = result[0].items; + y1items = result[1].items; + y2items = result[2].items; + for (j = 0, jj = y0items.length; j < jj; ++j) { + y0 = y0items[j]; y1 = y1items[j]; y2 = y2items[j]; var i1 = y0 - ((y2 + y1) >> 2); y1items[j] = i1; y0items[j] = y2 + i1; @@ -37100,32 +38374,18 @@ var JpxImage = (function JpxImageClosure() { } } - // Section G.1 DC level shifting to unsigned component values - for (var c = 0; c < componentsCount; c++) { - var component = components[c]; - if (component.isSigned) { - continue; - } - - var offset = 1 << (component.precision - 1); - var tileImage = result[c]; - var items = tileImage.items; - for (var j = 0, jj = items.length; j < jj; j++) { - items[j] += offset; - } - } - // To simplify things: shift and clamp output to 8 bit unsigned - for (var c = 0; c < componentsCount; c++) { - var component = components[c]; - var offset = component.isSigned ? 128 : 0; + for (c = 0; c < componentsCount; c++) { + component = components[c]; var shift = component.precision - 8; - var tileImage = result[c]; - var items = tileImage.items; + tileImage = result[c]; + items = tileImage.items; var data = new Uint8Array(items.length); - for (var j = 0, jj = items.length; j < jj; j++) { - var value = (items[j] >> shift) + offset; - data[j] = value < 0 ? 0 : value > 255 ? 255 : value; + var low = -(128 << shift); + var high = 127 << shift; + for (j = 0, jj = items.length; j < jj; j++) { + var val = items[j]; + data[j] = val <= low ? 0 : val >= high ? 255 : (val >> shift) + 128; } result[c].items = data; } @@ -37169,9 +38429,9 @@ var JpxImage = (function JpxImageClosure() { } TagTree.prototype = { reset: function TagTree_reset(i, j) { - var currentLevel = 0, value = 0; + var currentLevel = 0, value = 0, level; while (currentLevel < this.levels.length) { - var level = this.levels[currentLevel]; + level = this.levels[currentLevel]; var index = i + j * level.width; if (index in level.items) { value = level.items[index]; @@ -37183,7 +38443,7 @@ var JpxImage = (function JpxImageClosure() { currentLevel++; } currentLevel--; - var level = this.levels[currentLevel]; + level = this.levels[currentLevel]; level.items[level.index] = value; this.currentLevel = currentLevel; delete this.value; @@ -37203,7 +38463,7 @@ var JpxImage = (function JpxImageClosure() { } this.currentLevel = currentLevel; - var level = this.levels[currentLevel]; + level = this.levels[currentLevel]; level.items[level.index] = value; return true; } @@ -37269,7 +38529,7 @@ var JpxImage = (function JpxImageClosure() { var level = this.levels[levelIndex]; var currentValue = level.items[level.index]; while (--levelIndex >= 0) { - var level = this.levels[levelIndex]; + level = this.levels[levelIndex]; level.items[level.index] = currentValue; } }, @@ -37284,7 +38544,7 @@ var JpxImage = (function JpxImageClosure() { } this.currentLevel = currentLevel; - var level = this.levels[currentLevel]; + level = this.levels[currentLevel]; level.items[level.index] = value; return true; } @@ -37315,31 +38575,6 @@ var JpxImage = (function JpxImageClosure() { 8, 0, 8, 8, 8, 0, 8, 8, 8, 0, 0, 0, 0, 0, 8, 8, 8, 0, 8, 8, 8, 0, 8, 8, 8 ]); - // Table D-2 - function calcSignContribution(significance0, sign0, significance1, sign1) { - if (significance1) { - if (!sign1) { - return significance0 ? (!sign0 ? 1 : 0) : 1; - } else { - return significance0 ? (!sign0 ? 0 : -1) : -1; - } - } else { - return significance0 ? (!sign0 ? 1 : -1) : 0; - } - } - // Table D-3 - var SignContextLabels = [ - {contextLabel: 13, xorBit: 0}, - {contextLabel: 12, xorBit: 0}, - {contextLabel: 11, xorBit: 0}, - {contextLabel: 10, xorBit: 0}, - {contextLabel: 9, xorBit: 0}, - {contextLabel: 10, xorBit: 1}, - {contextLabel: 11, xorBit: 1}, - {contextLabel: 12, xorBit: 1}, - {contextLabel: 13, xorBit: 1} - ]; - function BitModel(width, height, subband, zeroBitPlanes) { this.width = width; this.height = height; @@ -37356,9 +38591,11 @@ var JpxImage = (function JpxImageClosure() { this.coefficentsMagnitude = new Uint32Array(coefficientCount); this.processingFlags = new Uint8Array(coefficientCount); - var bitsDecoded = new Uint8Array(this.width * this.height); - for (var i = 0, ii = bitsDecoded.length; i < ii; i++) { - bitsDecoded[i] = zeroBitPlanes; + var bitsDecoded = new Uint8Array(coefficientCount); + if (zeroBitPlanes !== 0) { + for (var i = 0; i < coefficientCount; i++) { + bitsDecoded[i] = zeroBitPlanes; + } } this.bitsDecoded = bitsDecoded; @@ -37370,7 +38607,7 @@ var JpxImage = (function JpxImageClosure() { this.decoder = decoder; }, reset: function BitModel_reset() { - // We have 17 contexts that are accessed via context labels, + // We have 17 contexts that are accessed via context labels, // plus the uniform and runlength context. this.contexts = new Int8Array(19); @@ -37381,32 +38618,39 @@ var JpxImage = (function JpxImageClosure() { this.contexts[RUNLENGTH_CONTEXT] = (3 << 1) | 0; }, setNeighborsSignificance: - function BitModel_setNeighborsSignificance(row, column) { + function BitModel_setNeighborsSignificance(row, column, index) { var neighborsSignificance = this.neighborsSignificance; var width = this.width, height = this.height; - var index = row * width + column; + var left = (column > 0); + var right = (column + 1 < width); + var i; + if (row > 0) { - if (column > 0) { - neighborsSignificance[index - width - 1] += 0x10; + i = index - width; + if (left) { + neighborsSignificance[i - 1] += 0x10; } - if (column + 1 < width) { - neighborsSignificance[index - width + 1] += 0x10; + if (right) { + neighborsSignificance[i + 1] += 0x10; } - neighborsSignificance[index - width] += 0x04; + neighborsSignificance[i] += 0x04; } + if (row + 1 < height) { - if (column > 0) { - neighborsSignificance[index + width - 1] += 0x10; + i = index + width; + if (left) { + neighborsSignificance[i - 1] += 0x10; } - if (column + 1 < width) { - neighborsSignificance[index + width + 1] += 0x10; + if (right) { + neighborsSignificance[i + 1] += 0x10; } - neighborsSignificance[index + width] += 0x04; + neighborsSignificance[i] += 0x04; } - if (column > 0) { + + if (left) { neighborsSignificance[index - 1] += 0x01; } - if (column + 1 < width) { + if (right) { neighborsSignificance[index + 1] += 0x01; } neighborsSignificance[index] |= 0x80; @@ -37423,13 +38667,9 @@ var JpxImage = (function JpxImageClosure() { var contexts = this.contexts; var labels = this.contextLabelTable; var bitsDecoded = this.bitsDecoded; - // clear processed flag var processedInverseMask = ~1; var processedMask = 1; var firstMagnitudeBitMask = 2; - for (var q = 0, qq = width * height; q < qq; q++) { - processingFlags[q] &= processedInverseMask; - } for (var i0 = 0; i0 < height; i0 += 4) { for (var j = 0; j < width; j++) { @@ -37439,6 +38679,8 @@ var JpxImage = (function JpxImageClosure() { if (i >= height) { break; } + // clear processed flag first + processingFlags[index] &= processedInverseMask; if (coefficentsMagnitude[index] || !neighborsSignificance[index]) { @@ -37448,10 +38690,10 @@ var JpxImage = (function JpxImageClosure() { var contextLabel = labels[neighborsSignificance[index]]; var decision = decoder.readBit(contexts, contextLabel); if (decision) { - var sign = this.decodeSignBit(i, j); + var sign = this.decodeSignBit(i, j, index); coefficentsSign[index] = sign; coefficentsMagnitude[index] = 1; - this.setNeighborsSignificance(i, j); + this.setNeighborsSignificance(i, j, index); processingFlags[index] |= firstMagnitudeBitMask; } bitsDecoded[index]++; @@ -37460,27 +38702,56 @@ var JpxImage = (function JpxImageClosure() { } } }, - decodeSignBit: function BitModel_decodeSignBit(row, column) { + decodeSignBit: function BitModel_decodeSignBit(row, column, index) { var width = this.width, height = this.height; - var index = row * width + column; var coefficentsMagnitude = this.coefficentsMagnitude; var coefficentsSign = this.coefficentsSign; - var horizontalContribution = calcSignContribution( - column > 0 && coefficentsMagnitude[index - 1], - coefficentsSign[index - 1], - column + 1 < width && coefficentsMagnitude[index + 1], - coefficentsSign[index + 1]); - var verticalContribution = calcSignContribution( - row > 0 && coefficentsMagnitude[index - width], - coefficentsSign[index - width], - row + 1 < height && coefficentsMagnitude[index + width], - coefficentsSign[index + width]); + var contribution, sign0, sign1, significance1; + var contextLabel, decoded; - var contextLabelAndXor = SignContextLabels[ - 3 * (1 - horizontalContribution) + (1 - verticalContribution)]; - var contextLabel = contextLabelAndXor.contextLabel; - var decoded = this.decoder.readBit(this.contexts, contextLabel); - return decoded ^ contextLabelAndXor.xorBit; + // calculate horizontal contribution + significance1 = (column > 0 && coefficentsMagnitude[index - 1] !== 0); + if (column + 1 < width && coefficentsMagnitude[index + 1] !== 0) { + sign1 = coefficentsSign[index + 1]; + if (significance1) { + sign0 = coefficentsSign[index - 1]; + contribution = 1 - sign1 - sign0; + } else { + contribution = 1 - sign1 - sign1; + } + } else if (significance1) { + sign0 = coefficentsSign[index - 1]; + contribution = 1 - sign0 - sign0; + } else { + contribution = 0; + } + var horizontalContribution = 3 * contribution; + + // calculate vertical contribution and combine with the horizontal + significance1 = (row > 0 && coefficentsMagnitude[index - width] !== 0); + if (row + 1 < height && coefficentsMagnitude[index + width] !== 0) { + sign1 = coefficentsSign[index + width]; + if (significance1) { + sign0 = coefficentsSign[index - width]; + contribution = 1 - sign1 - sign0 + horizontalContribution; + } else { + contribution = 1 - sign1 - sign1 + horizontalContribution; + } + } else if (significance1) { + sign0 = coefficentsSign[index - width]; + contribution = 1 - sign0 - sign0 + horizontalContribution; + } else { + contribution = horizontalContribution; + } + + if (contribution >= 0) { + contextLabel = 9 + contribution; + decoded = this.decoder.readBit(this.contexts, contextLabel); + } else { + contextLabel = 9 - contribution; + decoded = this.decoder.readBit(this.contexts, contextLabel) ^ 1; + } + return decoded; }, runMagnitudeRefinementPass: function BitModel_runMagnitudeRefinementPass() { @@ -37493,14 +38764,13 @@ var JpxImage = (function JpxImageClosure() { var processingFlags = this.processingFlags; var processedMask = 1; var firstMagnitudeBitMask = 2; - for (var i0 = 0; i0 < height; i0 += 4) { + var length = width * height; + var width4 = width * 4; + + for (var index0 = 0, indexNext; index0 < length; index0 = indexNext) { + indexNext = Math.min(length, index0 + width4); for (var j = 0; j < width; j++) { - for (var i1 = 0; i1 < 4; i1++) { - var i = i0 + i1; - if (i >= height) { - break; - } - var index = i * width + j; + for (var index = index0 + j; index < indexNext; index += width) { // significant but not those that have just become if (!coefficentsMagnitude[index] || @@ -37510,12 +38780,10 @@ var JpxImage = (function JpxImageClosure() { var contextLabel = 16; if ((processingFlags[index] & firstMagnitudeBitMask) !== 0) { - processingFlags[i * width + j] ^= firstMagnitudeBitMask; + processingFlags[index] ^= firstMagnitudeBitMask; // first refinement - var significance = neighborsSignificance[index]; - var sumOfSignificance = (significance & 3) + - ((significance >> 2) & 3) + ((significance >> 4) & 7); - contextLabel = sumOfSignificance >= 1 ? 15 : 14; + var significance = neighborsSignificance[index] & 127; + contextLabel = significance === 0 ? 15 : 14; } var bit = decoder.readBit(contexts, contextLabel); @@ -37558,7 +38826,7 @@ var JpxImage = (function JpxImageClosure() { neighborsSignificance[index0 + twoRowsDown] === 0 && neighborsSignificance[index0 + threeRowsDown] === 0); var i1 = 0, index = index0; - var i; + var i, sign; if (allEmpty) { var hasSignificantCoefficent = decoder.readBit(contexts, RUNLENGTH_CONTEXT); @@ -37574,10 +38842,10 @@ var JpxImage = (function JpxImageClosure() { i = i0 + i1; index += i1 * width; - var sign = this.decodeSignBit(i, j); + sign = this.decodeSignBit(i, j, index); coefficentsSign[index] = sign; coefficentsMagnitude[index] = 1; - this.setNeighborsSignificance(i, j); + this.setNeighborsSignificance(i, j, index); processingFlags[index] |= firstMagnitudeBitMask; index = index0; @@ -37601,10 +38869,10 @@ var JpxImage = (function JpxImageClosure() { var contextLabel = labels[neighborsSignificance[index]]; var decision = decoder.readBit(contexts, contextLabel); if (decision == 1) { - var sign = this.decodeSignBit(i, j); + sign = this.decodeSignBit(i, j, index); coefficentsSign[index] = sign; coefficentsMagnitude[index] = 1; - this.setNeighborsSignificance(i, j); + this.setNeighborsSignificance(i, j, index); processingFlags[index] |= firstMagnitudeBitMask; } bitsDecoded[index]++; @@ -37635,7 +38903,7 @@ var JpxImage = (function JpxImageClosure() { Transform.prototype.calculate = function transformCalculate(subbands, u0, v0) { var ll = subbands[0]; - for (var i = 1, ii = subbands.length, j = 1; i < ii; i += 3, j++) { + for (var i = 1, ii = subbands.length; i < ii; i += 3) { ll = this.iterate(ll, subbands[i], subbands[i + 1], subbands[i + 2], u0, v0); } @@ -37665,28 +38933,28 @@ var JpxImage = (function JpxImageClosure() { var width = llWidth + hlWidth; var height = llHeight + lhHeight; var items = new Float32Array(width * height); - var i, j, k, l; + var i, j, k, l, v, u; - for (i = 0; i < llHeight; i++) { - var k = i * llWidth, l = i * 2 * width; - for (var j = 0; j < llWidth; j++, k++, l += 2) { + for (i = 0, k = 0; i < llHeight; i++) { + l = i * 2 * width; + for (j = 0; j < llWidth; j++, k++, l += 2) { items[l] = llItems[k]; } } - for (i = 0; i < hlHeight; i++) { - k = i * hlWidth; l = i * 2 * width + 1; + for (i = 0, k = 0; i < hlHeight; i++) { + l = i * 2 * width + 1; for (j = 0; j < hlWidth; j++, k++, l += 2) { items[l] = hlItems[k]; } } - for (i = 0; i < lhHeight; i++) { - k = i * lhWidth; l = (i * 2 + 1) * width; + for (i = 0, k = 0; i < lhHeight; i++) { + l = (i * 2 + 1) * width; for (j = 0; j < lhWidth; j++, k++, l += 2) { items[l] = lhItems[k]; } } - for (i = 0; i < hhHeight; i++) { - k = i * hhWidth; l = (i * 2 + 1) * width + 1; + for (i = 0, k = 0; i < hhHeight; i++) { + l = (i * 2 + 1) * width + 1; for (j = 0; j < hhWidth; j++, k++, l += 2) { items[l] = hhItems[k]; } @@ -37696,21 +38964,24 @@ var JpxImage = (function JpxImageClosure() { var rowBuffer = new Float32Array(width + 2 * bufferPadding); // Section F.3.4 HOR_SR - for (var v = 0; v < height; v++) { - if (width == 1) { - // if width = 1, when u0 even keep items as is, when odd divide by 2 - if ((u0 % 1) !== 0) { - items[v * width] /= 2; + if (width === 1) { + // if width = 1, when u0 even keep items as is, when odd divide by 2 + if ((u0 & 1) !== 0) { + for (v = 0, k = 0; v < height; v++, k += width) { + items[k] *= 0.5; } - continue; } - k = v * width; - rowBuffer.set(items.subarray(k, k + width), bufferPadding); + } else { + for (v = 0, k = 0; v < height; v++, k += width) { + rowBuffer.set(items.subarray(k, k + width), bufferPadding); - this.extend(rowBuffer, bufferPadding, width); - this.filter(rowBuffer, bufferPadding, width, u0, rowBuffer); + this.extend(rowBuffer, bufferPadding, width); + this.filter(rowBuffer, bufferPadding, width, u0, rowBuffer); - items.set(rowBuffer.subarray(bufferPadding, bufferPadding + width), k); + items.set( + rowBuffer.subarray(bufferPadding, bufferPadding + width), + k); + } } // Accesses to the items array can take long, because it may not fit into @@ -37724,40 +38995,42 @@ var JpxImage = (function JpxImageClosure() { for (i = 0; i < numBuffers; i++) { colBuffers.push(new Float32Array(height + 2 * bufferPadding)); } - var b, currentBuffer = 0, ll = bufferPadding + height; + var b, currentBuffer = 0; + ll = bufferPadding + height; // Section F.3.5 VER_SR - for (var u = 0; u < width; u++) { - if (height == 1) { + if (height === 1) { // if height = 1, when v0 even keep items as is, when odd divide by 2 - if ((v0 % 1) !== 0) { - items[u] /= 2; + if ((v0 & 1) !== 0) { + for (u = 0; u < width; u++) { + items[u] *= 0.5; } - continue; } - - // if we ran out of buffers, copy several image columns at once - if (currentBuffer === 0) { - numBuffers = Math.min(width - u, numBuffers); - for (k = u, l = bufferPadding; l < ll; k += width, l++) { - for (b = 0; b < numBuffers; b++) { - colBuffers[b][l] = items[k + b]; + } else { + for (u = 0; u < width; u++) { + // if we ran out of buffers, copy several image columns at once + if (currentBuffer === 0) { + numBuffers = Math.min(width - u, numBuffers); + for (k = u, l = bufferPadding; l < ll; k += width, l++) { + for (b = 0; b < numBuffers; b++) { + colBuffers[b][l] = items[k + b]; + } } + currentBuffer = numBuffers; } - currentBuffer = numBuffers; - } - currentBuffer--; - var buffer = colBuffers[currentBuffer]; - this.extend(buffer, bufferPadding, height); - this.filter(buffer, bufferPadding, height, v0, buffer); + currentBuffer--; + var buffer = colBuffers[currentBuffer]; + this.extend(buffer, bufferPadding, height); + this.filter(buffer, bufferPadding, height, v0, buffer); - // If this is last buffer in this group of buffers, flush all buffers. - if (currentBuffer === 0) { - k = u - numBuffers + 1; - for (l = bufferPadding; l < ll; k += width, l++) { - for (b = 0; b < numBuffers; b++) { - items[k + b] = colBuffers[b][l]; + // If this is last buffer in this group of buffers, flush all buffers. + if (currentBuffer === 0) { + k = u - numBuffers + 1; + for (l = bufferPadding; l < ll; k += width, l++) { + for (b = 0; b < numBuffers; b++) { + items[k + b] = colBuffers[b][l]; + } } } } @@ -37781,9 +39054,8 @@ var JpxImage = (function JpxImageClosure() { IrreversibleTransform.prototype = Object.create(Transform.prototype); IrreversibleTransform.prototype.filter = function irreversibleTransformFilter(y, offset, length, i0, x) { - var i0_ = Math.floor(i0 / 2); - var i1_ = Math.floor((i0 + length) / 2); - var offset_ = offset - (i0 % 1); + var len = length >> 1; + offset = offset | 0; var alpha = -1.586134342059924; var beta = -0.052980118572961; @@ -37791,40 +39063,33 @@ var JpxImage = (function JpxImageClosure() { var delta = 0.443506852043971; var K = 1.230174104914001; var K_ = 1 / K; + var j, n, nn; - // step 1 - var j = offset_ - 2; - for (var n = i0_ - 1, nn = i1_ + 2; n < nn; n++, j += 2) { - x[j] = K * y[j]; - } + // step 1 is combined with step 3 // step 2 - var j = offset_ - 3; - for (var n = i0_ - 2, nn = i1_ + 2; n < nn; n++, j += 2) { + for (j = offset - 3, n = len + 4; n--; j += 2) { x[j] = K_ * y[j]; } - // step 3 - var j = offset_ - 2; - for (var n = i0_ - 1, nn = i1_ + 2; n < nn; n++, j += 2) { - x[j] -= delta * (x[j - 1] + x[j + 1]); + // step 1 & 3 + for (j = offset - 2, n = len + 3; n--; j += 2) { + x[j] = K * y[j] - + delta * (x[j - 1] + x[j + 1]); } // step 4 - var j = offset_ - 1; - for (var n = i0_ - 1, nn = i1_ + 1; n < nn; n++, j += 2) { + for (j = offset - 1, n = len + 2; n--; j += 2) { x[j] -= gamma * (x[j - 1] + x[j + 1]); } // step 5 - var j = offset_; - for (var n = i0_, nn = i1_ + 1; n < nn; n++, j += 2) { + for (j = offset, n = len + 1; n--; j += 2) { x[j] -= beta * (x[j - 1] + x[j + 1]); } // step 6 - var j = offset_ + 1; - for (var n = i0_, nn = i1_; n < nn; n++, j += 2) { + for (j = offset + 1, n = len; n--; j += 2) { x[j] -= alpha * (x[j - 1] + x[j + 1]); } }; @@ -37841,16 +39106,16 @@ var JpxImage = (function JpxImageClosure() { ReversibleTransform.prototype = Object.create(Transform.prototype); ReversibleTransform.prototype.filter = function reversibleTransformFilter(y, offset, length, i0, x) { - var i0_ = Math.floor(i0 / 2); - var i1_ = Math.floor((i0 + length) / 2); - var offset_ = offset - (i0 % 1); + var len = length >> 1; + offset = offset | 0; + var j, n; - for (var n = i0_, nn = i1_ + 1, j = offset_; n < nn; n++, j += 2) { - x[j] = y[j] - Math.floor((y[j - 1] + y[j + 1] + 2) / 4); + for (j = offset, n = len + 1; n--; j += 2) { + x[j] = y[j] - ((y[j - 1] + y[j + 1] + 2) >> 2); } - for (var n = i0_, nn = i1_, j = offset_ + 1; n < nn; n++, j += 2) { - x[j] = y[j] + Math.floor((x[j - 1] + x[j + 1]) / 2); + for (j = offset + 1, n = len; n--; j += 2) { + x[j] = y[j] + ((x[j - 1] + x[j + 1]) >> 1); } }; @@ -38081,8 +39346,9 @@ var Jbig2Image = (function Jbig2ImageClosure() { var templateY = new Int8Array(templateLength); var changingTemplateEntries = []; var reuseMask = 0, minX = 0, maxX = 0, minY = 0; + var c, k; - for (var k = 0; k < templateLength; k++) { + for (k = 0; k < templateLength; k++) { templateX[k] = template[k].x; templateY[k] = template[k].y; minX = Math.min(minX, template[k].x); @@ -38104,7 +39370,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { var changingTemplateX = new Int8Array(changingEntriesLength); var changingTemplateY = new Int8Array(changingEntriesLength); var changingTemplateBit = new Uint16Array(changingEntriesLength); - for (var c = 0; c < changingEntriesLength; c++) { + for (c = 0; c < changingEntriesLength; c++) { k = changingTemplateEntries[c]; changingTemplateX[c] = template[k].x; changingTemplateY[c] = template[k].y; @@ -38123,7 +39389,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { var decoder = decodingContext.decoder; var contexts = decodingContext.contextCache.getContexts('GB'); - var ltp = 0, c, j, i0, j0, k, contextLabel = 0, bit, shift; + var ltp = 0, j, i0, j0, contextLabel = 0, bit, shift; for (var i = 0; i < height; i++) { if (prediction) { var sltp = decoder.readBit(contexts, pseudoPixelContext); @@ -38190,7 +39456,8 @@ var Jbig2Image = (function Jbig2ImageClosure() { var codingTemplateLength = codingTemplate.length; var codingTemplateX = new Int32Array(codingTemplateLength); var codingTemplateY = new Int32Array(codingTemplateLength); - for (var k = 0; k < codingTemplateLength; k++) { + var k; + for (k = 0; k < codingTemplateLength; k++) { codingTemplateX[k] = codingTemplate[k].x; codingTemplateY[k] = codingTemplate[k].y; } @@ -38202,7 +39469,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { var referenceTemplateLength = referenceTemplate.length; var referenceTemplateX = new Int32Array(referenceTemplateLength); var referenceTemplateY = new Int32Array(referenceTemplateLength); - for (var k = 0; k < referenceTemplateLength; k++) { + for (k = 0; k < referenceTemplateLength; k++) { referenceTemplateX[k] = referenceTemplate[k].x; referenceTemplateY[k] = referenceTemplate[k].y; } @@ -38227,19 +39494,20 @@ var Jbig2Image = (function Jbig2ImageClosure() { var row = new Uint8Array(width); bitmap.push(row); for (var j = 0; j < width; j++) { - + var i0, j0; var contextLabel = 0; - for (var k = 0; k < codingTemplateLength; k++) { - var i0 = i + codingTemplateY[k], j0 = j + codingTemplateX[k]; + for (k = 0; k < codingTemplateLength; k++) { + i0 = i + codingTemplateY[k]; + j0 = j + codingTemplateX[k]; if (i0 < 0 || j0 < 0 || j0 >= width) { contextLabel <<= 1; // out of bound pixel } else { contextLabel = (contextLabel << 1) | bitmap[i0][j0]; } } - for (var k = 0; k < referenceTemplateLength; k++) { - var i0 = i + referenceTemplateY[k] + offsetY; - var j0 = j + referenceTemplateX[k] + offsetX; + for (k = 0; k < referenceTemplateLength; k++) { + i0 = i + referenceTemplateY[k] + offsetY; + j0 = j + referenceTemplateX[k] + offsetX; if (i0 < 0 || i0 >= referenceHeight || j0 < 0 || j0 >= referenceWidth) { contextLabel <<= 1; // out of bound pixel @@ -38356,8 +39624,9 @@ var Jbig2Image = (function Jbig2ImageClosure() { // Prepare bitmap var bitmap = []; - for (var i = 0; i < height; i++) { - var row = new Uint8Array(width); + var i, row; + for (i = 0; i < height; i++) { + row = new Uint8Array(width); if (defaultPixelValue) { for (var j = 0; j < width; j++) { row[j] = defaultPixelValue; @@ -38370,7 +39639,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { var contextCache = decodingContext.contextCache; var stripT = -decodeInteger(contextCache, 'IADT', decoder); // 6.4.6 var firstS = 0; - var i = 0; + i = 0; while (i < numberOfSymbolInstances) { var deltaT = decodeInteger(contextCache, 'IADT', decoder); // 6.4.6 stripT += deltaT; @@ -38379,12 +39648,12 @@ var Jbig2Image = (function Jbig2ImageClosure() { firstS += deltaFirstS; var currentS = firstS; do { - var currentT = stripSize == 1 ? 0 : - decodeInteger(contextCache, 'IAIT', decoder); // 6.4.9 + var currentT = (stripSize == 1 ? 0 : + decodeInteger(contextCache, 'IAIT', decoder)); // 6.4.9 var t = stripSize * stripT + currentT; var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength); - var applyRefinement = refinement && - decodeInteger(contextCache, 'IARI', decoder); + var applyRefinement = (refinement && + decodeInteger(contextCache, 'IARI', decoder)); var symbolBitmap = inputSymbols[symbolId]; var symbolWidth = symbolBitmap[0].length; var symbolHeight = symbolBitmap.length; @@ -38402,25 +39671,26 @@ var Jbig2Image = (function Jbig2ImageClosure() { } var offsetT = t - ((referenceCorner & 1) ? 0 : symbolHeight); var offsetS = currentS - ((referenceCorner & 2) ? symbolWidth : 0); + var s2, t2, symbolRow; if (transposed) { // Place Symbol Bitmap from T1,S1 - for (var s2 = 0; s2 < symbolHeight; s2++) { - var row = bitmap[offsetS + s2]; + for (s2 = 0; s2 < symbolHeight; s2++) { + row = bitmap[offsetS + s2]; if (!row) { continue; } - var symbolRow = symbolBitmap[s2]; + symbolRow = symbolBitmap[s2]; // To ignore Parts of Symbol bitmap which goes // outside bitmap region var maxWidth = Math.min(width - offsetT, symbolWidth); switch (combinationOperator) { case 0: // OR - for (var t2 = 0; t2 < maxWidth; t2++) { + for (t2 = 0; t2 < maxWidth; t2++) { row[offsetT + t2] |= symbolRow[t2]; } break; case 2: // XOR - for (var t2 = 0; t2 < maxWidth; t2++) { + for (t2 = 0; t2 < maxWidth; t2++) { row[offsetT + t2] ^= symbolRow[t2]; } break; @@ -38431,20 +39701,20 @@ var Jbig2Image = (function Jbig2ImageClosure() { } currentS += symbolHeight - 1; } else { - for (var t2 = 0; t2 < symbolHeight; t2++) { - var row = bitmap[offsetT + t2]; + for (t2 = 0; t2 < symbolHeight; t2++) { + row = bitmap[offsetT + t2]; if (!row) { continue; } - var symbolRow = symbolBitmap[t2]; + symbolRow = symbolBitmap[t2]; switch (combinationOperator) { case 0: // OR - for (var s2 = 0; s2 < symbolWidth; s2++) { + for (s2 = 0; s2 < symbolWidth; s2++) { row[offsetS + s2] |= symbolRow[s2]; } break; case 2: // XOR - for (var s2 = 0; s2 < symbolWidth; s2++) { + for (s2 = 0; s2 < symbolWidth; s2++) { row[offsetS + s2] ^= symbolRow[s2]; } break; @@ -38499,7 +39769,8 @@ var Jbig2Image = (function Jbig2ImageClosure() { var referredToSegmentNumberSize = (segmentHeader.number <= 256 ? 1 : (segmentHeader.number <= 65536 ? 2 : 4)); var referredTo = []; - for (var i = 0; i < referredToCount; i++) { + var i, ii; + for (i = 0; i < referredToCount; i++) { var number = (referredToSegmentNumberSize == 1 ? data[position] : (referredToSegmentNumberSize == 2 ? readUint16(data, position) : readUint32(data, position))); @@ -38534,7 +39805,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { searchPattern[3] = (genericRegionInfo.height >> 16) & 0xFF; searchPattern[4] = (genericRegionInfo.height >> 8) & 0xFF; searchPattern[5] = genericRegionInfo.height & 0xFF; - for (var i = position, ii = data.length; i < ii; i++) { + for (i = position, ii = data.length; i < ii; i++) { var j = 0; while (j < searchPatternLength && searchPattern[j] === data[i + j]) { j++; @@ -38601,7 +39872,7 @@ var Jbig2Image = (function Jbig2ImageClosure() { var header = segment.header; var data = segment.data, position = segment.start, end = segment.end; - var args; + var args, at, i, atLength; switch (header.type) { case 0: // SymbolDictionary // 7.4.2 Symbol dictionary segment syntax @@ -38619,9 +39890,9 @@ var Jbig2Image = (function Jbig2ImageClosure() { dictionary.refinementTemplate = (dictionaryFlags >> 12) & 1; position += 2; if (!dictionary.huffman) { - var atLength = dictionary.template === 0 ? 4 : 1; - var at = []; - for (var i = 0; i < atLength; i++) { + atLength = dictionary.template === 0 ? 4 : 1; + at = []; + for (i = 0; i < atLength; i++) { at.push({ x: readInt8(data, position), y: readInt8(data, position + 1) @@ -38631,8 +39902,8 @@ var Jbig2Image = (function Jbig2ImageClosure() { dictionary.at = at; } if (dictionary.refinement && !dictionary.refinementTemplate) { - var at = []; - for (var i = 0; i < 2; i++) { + at = []; + for (i = 0; i < 2; i++) { at.push({ x: readInt8(data, position), y: readInt8(data, position + 1) @@ -38678,8 +39949,8 @@ var Jbig2Image = (function Jbig2ImageClosure() { !!(textRegionHuffmanFlags & 14); } if (textRegion.refinement && !textRegion.refinementTemplate) { - var at = []; - for (var i = 0; i < 2; i++) { + at = []; + for (i = 0; i < 2; i++) { at.push({ x: readInt8(data, position), y: readInt8(data, position + 1) @@ -38706,9 +39977,9 @@ var Jbig2Image = (function Jbig2ImageClosure() { genericRegion.template = (genericRegionSegmentFlags >> 1) & 3; genericRegion.prediction = !!(genericRegionSegmentFlags & 8); if (!genericRegion.mmr) { - var atLength = genericRegion.template === 0 ? 4 : 1; - var at = []; - for (var i = 0; i < atLength; i++) { + atLength = genericRegion.template === 0 ? 4 : 1; + at = []; + for (i = 0; i < atLength; i++) { at.push({ x: readInt8(data, position), y: readInt8(data, position + 1) @@ -38820,12 +40091,13 @@ var Jbig2Image = (function Jbig2ImageClosure() { var buffer = this.buffer; var mask0 = 128 >> (regionInfo.x & 7); var offset0 = regionInfo.y * rowSize + (regionInfo.x >> 3); + var i, j, mask, offset; switch (combinationOperator) { case 0: // OR - for (var i = 0; i < height; i++) { - var mask = mask0; - var offset = offset0; - for (var j = 0; j < width; j++) { + for (i = 0; i < height; i++) { + mask = mask0; + offset = offset0; + for (j = 0; j < width; j++) { if (bitmap[i][j]) { buffer[offset] |= mask; } @@ -38839,10 +40111,10 @@ var Jbig2Image = (function Jbig2ImageClosure() { } break; case 2: // XOR - for (var i = 0; i < height; i++) { - var mask = mask0; - var offset = offset0; - for (var j = 0; j < width; j++) { + for (i = 0; i < height; i++) { + mask = mask0; + offset = offset0; + for (j = 0; j < width; j++) { if (bitmap[i][j]) { buffer[offset] ^= mask; } @@ -39004,7 +40276,6 @@ var bidi = PDFJS.bidi = (function bidiClosure() { } function findUnequal(arr, start, value) { - var j; for (var j = start, jj = arr.length; j < jj; ++j) { if (arr[j] != value) { return j; @@ -39091,7 +40362,8 @@ var bidi = PDFJS.bidi = (function bidiClosure() { types.length = 0; var numBidi = 0; - for (var i = 0; i < strLength; ++i) { + var i, ii; + for (i = 0; i < strLength; ++i) { chars[i] = str.charAt(i); var charCode = str.charCodeAt(i); @@ -39131,7 +40403,7 @@ var bidi = PDFJS.bidi = (function bidiClosure() { } var levels = []; - for (var i = 0; i < strLength; ++i) { + for (i = 0; i < strLength; ++i) { levels[i] = startLevel; } @@ -39148,7 +40420,7 @@ var bidi = PDFJS.bidi = (function bidiClosure() { start of the level run, it will get the type of sor. */ var lastType = sor; - for (var i = 0; i < strLength; ++i) { + for (i = 0; i < strLength; ++i) { if (types[i] == 'NSM') { types[i] = lastType; } else { @@ -39161,9 +40433,10 @@ var bidi = PDFJS.bidi = (function bidiClosure() { first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number. */ - var lastType = sor; - for (var i = 0; i < strLength; ++i) { - var t = types[i]; + lastType = sor; + var t; + for (i = 0; i < strLength; ++i) { + t = types[i]; if (t == 'EN') { types[i] = (lastType == 'AL') ? 'AN' : 'EN'; } else if (t == 'R' || t == 'L' || t == 'AL') { @@ -39174,8 +40447,8 @@ var bidi = PDFJS.bidi = (function bidiClosure() { /* W3. Change all ALs to R. */ - for (var i = 0; i < strLength; ++i) { - var t = types[i]; + for (i = 0; i < strLength; ++i) { + t = types[i]; if (t == 'AL') { types[i] = 'R'; } @@ -39186,7 +40459,7 @@ var bidi = PDFJS.bidi = (function bidiClosure() { European number. A single common separator between two numbers of the same type changes to that type: */ - for (var i = 1; i < strLength - 1; ++i) { + for (i = 1; i < strLength - 1; ++i) { if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN') { types[i] = 'EN'; } @@ -39200,17 +40473,18 @@ var bidi = PDFJS.bidi = (function bidiClosure() { W5. A sequence of European terminators adjacent to European numbers changes to all European numbers: */ - for (var i = 0; i < strLength; ++i) { + for (i = 0; i < strLength; ++i) { if (types[i] == 'EN') { // do before - for (var j = i - 1; j >= 0; --j) { + var j; + for (j = i - 1; j >= 0; --j) { if (types[j] != 'ET') { break; } types[j] = 'EN'; } // do after - for (var j = i + 1; j < strLength; --j) { + for (j = i + 1; j < strLength; --j) { if (types[j] != 'ET') { break; } @@ -39222,8 +40496,8 @@ var bidi = PDFJS.bidi = (function bidiClosure() { /* W6. Otherwise, separators and terminators change to Other Neutral: */ - for (var i = 0; i < strLength; ++i) { - var t = types[i]; + for (i = 0; i < strLength; ++i) { + t = types[i]; if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS') { types[i] = 'ON'; } @@ -39234,9 +40508,9 @@ var bidi = PDFJS.bidi = (function bidiClosure() { first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L. */ - var lastType = sor; - for (var i = 0; i < strLength; ++i) { - var t = types[i]; + lastType = sor; + for (i = 0; i < strLength; ++i) { + t = types[i]; if (t == 'EN') { types[i] = ((lastType == 'L') ? 'L' : 'EN'); } else if (t == 'R' || t == 'L') { @@ -39250,7 +40524,7 @@ var bidi = PDFJS.bidi = (function bidiClosure() { numbers are treated as though they were R. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries. */ - for (var i = 0; i < strLength; ++i) { + for (i = 0; i < strLength; ++i) { if (types[i] == 'ON') { var end = findUnequal(types, i + 1, 'ON'); var before = sor; @@ -39278,7 +40552,7 @@ var bidi = PDFJS.bidi = (function bidiClosure() { /* N2. Any remaining neutrals take the embedding direction. */ - for (var i = 0; i < strLength; ++i) { + for (i = 0; i < strLength; ++i) { if (types[i] == 'ON') { types[i] = e; } @@ -39291,8 +40565,8 @@ var bidi = PDFJS.bidi = (function bidiClosure() { I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level. */ - for (var i = 0; i < strLength; ++i) { - var t = types[i]; + for (i = 0; i < strLength; ++i) { + t = types[i]; if (isEven(levels[i])) { if (t == 'R') { levels[i] += 1; @@ -39328,8 +40602,9 @@ var bidi = PDFJS.bidi = (function bidiClosure() { // find highest level & lowest odd level var highestLevel = -1; var lowestOddLevel = 99; - for (var i = 0, ii = levels.length; i < ii; ++i) { - var level = levels[i]; + var level; + for (i = 0, ii = levels.length; i < ii; ++i) { + level = levels[i]; if (highestLevel < level) { highestLevel = level; } @@ -39339,10 +40614,10 @@ var bidi = PDFJS.bidi = (function bidiClosure() { } // now reverse between those limits - for (var level = highestLevel; level >= lowestOddLevel; --level) { + for (level = highestLevel; level >= lowestOddLevel; --level) { // find segments to reverse var start = -1; - for (var i = 0, ii = levels.length; i < ii; ++i) { + for (i = 0, ii = levels.length; i < ii; ++i) { if (levels[i] < level) { if (start >= 0) { reverseValues(chars, start, i); @@ -39376,7 +40651,7 @@ var bidi = PDFJS.bidi = (function bidiClosure() { // Finally, return string var result = ''; - for (var i = 0, ii = chars.length; i < ii; ++i) { + for (i = 0, ii = chars.length; i < ii; ++i) { var ch = chars[i]; if (ch != '<' && ch != '>') { result += ch; @@ -39389,838 +40664,161 @@ var bidi = PDFJS.bidi = (function bidiClosure() { })(); +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -var BUILT_IN_CMAPS = [ -// << Start unicode maps. -'Adobe-GB1-UCS2', -'Adobe-CNS1-UCS2', -'Adobe-Japan1-UCS2', -'Adobe-Korea1-UCS2', -// >> End unicode maps. -'78-EUC-H', -'78-EUC-V', -'78-H', -'78-RKSJ-H', -'78-RKSJ-V', -'78-V', -'78ms-RKSJ-H', -'78ms-RKSJ-V', -'83pv-RKSJ-H', -'90ms-RKSJ-H', -'90ms-RKSJ-V', -'90msp-RKSJ-H', -'90msp-RKSJ-V', -'90pv-RKSJ-H', -'90pv-RKSJ-V', -'Add-H', -'Add-RKSJ-H', -'Add-RKSJ-V', -'Add-V', -'Adobe-CNS1-0', -'Adobe-CNS1-1', -'Adobe-CNS1-2', -'Adobe-CNS1-3', -'Adobe-CNS1-4', -'Adobe-CNS1-5', -'Adobe-CNS1-6', -'Adobe-GB1-0', -'Adobe-GB1-1', -'Adobe-GB1-2', -'Adobe-GB1-3', -'Adobe-GB1-4', -'Adobe-GB1-5', -'Adobe-Japan1-0', -'Adobe-Japan1-1', -'Adobe-Japan1-2', -'Adobe-Japan1-3', -'Adobe-Japan1-4', -'Adobe-Japan1-5', -'Adobe-Japan1-6', -'Adobe-Korea1-0', -'Adobe-Korea1-1', -'Adobe-Korea1-2', -'B5-H', -'B5-V', -'B5pc-H', -'B5pc-V', -'CNS-EUC-H', -'CNS-EUC-V', -'CNS1-H', -'CNS1-V', -'CNS2-H', -'CNS2-V', -'ETHK-B5-H', -'ETHK-B5-V', -'ETen-B5-H', -'ETen-B5-V', -'ETenms-B5-H', -'ETenms-B5-V', -'EUC-H', -'EUC-V', -'Ext-H', -'Ext-RKSJ-H', -'Ext-RKSJ-V', -'Ext-V', -'GB-EUC-H', -'GB-EUC-V', -'GB-H', -'GB-V', -'GBK-EUC-H', -'GBK-EUC-V', -'GBK2K-H', -'GBK2K-V', -'GBKp-EUC-H', -'GBKp-EUC-V', -'GBT-EUC-H', -'GBT-EUC-V', -'GBT-H', -'GBT-V', -'GBTpc-EUC-H', -'GBTpc-EUC-V', -'GBpc-EUC-H', -'GBpc-EUC-V', -'H', -'HKdla-B5-H', -'HKdla-B5-V', -'HKdlb-B5-H', -'HKdlb-B5-V', -'HKgccs-B5-H', -'HKgccs-B5-V', -'HKm314-B5-H', -'HKm314-B5-V', -'HKm471-B5-H', -'HKm471-B5-V', -'HKscs-B5-H', -'HKscs-B5-V', -'Hankaku', -'Hiragana', -'KSC-EUC-H', -'KSC-EUC-V', -'KSC-H', -'KSC-Johab-H', -'KSC-Johab-V', -'KSC-V', -'KSCms-UHC-H', -'KSCms-UHC-HW-H', -'KSCms-UHC-HW-V', -'KSCms-UHC-V', -'KSCpc-EUC-H', -'KSCpc-EUC-V', -'Katakana', -'NWP-H', -'NWP-V', -'RKSJ-H', -'RKSJ-V', -'Roman', -'UniCNS-UCS2-H', -'UniCNS-UCS2-V', -'UniCNS-UTF16-H', -'UniCNS-UTF16-V', -'UniCNS-UTF32-H', -'UniCNS-UTF32-V', -'UniCNS-UTF8-H', -'UniCNS-UTF8-V', -'UniGB-UCS2-H', -'UniGB-UCS2-V', -'UniGB-UTF16-H', -'UniGB-UTF16-V', -'UniGB-UTF32-H', -'UniGB-UTF32-V', -'UniGB-UTF8-H', -'UniGB-UTF8-V', -'UniJIS-UCS2-H', -'UniJIS-UCS2-HW-H', -'UniJIS-UCS2-HW-V', -'UniJIS-UCS2-V', -'UniJIS-UTF16-H', -'UniJIS-UTF16-V', -'UniJIS-UTF32-H', -'UniJIS-UTF32-V', -'UniJIS-UTF8-H', -'UniJIS-UTF8-V', -'UniJIS2004-UTF16-H', -'UniJIS2004-UTF16-V', -'UniJIS2004-UTF32-H', -'UniJIS2004-UTF32-V', -'UniJIS2004-UTF8-H', -'UniJIS2004-UTF8-V', -'UniJISPro-UCS2-HW-V', -'UniJISPro-UCS2-V', -'UniJISPro-UTF8-V', -'UniJISX0213-UTF32-H', -'UniJISX0213-UTF32-V', -'UniJISX02132004-UTF32-H', -'UniJISX02132004-UTF32-V', -'UniKS-UCS2-H', -'UniKS-UCS2-V', -'UniKS-UTF16-H', -'UniKS-UTF16-V', -'UniKS-UTF32-H', -'UniKS-UTF32-V', -'UniKS-UTF8-H', -'UniKS-UTF8-V', -'V', -'WP-Symbol']; +/* Copyright 2014 Opera Software ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * Based on https://code.google.com/p/smhasher/wiki/MurmurHash3. + * Hashes roughly 100 KB per millisecond on i7 3.4 GHz. + */ +/* globals Uint32ArrayView */ -// CMap, not to be confused with TrueType's cmap. -var CMap = (function CMapClosure() { - function CMap(builtInCMap) { - // Codespace ranges are stored as follows: - // [[1BytePairs], [2BytePairs], [3BytePairs], [4BytePairs]] - // where nBytePairs are ranges e.g. [low1, high1, low2, high2, ...] - this.codespaceRanges = [[], [], [], []]; - this.numCodespaceRanges = 0; - this.map = []; - this.vertical = false; - this.useCMap = null; - this.builtInCMap = builtInCMap; +'use strict'; + +var MurmurHash3_64 = (function MurmurHash3_64Closure (seed) { + // Workaround for missing math precison in JS. + var MASK_HIGH = 0xffff0000; + var MASK_LOW = 0xffff; + + function MurmurHash3_64 (seed) { + var SEED = 0xc3d2e1f0; + this.h1 = seed ? seed & 0xffffffff : SEED; + this.h2 = seed ? seed & 0xffffffff : SEED; } - CMap.prototype = { - addCodespaceRange: function(n, low, high) { - this.codespaceRanges[n - 1].push(low, high); - this.numCodespaceRanges++; - }, - mapRange: function(low, high, dstLow) { - var lastByte = dstLow.length - 1; - while (low <= high) { - this.map[low] = dstLow; - // Only the last byte has to be incremented. - dstLow = dstLow.substr(0, lastByte) + - String.fromCharCode(dstLow.charCodeAt(lastByte) + 1); - ++low; - } - }, - - mapRangeToArray: function(low, high, array) { - var i = 0; - while (low <= high) { - this.map[low] = array[i++]; - ++low; - } - }, - - mapOne: function(src, dst) { - this.map[src] = dst; - }, - - lookup: function(code) { - return this.map[code]; - }, - - readCharCode: function(str, offset) { - var c = 0; - var codespaceRanges = this.codespaceRanges; - var codespaceRangesLen = this.codespaceRanges.length; - // 9.7.6.2 CMap Mapping - // The code length is at most 4. - for (var n = 0; n < codespaceRangesLen; n++) { - c = ((c << 8) | str.charCodeAt(offset + n)) >>> 0; - // Check each codespace range to see if it falls within. - var codespaceRange = codespaceRanges[n]; - for (var k = 0, kk = codespaceRange.length; k < kk;) { - var low = codespaceRange[k++]; - var high = codespaceRange[k++]; - if (c >= low && c <= high) { - return [c, n + 1]; + MurmurHash3_64.prototype = { + update: function MurmurHash3_64_update(input) { + var useUint32ArrayView = false; + var i; + if (typeof input == 'string') { + var data = new Uint8Array(input.length * 2); + var length = 0; + for (i = 0; i < input.length; i++) { + var code = input.charCodeAt(i); + if (code <= 0xff) { + data[length++] = code; + } + else { + data[length++] = code >>> 8; + data[length++] = code & 0xff; } } - } - - return [0, 1]; - } - - }; - return CMap; -})(); - -var IdentityCMap = (function IdentityCMapClosure() { - function IdentityCMap(vertical, n) { - CMap.call(this); - this.vertical = vertical; - this.addCodespaceRange(n, 0, 0xffff); - this.mapRange(0, 0xffff, '\u0000'); - } - Util.inherit(IdentityCMap, CMap, {}); - - return IdentityCMap; -})(); - -var BinaryCMapReader = (function BinaryCMapReaderClosure() { - function fetchBinaryData(url) { - var nonBinaryRequest = PDFJS.disableWorker; - var request = new XMLHttpRequest(); - request.open('GET', url, false); - if (!nonBinaryRequest) { - try { - request.responseType = 'arraybuffer'; - nonBinaryRequest = request.responseType !== 'arraybuffer'; - } catch (e) { - nonBinaryRequest = true; - } - } - if (nonBinaryRequest && request.overrideMimeType) { - request.overrideMimeType('text/plain; charset=x-user-defined'); - } - request.send(null); - if (request.status === 0 && /^https?:/i.test(url)) { - error('Unable to get binary cMap at: ' + url); - } - if (nonBinaryRequest) { - var data = Array.prototype.map.call(request.responseText, function (ch) { - return ch.charCodeAt(0) & 255; - }); - return new Uint8Array(data); - } - return new Uint8Array(request.response); - } - - function hexToInt(a, size) { - var n = 0; - for (var i = 0; i <= size; i++) { - n = (n << 8) | a[i]; - } - return n >>> 0; - } - - function hexToStr(a, size) { - return String.fromCharCode.apply(null, a.subarray(0, size + 1)); - } - - function addHex(a, b, size) { - var c = 0; - for (var i = size; i >= 0; i--) { - c += a[i] + b[i]; - a[i] = c & 255; - c >>= 8; - } - } - - function incHex(a, size) { - var c = 1; - for (var i = size; i >= 0 && c > 0; i--) { - c += a[i]; - a[i] = c & 255; - c >>= 8; - } - } - - var MAX_NUM_SIZE = 16; - var MAX_ENCODED_NUM_SIZE = 19; // ceil(MAX_NUM_SIZE * 7 / 8) - - function BinaryCMapStream(data) { - this.buffer = data; - this.pos = 0; - this.end = data.length; - this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE); - } - - BinaryCMapStream.prototype = { - readByte: function () { - if (this.pos >= this.end) { - return -1; - } - return this.buffer[this.pos++]; - }, - readNumber: function () { - var n = 0; - var last; - do { - var b = this.readByte(); - if (b < 0) { - error('unexpected EOF in bcmap'); - } - last = !(b & 0x80); - n = (n << 7) | (b & 0x7F); - } while (!last); - return n; - }, - readSigned: function () { - var n = this.readNumber(); - return (n & 1) ? ~(n >>> 1) : n >>> 1; - }, - readHex: function (num, size) { - num.set(this.buffer.subarray(this.pos, - this.pos + size + 1)); - this.pos += size + 1; - }, - readHexNumber: function (num, size) { - var last; - var stack = this.tmpBuf, sp = 0; - do { - var b = this.readByte(); - if (b < 0) { - error('unexpected EOF in bcmap'); - } - last = !(b & 0x80); - stack[sp++] = b & 0x7F; - } while (!last); - var i = size, buffer = 0, bufferSize = 0; - while (i >= 0) { - while (bufferSize < 8 && stack.length > 0) { - buffer = (stack[--sp] << bufferSize) | buffer; - bufferSize += 7; - } - num[i] = buffer & 255; - i--; - buffer >>= 8; - bufferSize -= 8; - } - }, - readHexSigned: function (num, size) { - this.readHexNumber(num, size); - var sign = num[size] & 1 ? 255 : 0; - var c = 0; - for (var i = 0; i <= size; i++) { - c = ((c & 1) << 8) | num[i]; - num[i] = (c >> 1) ^ sign; - } - }, - readString: function () { - var len = this.readNumber(); - var s = ''; - for (var i = 0; i < len; i++) { - s += String.fromCharCode(this.readNumber()); - } - return s; - } - }; - - function processBinaryCMap(url, cMap, extend) { - var data = fetchBinaryData(url); - var stream = new BinaryCMapStream(data); - - var header = stream.readByte(); - cMap.vertical = !!(header & 1); - - var useCMap = null; - var start = new Uint8Array(MAX_NUM_SIZE); - var end = new Uint8Array(MAX_NUM_SIZE); - var char = new Uint8Array(MAX_NUM_SIZE); - var charCode = new Uint8Array(MAX_NUM_SIZE); - var tmp = new Uint8Array(MAX_NUM_SIZE); - var code; - - var b; - while ((b = stream.readByte()) >= 0) { - var type = b >> 5; - if (type === 7) { // metadata, e.g. comment or usecmap - switch (b & 0x1F) { - case 0: - stream.readString(); // skipping comment - break; - case 1: - useCMap = stream.readString(); - break; - } - continue; - } - var sequence = !!(b & 0x10); - var dataSize = b & 15; - - assert(dataSize + 1 <= MAX_NUM_SIZE); - - var ucs2DataSize = 1; - var subitemsCount = stream.readNumber(); - switch (type) { - case 0: // codespacerange - stream.readHex(start, dataSize); - stream.readHexNumber(end, dataSize); - addHex(end, start, dataSize); - cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), - hexToInt(end, dataSize)); - for (var i = 1; i < subitemsCount; i++) { - incHex(end, dataSize); - stream.readHexNumber(start, dataSize); - addHex(start, end, dataSize); - stream.readHexNumber(end, dataSize); - addHex(end, start, dataSize); - cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize), - hexToInt(end, dataSize)); - } - break; - case 1: // notdefrange - stream.readHex(start, dataSize); - stream.readHexNumber(end, dataSize); - addHex(end, start, dataSize); - code = stream.readNumber(); - // undefined range, skipping - for (var i = 1; i < subitemsCount; i++) { - incHex(end, dataSize); - stream.readHexNumber(start, dataSize); - addHex(start, end, dataSize); - stream.readHexNumber(end, dataSize); - addHex(end, start, dataSize); - code = stream.readNumber(); - // nop - } - break; - case 2: // cidchar - stream.readHex(char, dataSize); - code = stream.readNumber(); - cMap.mapOne(hexToInt(char, dataSize), String.fromCharCode(code)); - for (var i = 1; i < subitemsCount; i++) { - incHex(char, dataSize); - if (!sequence) { - stream.readHexNumber(tmp, dataSize); - addHex(char, tmp, dataSize); - } - code = stream.readSigned() + (code + 1); - cMap.mapOne(hexToInt(char, dataSize), String.fromCharCode(code)); - } - break; - case 3: // cidrange - stream.readHex(start, dataSize); - stream.readHexNumber(end, dataSize); - addHex(end, start, dataSize); - code = stream.readNumber(); - cMap.mapRange(hexToInt(start, dataSize), hexToInt(end, dataSize), - String.fromCharCode(code)); - for (var i = 1; i < subitemsCount; i++) { - incHex(end, dataSize); - if (!sequence) { - stream.readHexNumber(start, dataSize); - addHex(start, end, dataSize); - } else { - start.set(end); - } - stream.readHexNumber(end, dataSize); - addHex(end, start, dataSize); - code = stream.readNumber(); - cMap.mapRange(hexToInt(start, dataSize), hexToInt(end, dataSize), - String.fromCharCode(code)); - } - break; - case 4: // bfchar - stream.readHex(char, ucs2DataSize); - stream.readHex(charCode, dataSize); - cMap.mapOne(hexToInt(char, ucs2DataSize), - hexToStr(charCode, dataSize)); - for (var i = 1; i < subitemsCount; i++) { - incHex(char, ucs2DataSize); - if (!sequence) { - stream.readHexNumber(tmp, ucs2DataSize); - addHex(char, tmp, ucs2DataSize); - } - incHex(charCode, dataSize); - stream.readHexSigned(tmp, dataSize); - addHex(charCode, tmp, dataSize); - cMap.mapOne(hexToInt(char, ucs2DataSize), - hexToStr(charCode, dataSize)); - } - break; - case 5: // bfrange - stream.readHex(start, ucs2DataSize); - stream.readHexNumber(end, ucs2DataSize); - addHex(end, start, ucs2DataSize); - stream.readHex(charCode, dataSize); - cMap.mapRange(hexToInt(start, ucs2DataSize), - hexToInt(end, ucs2DataSize), - hexToStr(charCode, dataSize)); - for (var i = 1; i < subitemsCount; i++) { - incHex(end, ucs2DataSize); - if (!sequence) { - stream.readHexNumber(start, ucs2DataSize); - addHex(start, end, ucs2DataSize); - } else { - start.set(end); - } - stream.readHexNumber(end, ucs2DataSize); - addHex(end, start, ucs2DataSize); - stream.readHex(charCode, dataSize); - cMap.mapRange(hexToInt(start, ucs2DataSize), - hexToInt(end, ucs2DataSize), - hexToStr(charCode, dataSize)); - } - break; - default: - error('Unknown type: ' + type); - break; - } - } - - if (useCMap) { - extend(useCMap); - } - return cMap; - } - - function BinaryCMapReader() {} - - BinaryCMapReader.prototype = { - read: processBinaryCMap - }; - - return BinaryCMapReader; -})(); - -var CMapFactory = (function CMapFactoryClosure() { - function strToInt(str) { - var a = 0; - for (var i = 0; i < str.length; i++) { - a = (a << 8) | str.charCodeAt(i); - } - return a >>> 0; - } - - function expectString(obj) { - if (!isString(obj)) { - error('Malformed CMap: expected string.'); - } - } - - function expectInt(obj) { - if (!isInt(obj)) { - error('Malformed CMap: expected int.'); - } - } - - function parseBfChar(cMap, lexer) { - while (true) { - var obj = lexer.getObj(); - if (isEOF(obj)) { - break; - } - if (isCmd(obj, 'endbfchar')) { - return; - } - expectString(obj); - var src = strToInt(obj); - obj = lexer.getObj(); - // TODO are /dstName used? - expectString(obj); - var dst = obj; - cMap.mapOne(src, dst); - } - } - - function parseBfRange(cMap, lexer) { - while (true) { - var obj = lexer.getObj(); - if (isEOF(obj)) { - break; - } - if (isCmd(obj, 'endbfrange')) { - return; - } - expectString(obj); - var low = strToInt(obj); - obj = lexer.getObj(); - expectString(obj); - var high = strToInt(obj); - obj = lexer.getObj(); - if (isInt(obj) || isString(obj)) { - var dstLow = isInt(obj) ? String.fromCharCode(obj) : obj; - cMap.mapRange(low, high, dstLow); - } else if (isCmd(obj, '[')) { - obj = lexer.getObj(); - var array = []; - while (!isCmd(obj, ']') && !isEOF(obj)) { - array.push(obj); - obj = lexer.getObj(); - } - cMap.mapRangeToArray(low, high, array); + } else if (input instanceof Uint8Array) { + data = input; + length = data.length; + } else if (typeof input === 'object' && ('length' in input)) { + // processing regular arrays as well, e.g. for IE9 + data = input; + length = data.length; + useUint32ArrayView = true; } else { - break; + throw new Error('Wrong data format in MurmurHash3_64_update. ' + + 'Input must be a string or array.'); } - } - error('Invalid bf range.'); - } - function parseCidChar(cMap, lexer) { - while (true) { - var obj = lexer.getObj(); - if (isEOF(obj)) { - break; - } - if (isCmd(obj, 'endcidchar')) { - return; - } - expectString(obj); - var src = strToInt(obj); - obj = lexer.getObj(); - expectInt(obj); - var dst = String.fromCharCode(obj); - cMap.mapOne(src, dst); - } - } + var blockCounts = length >> 2; + var tailLength = length - blockCounts * 4; + // we don't care about endianness here + var dataUint32 = useUint32ArrayView ? + new Uint32ArrayView(data, blockCounts) : + new Uint32Array(data.buffer, 0, blockCounts); + var k1 = 0; + var k2 = 0; + var h1 = this.h1; + var h2 = this.h2; + var C1 = 0xcc9e2d51; + var C2 = 0x1b873593; + var C1_LOW = C1 & MASK_LOW; + var C2_LOW = C2 & MASK_LOW; - function parseCidRange(cMap, lexer) { - while (true) { - var obj = lexer.getObj(); - if (isEOF(obj)) { - break; - } - if (isCmd(obj, 'endcidrange')) { - return; - } - expectString(obj); - var low = strToInt(obj); - obj = lexer.getObj(); - expectString(obj); - var high = strToInt(obj); - obj = lexer.getObj(); - expectInt(obj); - var dstLow = String.fromCharCode(obj); - cMap.mapRange(low, high, dstLow); - } - } - - function parseCodespaceRange(cMap, lexer) { - while (true) { - var obj = lexer.getObj(); - if (isEOF(obj)) { - break; - } - if (isCmd(obj, 'endcodespacerange')) { - return; - } - if (!isString(obj)) { - break; - } - var low = strToInt(obj); - obj = lexer.getObj(); - if (!isString(obj)) { - break; - } - var high = strToInt(obj); - cMap.addCodespaceRange(obj.length, low, high); - } - error('Invalid codespace range.'); - } - - function parseWMode(cMap, lexer) { - var obj = lexer.getObj(); - if (isInt(obj)) { - cMap.vertical = !!obj; - } - } - - function parseCMap(cMap, lexer, builtInCMapParams, useCMap) { - var previous; - var embededUseCMap; - objLoop: while (true) { - var obj = lexer.getObj(); - if (isEOF(obj)) { - break; - } else if (isName(obj)) { - if (obj.name === 'WMode') { - parseWMode(cMap, lexer); - } - previous = obj; - } else if (isCmd(obj)) { - switch (obj.cmd) { - case 'endcmap': - break objLoop; - case 'usecmap': - if (isName(previous)) { - embededUseCMap = previous.name; - } - break; - case 'begincodespacerange': - parseCodespaceRange(cMap, lexer); - break; - case 'beginbfchar': - parseBfChar(cMap, lexer); - break; - case 'begincidchar': - parseCidChar(cMap, lexer); - break; - case 'beginbfrange': - parseBfRange(cMap, lexer); - break; - case 'begincidrange': - parseCidRange(cMap, lexer); - break; + for (i = 0; i < blockCounts; i++) { + if (i & 1) { + k1 = dataUint32[i]; + k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW); + k1 = k1 << 15 | k1 >>> 17; + k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW); + h1 ^= k1; + h1 = h1 << 13 | h1 >>> 19; + h1 = h1 * 5 + 0xe6546b64; + } else { + k2 = dataUint32[i]; + k2 = (k2 * C1 & MASK_HIGH) | (k2 * C1_LOW & MASK_LOW); + k2 = k2 << 15 | k2 >>> 17; + k2 = (k2 * C2 & MASK_HIGH) | (k2 * C2_LOW & MASK_LOW); + h2 ^= k2; + h2 = h2 << 13 | h2 >>> 19; + h2 = h2 * 5 + 0xe6546b64; } } - } - if (!useCMap && embededUseCMap) { - // Load the usecmap definition from the file only if there wasn't one - // specified. - useCMap = embededUseCMap; - } - if (useCMap) { - extendCMap(cMap, builtInCMapParams, useCMap); - } - } + k1 = 0; - function extendCMap(cMap, builtInCMapParams, useCMap) { - cMap.useCMap = createBuiltInCMap(useCMap, builtInCMapParams); - // If there aren't any code space ranges defined clone all the parent ones - // into this cMap. - if (cMap.numCodespaceRanges === 0) { - var useCodespaceRanges = cMap.useCMap.codespaceRanges; - for (var i = 0; i < useCodespaceRanges.length; i++) { - cMap.codespaceRanges[i] = useCodespaceRanges[i].slice(); - } - cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges; - } - // Merge the map into the current one, making sure not to override - // any previously defined entries. - for (var key in cMap.useCMap.map) { - if (key in cMap.map) { - continue; - } - cMap.map[key] = cMap.useCMap.map[key]; - } - } - - function parseBinaryCMap(name, builtInCMapParams) { - var url = builtInCMapParams.url + name + '.bcmap'; - var cMap = new CMap(true); - new BinaryCMapReader().read(url, cMap, function (useCMap) { - extendCMap(cMap, builtInCMapParams, useCMap); - }); - return cMap; - } - - function createBuiltInCMap(name, builtInCMapParams) { - if (name === 'Identity-H') { - return new IdentityCMap(false, 2); - } else if (name === 'Identity-V') { - return new IdentityCMap(true, 2); - } - if (BUILT_IN_CMAPS.indexOf(name) === -1) { - error('Unknown cMap name: ' + name); - } - assert (builtInCMapParams, 'buildin cmap parameters are not provided'); - - if (builtInCMapParams.packed) { - return parseBinaryCMap(name, builtInCMapParams); - } - - var request = new XMLHttpRequest(); - var url = builtInCMapParams.url + name; - request.open('GET', url, false); - request.send(null); - if (request.status === 0 && /^https?:/i.test(url)) { - error('Unable to get cMap at: ' + url); - } - var cMap = new CMap(true); - var lexer = new Lexer(new StringStream(request.responseText)); - parseCMap(cMap, lexer, builtInCMapParams, null); - return cMap; - } - - return { - create: function (encoding, builtInCMapParams, useCMap) { - if (isName(encoding)) { - return createBuiltInCMap(encoding.name, builtInCMapParams); - } else if (isStream(encoding)) { - var cMap = new CMap(); - var lexer = new Lexer(encoding); - try { - parseCMap(cMap, lexer, builtInCMapParams, useCMap); - } catch (e) { - warn('Invalid CMap data. ' + e); + switch (tailLength) { + case 3: + k1 ^= data[blockCounts * 4 + 2] << 16; + /* falls through */ + case 2: + k1 ^= data[blockCounts * 4 + 1] << 8; + /* falls through */ + case 1: + k1 ^= data[blockCounts * 4]; + /* falls through */ + k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW); + k1 = k1 << 15 | k1 >>> 17; + k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW); + if (blockCounts & 1) { + h1 ^= k1; + } else { + h2 ^= k1; } - return cMap; } - error('Encoding required.'); + + this.h1 = h1; + this.h2 = h2; + return this; + }, + + hexdigest: function MurmurHash3_64_hexdigest () { + var h1 = this.h1; + var h2 = this.h2; + + h1 ^= h2 >>> 1; + h1 = (h1 * 0xed558ccd & MASK_HIGH) | (h1 * 0x8ccd & MASK_LOW); + h2 = (h2 * 0xff51afd7 & MASK_HIGH) | + (((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16); + h1 ^= h2 >>> 1; + h1 = (h1 * 0x1a85ec53 & MASK_HIGH) | (h1 * 0xec53 & MASK_LOW); + h2 = (h2 * 0xc4ceb9fe & MASK_HIGH) | + (((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16); + h1 ^= h2 >>> 1; + + for (var i = 0, arr = [h1, h2], str = ''; i < arr.length; i++) { + var hex = (arr[i] >>> 0).toString(16); + while (hex.length < 8) { + hex = '0' + hex; + } + str += hex; + } + + return str; } }; + + return MurmurHash3_64; })(); /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ diff --git a/browser/extensions/pdfjs/content/web/debugger.js b/browser/extensions/pdfjs/content/web/debugger.js index 21e1193a159..ee217b9e0b5 100644 --- a/browser/extensions/pdfjs/content/web/debugger.js +++ b/browser/extensions/pdfjs/content/web/debugger.js @@ -211,10 +211,11 @@ var StepperManager = (function StepperManagerClosure() { return stepper; }, selectStepper: function selectStepper(pageIndex, selectPanel) { + var i; if (selectPanel) { this.manager.selectPanel(this); } - for (var i = 0; i < steppers.length; ++i) { + for (i = 0; i < steppers.length; ++i) { var stepper = steppers[i]; if (stepper.pageIndex == pageIndex) { stepper.panel.removeAttribute('hidden'); @@ -223,7 +224,7 @@ var StepperManager = (function StepperManagerClosure() { } } var options = stepperChooser.options; - for (var i = 0; i < options.length; ++i) { + for (i = 0; i < options.length; ++i) { var option = options[i]; option.selected = option.value == pageIndex; } @@ -344,7 +345,7 @@ var Stepper = (function StepperClosure() { var self = this; var chunk = document.createDocumentFragment(); var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT, - operatorList.fnArray.length); + operatorList.fnArray.length); for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) { var line = c('tr'); line.className = 'line'; @@ -369,7 +370,7 @@ var Stepper = (function StepperClosure() { if (fn in glyphCommands) { var glyphIndex = glyphCommands[fn]; var glyphs = args[glyphIndex]; - var decArgs = args.slice(); + decArgs = args.slice(); var newArg; if (fn === 'showSpacedText') { newArg = []; diff --git a/browser/extensions/pdfjs/content/web/viewer.css b/browser/extensions/pdfjs/content/web/viewer.css index 832ea67e5e4..3a0628f77c4 100644 --- a/browser/extensions/pdfjs/content/web/viewer.css +++ b/browser/extensions/pdfjs/content/web/viewer.css @@ -67,10 +67,12 @@ select { :-moz-full-screen .page { margin-bottom: 100%; + border: 0; } :fullscreen .page { margin-bottom: 100%; + border: 0; } :-moz-full-screen a:not(.internalLink) { @@ -1187,7 +1189,6 @@ canvas { .textLayer > div { color: transparent; position: absolute; - line-height: 1; white-space: pre; cursor: text; } @@ -1305,6 +1306,9 @@ canvas { background-color: hsla(0,0%,0%,.2); z-index: 10000; } +#overlayContainer > * { + overflow: auto; +} #promptContainer { display: table-cell; diff --git a/browser/extensions/pdfjs/content/web/viewer.js b/browser/extensions/pdfjs/content/web/viewer.js index cfef329d929..40962983518 100644 --- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -309,49 +309,99 @@ var Cache = function cacheCache(size) { var DEFAULT_PREFERENCES = { showPreviousViewOnLoad: true, defaultZoomValue: '', - ifAvailableShowOutlineOnLoad: false + ifAvailableShowOutlineOnLoad: false, + enableHandToolOnLoad: false, + enableWebGL: false }; -var Preferences = (function PreferencesClosure() { - function Preferences() { - this.prefs = {}; - this.isInitializedPromiseResolved = false; - this.initializedPromise = this.readFromStorage(DEFAULT_PREFERENCES).then( - function(prefObj) { - this.isInitializedPromiseResolved = true; +/** + * Preferences - Utility for storing persistent settings. + * Used for settings that should be applied to all opened documents, + * or every time the viewer is loaded. + */ +var Preferences = { + prefs: Object.create(DEFAULT_PREFERENCES), + isInitializedPromiseResolved: false, + initializedPromise: null, + + /** + * Initialize and fetch the current preference values from storage. + * @return {Promise} A promise that is resolved when the preferences + * have been initialized. + */ + initialize: function preferencesInitialize() { + return this.initializedPromise = + this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) { + this.isInitializedPromiseResolved = true; + if (prefObj) { + this.prefs = prefObj; + } + }.bind(this)); + }, + + /** + * Stub function for writing preferences to storage. + * NOTE: This should be overridden by a build-specific function defined below. + * @param {Object} prefObj The preferences that should be written to storage. + * @return {Promise} A promise that is resolved when the preference values + * have been written. + */ + _writeToStorage: function preferences_writeToStorage(prefObj) { + return Promise.resolve(); + }, + + /** + * Stub function for reading preferences from storage. + * NOTE: This should be overridden by a build-specific function defined below. + * @param {Object} prefObj The preferences that should be read from storage. + * @return {Promise} A promise that is resolved with an {Object} containing + * the preferences that have been read. + */ + _readFromStorage: function preferences_readFromStorage(prefObj) { + return Promise.resolve(); + }, + + /** + * Reset the preferences to their default values and update storage. + * @return {Promise} A promise that is resolved when the preference values + * have been reset. + */ + reset: function preferencesReset() { + return this.initializedPromise.then(function() { + this.prefs = Object.create(DEFAULT_PREFERENCES); + return this._writeToStorage(DEFAULT_PREFERENCES); + }.bind(this)); + }, + + /** + * Replace the current preference values with the ones from storage. + * @return {Promise} A promise that is resolved when the preference values + * have been updated. + */ + reload: function preferencesReload() { + return this.initializedPromise.then(function () { + this._readFromStorage(DEFAULT_PREFERENCES).then(function(prefObj) { if (prefObj) { this.prefs = prefObj; } }.bind(this)); - } + }.bind(this)); + }, - Preferences.prototype = { - writeToStorage: function Preferences_writeToStorage(prefObj) { - return; - }, - - readFromStorage: function Preferences_readFromStorage(prefObj) { - var readFromStoragePromise = Promise.resolve(); - return readFromStoragePromise; - }, - - reset: function Preferences_reset() { - if (this.isInitializedPromiseResolved) { - this.prefs = {}; - this.writeToStorage(DEFAULT_PREFERENCES); - } - }, - - set: function Preferences_set(name, value) { - if (!this.isInitializedPromiseResolved) { - return; - } else if (DEFAULT_PREFERENCES[name] === undefined) { - console.error('Preferences_set: \'' + name + '\' is undefined.'); - return; + /** + * Set the value of a preference. + * @param {string} name The name of the preference that should be changed. + * @param {boolean|number|string} value The new value of the preference. + * @return {Promise} A promise that is resolved when the value has been set, + * provided that the preference exists and the types match. + */ + set: function preferencesSet(name, value) { + return this.initializedPromise.then(function () { + if (DEFAULT_PREFERENCES[name] === undefined) { + throw new Error('preferencesSet: \'' + name + '\' is undefined.'); } else if (value === undefined) { - console.error('Preferences_set: no value is specified.'); - return; + throw new Error('preferencesSet: no value is specified.'); } var valueType = typeof value; var defaultType = typeof DEFAULT_PREFERENCES[name]; @@ -360,40 +410,43 @@ var Preferences = (function PreferencesClosure() { if (valueType === 'number' && defaultType === 'string') { value = value.toString(); } else { - console.error('Preferences_set: \'' + value + '\' is a \"' + - valueType + '\", expected a \"' + defaultType + '\".'); - return; + throw new Error('Preferences_set: \'' + value + '\' is a \"' + + valueType + '\", expected \"' + defaultType + '\".'); } } else { if (valueType === 'number' && (value | 0) !== value) { - console.error('Preferences_set: \'' + value + - '\' must be an \"integer\".'); - return; + throw new Error('Preferences_set: \'' + value + + '\' must be an \"integer\".'); } } this.prefs[name] = value; - this.writeToStorage(this.prefs); - }, + return this._writeToStorage(this.prefs); + }.bind(this)); + }, - get: function Preferences_get(name) { - var defaultPref = DEFAULT_PREFERENCES[name]; + /** + * Get the value of a preference. + * @param {string} name The name of the preference whose value is requested. + * @return {Promise} A promise that is resolved with a {boolean|number|string} + * containing the value of the preference. + */ + get: function preferencesGet(name) { + return this.initializedPromise.then(function () { + var defaultValue = DEFAULT_PREFERENCES[name]; - if (defaultPref === undefined) { - console.error('Preferences_get: \'' + name + '\' is undefined.'); - return; - } else if (this.isInitializedPromiseResolved) { - var pref = this.prefs[name]; + if (defaultValue === undefined) { + throw new Error('preferencesGet: \'' + name + '\' is undefined.'); + } else { + var prefValue = this.prefs[name]; - if (pref !== undefined) { - return pref; + if (prefValue !== undefined) { + return prefValue; } } - return defaultPref; - } - }; - - return Preferences; -})(); + return defaultValue; + }.bind(this)); + } +}; @@ -488,17 +541,19 @@ var DownloadManager = (function DownloadManagerClosure() { return DownloadManager; })(); -Preferences.prototype.writeToStorage = function(prefObj) { - FirefoxCom.requestSync('setPreferences', prefObj); +Preferences._writeToStorage = function (prefObj) { + return new Promise(function (resolve) { + FirefoxCom.request('setPreferences', prefObj, resolve); + }); }; -Preferences.prototype.readFromStorage = function(prefObj) { - var readFromStoragePromise = new Promise(function (resolve) { - var readPrefs = JSON.parse(FirefoxCom.requestSync('getPreferences', - prefObj)); - resolve(readPrefs); +Preferences._readFromStorage = function (prefObj) { + return new Promise(function (resolve) { + FirefoxCom.request('getPreferences', prefObj, function (prefStr) { + var readPrefs = JSON.parse(prefStr); + resolve(readPrefs); + }); }); - return readFromStoragePromise; }; @@ -513,7 +568,7 @@ var currentPageNumber = 1; * * The way that the view parameters are stored depends on how PDF.js is built, * for 'node make ' the following cases exist: - * - FIREFOX or MOZCENTRAL - uses about:config. + * - FIREFOX or MOZCENTRAL - uses sessionStorage. * - B2G - uses asyncStorage. * - GENERIC or CHROME - uses localStorage, if it is available. */ @@ -533,7 +588,7 @@ var ViewHistory = (function ViewHistoryClosure() { }).bind(this); - resolvePromise(FirefoxCom.requestSync('getDatabase', null)); + resolvePromise(sessionStorage.getItem('pdfjsHistory')); } @@ -570,7 +625,7 @@ var ViewHistory = (function ViewHistoryClosure() { var database = JSON.stringify(this.database); - FirefoxCom.requestSync('setDatabase', database); + sessionStorage.setItem('pdfjsHistory',database); }, @@ -871,11 +926,12 @@ var PDFFindController = { var self = this; function extractPageText(pageIndex) { self.pdfPageSource.pages[pageIndex].getTextContent().then( - function textContentResolved(bidiTexts) { + function textContentResolved(textContent) { + var textItems = textContent.items; var str = ''; - for (var i = 0; i < bidiTexts.length; i++) { - str += bidiTexts[i].str; + for (var i = 0; i < textItems.length; i++) { + str += textItems[i].str; } // Store the pageContent as a string. @@ -1778,11 +1834,9 @@ var PresentationMode = { // Presentation Mode, by waiting until fullscreen mode is disabled. // Note: This is only necessary in non-Mozilla browsers. setTimeout(function exitPresentationModeTimeout() { + this.active = false; PDFView.setScale(this.args.previousScale); PDFView.page = page; - // Keep Presentation Mode active until the page is scrolled into view, - // to prevent issues in non-Mozilla browsers. - this.active = false; this.args = null; }.bind(this), 0); @@ -2110,8 +2164,15 @@ var HandTool = { }); if (toggleHandTool) { toggleHandTool.addEventListener('click', this.toggle.bind(this), false); + + window.addEventListener('localized', function (evt) { + Preferences.get('enableHandToolOnLoad').then(function (prefValue) { + if (prefValue) { + this.handTool.activate(); + } + }.bind(this)); + }.bind(this)); } - // TODO: Read global prefs and call this.handTool.activate() if needed. }, toggle: function handToolToggle() { @@ -2177,6 +2238,10 @@ var DocumentProperties = { options.closeButton.addEventListener('click', this.hide.bind(this)); } + this.dataAvailablePromise = new Promise(function (resolve) { + this.resolveDataAvailable = resolve; + }.bind(this)); + // Bind the event listener for the Esc key (to close the dialog). window.addEventListener('keydown', function (e) { @@ -2187,44 +2252,51 @@ var DocumentProperties = { }, getProperties: function documentPropertiesGetProperties() { - var self = this; - + if (!this.visible) { + // If the dialog was closed before dataAvailablePromise was resolved, + // don't bother updating the properties. + return; + } // Get the file name. this.fileName = getPDFFileNameFromURL(PDFView.url); // Get the file size. PDFView.pdfDocument.getDownloadInfo().then(function(data) { - self.setFileSize(data.length); - }); + this.setFileSize(data.length); + this.updateUI(this.fileSizeField, this.fileSize); + }.bind(this)); // Get the other document properties. PDFView.pdfDocument.getMetadata().then(function(data) { var fields = [ - { field: self.fileNameField, content: self.fileName }, - { field: self.fileSizeField, content: self.fileSize }, - { field: self.titleField, content: data.info.Title }, - { field: self.authorField, content: data.info.Author }, - { field: self.subjectField, content: data.info.Subject }, - { field: self.keywordsField, content: data.info.Keywords }, - { field: self.creationDateField, - content: self.parseDate(data.info.CreationDate) }, - { field: self.modificationDateField, - content: self.parseDate(data.info.ModDate) }, - { field: self.creatorField, content: data.info.Creator }, - { field: self.producerField, content: data.info.Producer }, - { field: self.versionField, content: data.info.PDFFormatVersion }, - { field: self.pageCountField, content: PDFView.pdfDocument.numPages } + { field: this.fileNameField, content: this.fileName }, + // The fileSize field is updated once getDownloadInfo is resolved. + { field: this.titleField, content: data.info.Title }, + { field: this.authorField, content: data.info.Author }, + { field: this.subjectField, content: data.info.Subject }, + { field: this.keywordsField, content: data.info.Keywords }, + { field: this.creationDateField, + content: this.parseDate(data.info.CreationDate) }, + { field: this.modificationDateField, + content: this.parseDate(data.info.ModDate) }, + { field: this.creatorField, content: data.info.Creator }, + { field: this.producerField, content: data.info.Producer }, + { field: this.versionField, content: data.info.PDFFormatVersion }, + { field: this.pageCountField, content: PDFView.pdfDocument.numPages } ]; // Show the properties in the dialog. for (var item in fields) { var element = fields[item]; - if (element.field && element.content !== undefined && - element.content !== '') { - element.field.textContent = element.content; - } + this.updateUI(element.field, element.content); } - }); + }.bind(this)); + }, + + updateUI: function documentPropertiesUpdateUI(field, content) { + if (field && content !== undefined && content !== '') { + field.textContent = content; + } }, setFileSize: function documentPropertiesSetFileSize(fileSize) { @@ -2249,7 +2321,10 @@ var DocumentProperties = { this.visible = true; this.overlayContainer.classList.remove('hidden'); this.overlayContainer.lastElementChild.classList.remove('hidden'); - this.getProperties(); + + this.dataAvailablePromise.then(function () { + this.getProperties(); + }.bind(this)); }, hide: function documentPropertiesClose() { @@ -2347,6 +2422,8 @@ var PDFView = { this.watchScroll(thumbnailContainer, this.thumbnailViewScroll, this.renderHighestPriority.bind(this)); + Preferences.initialize(); + PDFFindBar.initialize({ bar: document.getElementById('findbar'), toggleButton: document.getElementById('viewFind'), @@ -2421,10 +2498,20 @@ var PDFView = { pageCountField: document.getElementById('pageCountField') }); - this.initialized = true; container.addEventListener('scroll', function() { self.lastScroll = Date.now(); }, false); + + var initializedPromise = Promise.all([ + Preferences.get('enableWebGL').then(function (value) { + PDFJS.disableWebGL = !value; + }) + // TODO move more preferences and other async stuff here + ]); + + return initializedPromise.then(function () { + PDFView.initialized = true; + }); }, getPage: function pdfViewGetPage(n) { @@ -2490,9 +2577,11 @@ var PDFView = { if (!currentPage) { return; } - var pageWidthScale = (this.container.clientWidth - SCROLLBAR_PADDING) / + var hPadding = PresentationMode.active ? 0 : SCROLLBAR_PADDING; + var vPadding = PresentationMode.active ? 0 : VERTICAL_PADDING; + var pageWidthScale = (this.container.clientWidth - hPadding) / currentPage.width * currentPage.scale; - var pageHeightScale = (this.container.clientHeight - VERTICAL_PADDING) / + var pageHeightScale = (this.container.clientHeight - vPadding) / currentPage.height * currentPage.scale; switch (value) { case 'page-actual': @@ -2774,6 +2863,9 @@ var PDFView = { pdfDataRangeTransport, args) { if (this.pdfDocument) { this.close(); + + // Reload the preferences if a document was previously opened. + Preferences.reload(); } var parameters = {password: password}; @@ -2791,6 +2883,8 @@ var PDFView = { var self = this; self.loading = true; + self.downloadComplete = false; + var passwordNeeded = function passwordNeeded(updatePassword, reason) { PasswordPrompt.updatePassword = updatePassword; PasswordPrompt.reason = reason; @@ -2813,13 +2907,13 @@ var PDFView = { if (exception && exception.name === 'InvalidPDFException') { // change error message also for other builds - var loadingErrorMessage = mozL10n.get('invalid_file_error', null, + loadingErrorMessage = mozL10n.get('invalid_file_error', null, 'Invalid or corrupted PDF file.'); } if (exception && exception.name === 'MissingPDFException') { // special message for missing PDF's - var loadingErrorMessage = mozL10n.get('missing_file_error', null, + loadingErrorMessage = mozL10n.get('missing_file_error', null, 'Missing PDF file.'); } @@ -2834,7 +2928,7 @@ var PDFView = { }, download: function pdfViewDownload() { - function noData() { + function downloadByUrl() { downloadManager.downloadUrl(url, filename); } @@ -2848,7 +2942,12 @@ var PDFView = { }; if (!this.pdfDocument) { // the PDF is not ready yet - noData(); + downloadByUrl(); + return; + } + + if (!this.downloadComplete) { // the PDF is still downloading + downloadByUrl(); return; } @@ -2857,8 +2956,8 @@ var PDFView = { var blob = PDFJS.createBlob(data, 'application/pdf'); downloadManager.download(blob, url, filename); }, - noData // Error occurred try downloading with just the url. - ).then(null, noData); + downloadByUrl // Error occurred try downloading with just the url. + ).then(null, downloadByUrl); }, fallback: function pdfViewFallback(featureId) { @@ -3026,7 +3125,10 @@ var PDFView = { this.pdfDocument = pdfDocument; + DocumentProperties.resolveDataAvailable(); + pdfDocument.getDownloadInfo().then(function() { + self.downloadComplete = true; PDFView.loadingBar.hide(); var outerContainer = document.getElementById('outerContainer'); outerContainer.classList.remove('loadingInProgress'); @@ -3039,7 +3141,6 @@ var PDFView = { mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}'); document.getElementById('pageNumber').max = pagesCount; - var prefs = PDFView.prefs = new Preferences(); PDFView.documentFingerprint = id; var store = PDFView.store = new ViewHistory(id); @@ -3108,15 +3209,26 @@ var PDFView = { PDFView.loadingBar.setWidth(container); PDFFindController.resolveFirstPage(); + + // Initialize the browsing history. + PDFHistory.initialize(self.documentFingerprint); }); - var prefsPromise = prefs.initializedPromise; - var storePromise = store.initializedPromise; - Promise.all([firstPagePromise, prefsPromise, storePromise]). - then(function() { - var showPreviousViewOnLoad = prefs.get('showPreviousViewOnLoad'); - var defaultZoomValue = prefs.get('defaultZoomValue'); + // Fetch the necessary preference values. + var showPreviousViewOnLoad; + var showPreviousViewOnLoadPromise = + Preferences.get('showPreviousViewOnLoad').then(function (prefValue) { + showPreviousViewOnLoad = prefValue; + }); + var defaultZoomValue; + var defaultZoomValuePromise = + Preferences.get('defaultZoomValue').then(function (prefValue) { + defaultZoomValue = prefValue; + }); + var storePromise = store.initializedPromise; + Promise.all([firstPagePromise, storePromise, showPreviousViewOnLoadPromise, + defaultZoomValuePromise]).then(function resolved() { var storedHash = null; if (showPreviousViewOnLoad && store.get('exists', false)) { var pageNum = store.get('page', '1'); @@ -3129,9 +3241,6 @@ var PDFView = { } else if (defaultZoomValue) { storedHash = 'page=1&zoom=' + defaultZoomValue; } - // Initialize the browsing history. - PDFHistory.initialize(self.documentFingerprint); - self.setInitialView(storedHash, scale); // Make all navigation keys work on document load, @@ -3140,6 +3249,12 @@ var PDFView = { self.container.focus(); self.container.blur(); } + }, function rejected(errorMsg) { + console.error(errorMsg); + + firstPagePromise.then(function () { + self.setInitialView(null, scale); + }); }); pagesPromise.then(function() { @@ -3178,11 +3293,16 @@ var PDFView = { self.outline = new DocumentOutlineView(outline); document.getElementById('viewOutline').disabled = !outline; - if (outline && prefs.get('ifAvailableShowOutlineOnLoad')) { - if (!self.sidebarOpen) { - document.getElementById('sidebarToggle').click(); - } - self.switchSidebarView('outline'); + if (outline) { + Preferences.get('ifAvailableShowOutlineOnLoad').then( + function (prefValue) { + if (prefValue) { + if (!self.sidebarOpen) { + document.getElementById('sidebarToggle').click(); + } + self.switchSidebarView('outline'); + } + }); } }); }); @@ -3194,9 +3314,10 @@ var PDFView = { // Provides some basic debug information console.log('PDF ' + pdfDocument.fingerprint + ' [' + - info.PDFFormatVersion + ' ' + (info.Producer || '-') + - ' / ' + (info.Creator || '-') + ']' + - (PDFJS.version ? ' (PDF.js: ' + PDFJS.version + ')' : '')); + info.PDFFormatVersion + ' ' + (info.Producer || '-').trim() + + ' / ' + (info.Creator || '-').trim() + ']' + + ' (PDF.js: ' + (PDFJS.version || '-') + + (!PDFJS.disableWebGL ? ' [WebGL]' : '') + ')'); var pdfTitle; if (metadata && metadata.has('dc:title')) { @@ -3568,10 +3689,11 @@ var PDFView = { } var alertNotReady = false; + var i, ii; if (!this.pages.length) { alertNotReady = true; } else { - for (var i = 0, ii = this.pages.length; i < ii; ++i) { + for (i = 0, ii = this.pages.length; i < ii; ++i) { if (!this.pages[i].pdfPage) { alertNotReady = true; break; @@ -3587,7 +3709,7 @@ var PDFView = { var body = document.querySelector('body'); body.setAttribute('data-mozPrintCallback', true); - for (var i = 0, ii = this.pages.length; i < ii; ++i) { + for (i = 0, ii = this.pages.length; i < ii; ++i) { this.pages[i].beforePrint(); } }, @@ -3601,14 +3723,15 @@ var PDFView = { rotatePages: function pdfViewRotatePages(delta) { var currentPage = this.pages[this.page - 1]; + var i, l; this.pageRotation = (this.pageRotation + 360 + delta) % 360; - for (var i = 0, l = this.pages.length; i < l; i++) { + for (i = 0, l = this.pages.length; i < l; i++) { var page = this.pages[i]; page.update(page.scale, this.pageRotation); } - for (var i = 0, l = this.thumbnails.length; i < l; i++) { + for (i = 0, l = this.thumbnails.length; i < l; i++) { var thumb = this.thumbnails[i]; thumb.update(this.pageRotation); } @@ -4019,7 +4142,7 @@ var PageView = function pageView(container, id, scale, var x = 0, y = 0; var width = 0, height = 0, widthScale, heightScale; - var changeOrientation = !!(this.rotation % 180); + var changeOrientation = (this.rotation % 180 === 0 ? false : true); var pageWidth = (changeOrientation ? this.height : this.width) / this.scale / CSS_UNITS; var pageHeight = (changeOrientation ? this.width : this.height) / @@ -4156,8 +4279,8 @@ var PageView = function pageView(container, id, scale, if (!PDFJS.disableTextLayer) { textLayerDiv = document.createElement('div'); textLayerDiv.className = 'textLayer'; - textLayerDiv.style.width = canvas.width + 'px'; - textLayerDiv.style.height = canvas.height + 'px'; + textLayerDiv.style.width = canvas.style.width; + textLayerDiv.style.height = canvas.style.height; div.appendChild(textLayerDiv); } var textLayer = this.textLayer = @@ -4174,14 +4297,6 @@ var PageView = function pageView(container, id, scale, if (outputScale.scaled) { ctx.scale(outputScale.sx, outputScale.sy); } - if (outputScale.scaled && textLayerDiv) { - var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' + - (1 / outputScale.sy) + ')'; - CustomStyle.setProp('transform' , textLayerDiv, cssScale); - CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%'); - textLayerDiv.dataset._scaleX = outputScale.sx; - textLayerDiv.dataset._scaleY = outputScale.sy; - } // Rendering area @@ -4267,20 +4382,19 @@ var PageView = function pageView(container, id, scale, this.renderTask.promise.then( function pdfPageRenderCallback() { pageViewDrawCallback(null); + if (textLayer) { + self.getTextContent().then( + function textContentResolved(textContent) { + textLayer.setTextContent(textContent); + } + ); + } }, function pdfPageRenderError(error) { pageViewDrawCallback(error); } ); - if (textLayer) { - this.getTextContent().then( - function textContentResolved(textContent) { - textLayer.setTextContent(textContent); - } - ); - } - setupAnnotations(div, pdfPage, this.viewport); div.setAttribute('data-loaded', true); }; @@ -4555,6 +4669,7 @@ var TextLayerBuilder = function textLayerBuilder(options) { this.lastScrollSource = options.lastScrollSource; this.viewport = options.viewport; this.isViewerInPresentationMode = options.isViewerInPresentationMode; + this.textDivs = []; if (typeof PDFFindController === 'undefined') { window.PDFFindController = null; @@ -4564,16 +4679,6 @@ var TextLayerBuilder = function textLayerBuilder(options) { this.lastScrollSource = null; } - this.beginLayout = function textLayerBuilderBeginLayout() { - this.textDivs = []; - this.renderingDone = false; - }; - - this.endLayout = function textLayerBuilderEndLayout() { - this.layoutDone = true; - this.insertDivContent(); - }; - this.renderLayer = function textLayerBuilderRenderLayer() { var textDivs = this.textDivs; var canvas = document.createElement('canvas'); @@ -4633,70 +4738,56 @@ var TextLayerBuilder = function textLayerBuilder(options) { } }; - this.appendText = function textLayerBuilderAppendText(geom) { + this.appendText = function textLayerBuilderAppendText(geom, styles) { + var style = styles[geom.fontName]; var textDiv = document.createElement('div'); - - // vScale and hScale already contain the scaling to pixel units - var fontHeight = geom.fontSize * Math.abs(geom.vScale); - textDiv.dataset.canvasWidth = geom.canvasWidth * Math.abs(geom.hScale); - textDiv.dataset.fontName = geom.fontName; - textDiv.dataset.angle = geom.angle * (180 / Math.PI); - - textDiv.style.fontSize = fontHeight + 'px'; - textDiv.style.fontFamily = geom.fontFamily; - var fontAscent = (geom.ascent ? geom.ascent * fontHeight : - (geom.descent ? (1 + geom.descent) * fontHeight : fontHeight)); - textDiv.style.left = (geom.x + (fontAscent * Math.sin(geom.angle))) + 'px'; - textDiv.style.top = (geom.y - (fontAscent * Math.cos(geom.angle))) + 'px'; - - // The content of the div is set in the `setTextContent` function. - this.textDivs.push(textDiv); - }; - - this.insertDivContent = function textLayerUpdateTextContent() { - // Only set the content of the divs once layout has finished, the content - // for the divs is available and content is not yet set on the divs. - if (!this.layoutDone || this.divContentDone || !this.textContent) { + if (!/\S/.test(geom.str)) { + textDiv.dataset.isWhitespace = true; return; } + var tx = PDFJS.Util.transform(this.viewport.transform, geom.transform); + var angle = Math.atan2(tx[1], tx[0]); + if (style.vertical) { + angle += Math.PI / 2; + } + var fontHeight = Math.sqrt((tx[2] * tx[2]) + (tx[3] * tx[3])); + var fontAscent = (style.ascent ? style.ascent * fontHeight : + (style.descent ? (1 + style.descent) * fontHeight : fontHeight)); - this.divContentDone = true; + textDiv.style.position = 'absolute'; + textDiv.style.left = (tx[4] + (fontAscent * Math.sin(angle))) + 'px'; + textDiv.style.top = (tx[5] - (fontAscent * Math.cos(angle))) + 'px'; + textDiv.style.fontSize = fontHeight + 'px'; + textDiv.style.fontFamily = style.fontFamily; - var textDivs = this.textDivs; - var bidiTexts = this.textContent; - - for (var i = 0; i < bidiTexts.length; i++) { - var bidiText = bidiTexts[i]; - var textDiv = textDivs[i]; - if (!/\S/.test(bidiText.str)) { - textDiv.dataset.isWhitespace = true; - continue; - } - - textDiv.textContent = bidiText.str; - // TODO refactor text layer to use text content position - /** - * var arr = this.viewport.convertToViewportPoint(bidiText.x, bidiText.y); - * textDiv.style.left = arr[0] + 'px'; - * textDiv.style.top = arr[1] + 'px'; - */ - // bidiText.dir may be 'ttb' for vertical texts. - textDiv.dir = bidiText.dir; + textDiv.textContent = geom.str; + textDiv.dataset.fontName = geom.fontName; + textDiv.dataset.angle = angle * (180 / Math.PI); + if (style.vertical) { + textDiv.dataset.canvasWidth = geom.height * this.viewport.scale; + } else { + textDiv.dataset.canvasWidth = geom.width * this.viewport.scale; } - this.setupRenderLayoutTimer(); }; this.setTextContent = function textLayerBuilderSetTextContent(textContent) { this.textContent = textContent; - this.insertDivContent(); + + var textItems = textContent.items; + for (var i = 0; i < textItems.length; i++) { + this.appendText(textItems[i], textContent.styles); + } + this.divContentDone = true; + + this.setupRenderLayoutTimer(); }; this.convertMatches = function textLayerBuilderConvertMatches(matches) { var i = 0; var iIndex = 0; - var bidiTexts = this.textContent; + var bidiTexts = this.textContent.items; var end = bidiTexts.length - 1; var queryLen = (PDFFindController === null ? 0 : PDFFindController.state.query.length); @@ -4755,7 +4846,7 @@ var TextLayerBuilder = function textLayerBuilder(options) { return; } - var bidiTexts = this.textContent; + var bidiTexts = this.textContent.items; var textDivs = this.textDivs; var prevEnd = null; var isSelectedPage = (PDFFindController === null ? @@ -4871,7 +4962,7 @@ var TextLayerBuilder = function textLayerBuilder(options) { // Clear out all matches. var matches = this.matches; var textDivs = this.textDivs; - var bidiTexts = this.textContent; + var bidiTexts = this.textContent.items; var clearedUntilDivIdx = -1; // Clear out all current matches. @@ -4951,8 +5042,10 @@ var DocumentOutlineView = function documentOutlineView(outline) { function webViewerLoad(evt) { - PDFView.initialize(); + PDFView.initialize().then(webViewerInitialized); +} +function webViewerInitialized() { var file = window.location.href.split('#')[0]; document.getElementById('openFile').setAttribute('hidden', 'true'); @@ -4982,6 +5075,10 @@ function webViewerLoad(evt) { PDFJS.disableHistory = (hashParams['disableHistory'] === 'true'); } + if ('webgl' in hashParams) { + PDFJS.disableWebGL = (hashParams['webgl'] !== 'true'); + } + if ('useOnlyCssZoom' in hashParams) { USE_ONLY_CSS_ZOOM = (hashParams['useOnlyCssZoom'] === 'true'); } @@ -5135,7 +5232,6 @@ function webViewerLoad(evt) { if (file) { PDFView.open(file, 0); } - } document.addEventListener('DOMContentLoaded', webViewerLoad, true);