author | Ryan VanderMeulen <ryanvm@gmail.com> |
Fri, 20 Nov 2015 11:32:09 -0500 | |
changeset 273732 | aef06cd725fc577d1b3449e15cec9fa9f578a286 |
parent 273731 | 1f9d9bfafd5143618083adcd50f5fcb0fead5530 |
child 273733 | 67a9dd1470e731be61744b89693dd2e4103fd9e7 |
push id | 68366 |
push user | cbook@mozilla.com |
push date | Mon, 23 Nov 2015 13:31:58 +0000 |
treeherder | mozilla-inbound@eff4131a3e4c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bdahl |
bugs | 1226621 |
milestone | 45.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,3 +1,3 @@ This is the pdf.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.2.109 +Current extension version is: 1.3.14
--- a/browser/extensions/pdfjs/content/PdfJs.jsm +++ b/browser/extensions/pdfjs/content/PdfJs.jsm @@ -1,29 +1,22 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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. */ -/* jshint esnext:true */ -/* globals Components, Services, XPCOMUtils, PdfjsChromeUtils, - PdfjsContentUtils, DEFAULT_PREFERENCES, PdfStreamConverter */ - -'use strict'; var EXPORTED_SYMBOLS = ['PdfJs']; const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cm = Components.manager; const Cu = Components.utils;
--- a/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm +++ b/browser/extensions/pdfjs/content/PdfJsTelemetry.jsm @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2013 Mozilla Foundation * * 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 *
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm +++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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 *
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm @@ -1,28 +1,22 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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. */ -/* jshint esnext:true */ -/* globals Components, Services, XPCOMUtils, DEFAULT_PREFERENCES */ - -'use strict'; var EXPORTED_SYMBOLS = ['PdfjsChromeUtils']; const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils;
--- a/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm +++ b/browser/extensions/pdfjs/content/PdfjsContentUtils.jsm @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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 *
--- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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 * @@ -17,18 +15,18 @@ /*jshint globalstrict: false */ /* globals PDFJS */ // Initializing PDFJS global object (if still undefined) if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.2.109'; -PDFJS.build = '875588d'; +PDFJS.version = '1.3.14'; +PDFJS.build = 'df46b64'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it 'use strict'; var globalScope = (typeof window === 'undefined') ? this : window; @@ -1958,16 +1956,18 @@ var PDFDocumentProxy = (function PDFDocu * Page render parameters. * * @typedef {Object} RenderParameters * @property {Object} canvasContext - A 2D context of a DOM Canvas object. * @property {PDFJS.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 {Array} transform - (optional) Additional transform, applied + * just before viewport transform. * @property {Object} imageLayer - (optional) An object that has beginLayout, * endLayout and appendImage functions. * @property {function} continueCallback - (deprecated) 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. */ @@ -3020,17 +3020,17 @@ var InternalRenderTask = (function Inter this.stepper.init(this.operatorList); this.stepper.nextBreakPoint = this.stepper.getNextBreakPoint(); } var params = this.params; this.gfx = new CanvasGraphics(params.canvasContext, this.commonObjs, this.objs, params.imageLayer); - this.gfx.beginDrawing(params.viewport, transparency); + this.gfx.beginDrawing(params.transform, params.viewport, transparency); this.operatorListIdx = 0; this.graphicsReady = true; if (this.graphicsReadyCallback) { this.graphicsReadyCallback(); } }, cancel: function InternalRenderTask_cancel() { @@ -3317,48 +3317,51 @@ function addContextCurrentTransform(ctx) ]; this._originalRotate(angle); }; } } var CachedCanvases = (function CachedCanvasesClosure() { - var cache = {}; - return { + function CachedCanvases() { + this.cache = Object.create(null); + } + CachedCanvases.prototype = { getCanvas: function CachedCanvases_getCanvas(id, width, height, trackTransform) { var canvasEntry; - if (cache[id] !== undefined) { - canvasEntry = cache[id]; + if (this.cache[id] !== undefined) { + canvasEntry = this.cache[id]; canvasEntry.canvas.width = width; canvasEntry.canvas.height = height; // reset canvas transform for emulated mozCurrentTransform, if needed canvasEntry.context.setTransform(1, 0, 0, 1, 0, 0); } else { var canvas = createScratchCanvas(width, height); var ctx = canvas.getContext('2d'); if (trackTransform) { addContextCurrentTransform(ctx); } - cache[id] = canvasEntry = {canvas: canvas, context: ctx}; + this.cache[id] = canvasEntry = {canvas: canvas, context: ctx}; } return canvasEntry; }, clear: function () { - for (var id in cache) { - var canvasEntry = cache[id]; + for (var id in this.cache) { + var canvasEntry = this.cache[id]; // Zeroing the width and height causes Firefox to release graphics // resources immediately, which can greatly reduce memory consumption. canvasEntry.canvas.width = 0; canvasEntry.canvas.height = 0; - delete cache[id]; + delete this.cache[id]; } } }; + return CachedCanvases; })(); function compileType3Glyph(imgData) { var POINT_TO_PROCESS_LIMIT = 1000; var width = imgData.width, height = imgData.height; var i, j, j0, width1 = width + 1; var points = new Uint8Array(width1 * (height + 1)); @@ -3586,16 +3589,17 @@ var CanvasGraphics = (function CanvasGra // Patterns are painted relative to the initial page/form transform, see pdf // spec 8.7.2 NOTE 1. this.baseTransform = null; this.baseTransformStack = []; this.groupLevel = 0; this.smaskStack = []; this.smaskCounter = 0; this.tempSMask = null; + this.cachedCanvases = new CachedCanvases(); if (canvasCtx) { // NOTE: if mozCurrentTransform is polyfilled, then the current state of // the transformation must already be set in canvasCtx._transformMatrix. addContextCurrentTransform(canvasCtx); } this.cachedGetSinglePixelWidth = null; } @@ -3862,38 +3866,49 @@ var CanvasGraphics = (function CanvasGra var LINE_CAP_STYLES = ['butt', 'round', 'square']; var LINE_JOIN_STYLES = ['miter', 'round', 'bevel']; var NORMAL_CLIP = {}; var EO_CLIP = {}; CanvasGraphics.prototype = { - beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) { + beginDrawing: function CanvasGraphics_beginDrawing(transform, viewport, + transparency) { // For pdfs that use blend modes we have to clear the canvas else certain // blend modes can look wrong since we'd be blending with a white // backdrop. The problem with a transparent backdrop though is we then - // don't get sub pixel anti aliasing on text, so we fill with white if - // we can. + // don't get sub pixel anti aliasing on text, creating temporary + // transparent canvas when we have blend modes. var width = this.ctx.canvas.width; var height = this.ctx.canvas.height; - if (transparency) { - this.ctx.clearRect(0, 0, width, height); - } else { - this.ctx.mozOpaque = true; - this.ctx.save(); - this.ctx.fillStyle = 'rgb(255, 255, 255)'; - this.ctx.fillRect(0, 0, width, height); - this.ctx.restore(); - } - - var transform = viewport.transform; this.ctx.save(); - this.ctx.transform.apply(this.ctx, transform); + this.ctx.fillStyle = 'rgb(255, 255, 255)'; + this.ctx.fillRect(0, 0, width, height); + this.ctx.restore(); + + if (transparency) { + var transparentCanvas = this.cachedCanvases.getCanvas( + 'transparent', width, height, true); + this.compositeCtx = this.ctx; + this.transparentCanvas = transparentCanvas.canvas; + this.ctx = transparentCanvas.context; + this.ctx.save(); + // The transform can be applied before rendering, transferring it to + // the new canvas. + this.ctx.transform.apply(this.ctx, + this.compositeCtx.mozCurrentTransform); + } + + this.ctx.save(); + if (transform) { + this.ctx.transform.apply(this.ctx, transform); + } + this.ctx.transform.apply(this.ctx, viewport.transform); this.baseTransform = this.ctx.mozCurrentTransform.slice(); if (this.imageLayer) { this.imageLayer.beginLayout(); } }, @@ -3965,17 +3980,24 @@ var CanvasGraphics = (function CanvasGra // If the operatorList isn't executed completely yet OR the execution // time was short enough, do another execution round. } }, endDrawing: function CanvasGraphics_endDrawing() { this.ctx.restore(); - CachedCanvases.clear(); + + if (this.transparentCanvas) { + this.ctx = this.compositeCtx; + this.ctx.drawImage(this.transparentCanvas, 0, 0); + this.transparentCanvas = null; + } + + this.cachedCanvases.clear(); WebGLUtils.clear(); if (this.imageLayer) { this.imageLayer.endLayout(); } }, // Graphics state @@ -4079,17 +4101,17 @@ var CanvasGraphics = (function CanvasGra } }, beginSMaskGroup: function CanvasGraphics_beginSMaskGroup() { var activeSMask = this.current.activeSMask; var drawnWidth = activeSMask.canvas.width; var drawnHeight = activeSMask.canvas.height; var cacheId = 'smaskGroupAt' + this.groupLevel; - var scratchCanvas = CachedCanvases.getCanvas( + var scratchCanvas = this.cachedCanvases.getCanvas( cacheId, drawnWidth, drawnHeight, true); var currentCtx = this.ctx; var currentTransform = currentCtx.mozCurrentTransform; this.ctx.save(); var groupCtx = scratchCanvas.context; groupCtx.scale(1 / activeSMask.scaleX, 1 / activeSMask.scaleY); @@ -4863,17 +4885,17 @@ var CanvasGraphics = (function CanvasGra drawnHeight = MAX_GROUP_SIZE; } var cacheId = 'groupAt' + this.groupLevel; if (group.smask) { // Using two cache entries is case if masks are used one after another. cacheId += '_smask_' + ((this.smaskCounter++) % 2); } - var scratchCanvas = CachedCanvases.getCanvas( + var scratchCanvas = this.cachedCanvases.getCanvas( cacheId, drawnWidth, drawnHeight, true); var groupCtx = scratchCanvas.context; // Since we created a new canvas that is just the size of the bounding box // we have to translate the group ctx. groupCtx.scale(1 / scaleX, 1 / scaleY); groupCtx.translate(-offsetX, -offsetY); groupCtx.transform.apply(groupCtx, currentTransform); @@ -5004,17 +5026,18 @@ var CanvasGraphics = (function CanvasGra } } if (glyph && glyph.compiled) { glyph.compiled(ctx); return; } - var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); + var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas', + width, height); var maskCtx = maskCanvas.context; maskCtx.save(); putBinaryImageMask(maskCtx, img); maskCtx.globalCompositeOperation = 'source-in'; maskCtx.fillStyle = isPatternFill ? @@ -5029,17 +5052,18 @@ var CanvasGraphics = (function CanvasGra paintImageMaskXObjectRepeat: function CanvasGraphics_paintImageMaskXObjectRepeat(imgData, scaleX, scaleY, positions) { var width = imgData.width; var height = imgData.height; var fillColor = this.current.fillColor; var isPatternFill = this.current.patternFill; - var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); + var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas', + width, height); var maskCtx = maskCanvas.context; maskCtx.save(); putBinaryImageMask(maskCtx, imgData); maskCtx.globalCompositeOperation = 'source-in'; maskCtx.fillStyle = isPatternFill ? @@ -5064,17 +5088,18 @@ var CanvasGraphics = (function CanvasGra var ctx = this.ctx; var fillColor = this.current.fillColor; var isPatternFill = this.current.patternFill; for (var i = 0, ii = images.length; i < ii; i++) { var image = images[i]; var width = image.width, height = image.height; - var maskCanvas = CachedCanvases.getCanvas('maskCanvas', width, height); + var maskCanvas = this.cachedCanvases.getCanvas('maskCanvas', + width, height); var maskCtx = maskCanvas.context; maskCtx.save(); putBinaryImageMask(maskCtx, image); maskCtx.globalCompositeOperation = 'source-in'; maskCtx.fillStyle = isPatternFill ? @@ -5137,17 +5162,18 @@ var CanvasGraphics = (function CanvasGra var c = currentTransform[2], d = currentTransform[3]; var heightScale = Math.max(Math.sqrt(c * c + d * d), 1); var imgToPaint, tmpCanvas; // instanceof HTMLElement does not work in jsdom node.js module if (imgData instanceof HTMLElement || !imgData.data) { imgToPaint = imgData; } else { - tmpCanvas = CachedCanvases.getCanvas('inlineImage', width, height); + tmpCanvas = this.cachedCanvases.getCanvas('inlineImage', + width, height); var tmpCtx = tmpCanvas.context; putBinaryImageData(tmpCtx, imgData); imgToPaint = tmpCanvas.canvas; } var paintWidth = width, paintHeight = height; var tmpCanvasId = 'prescale1'; // Vertial or horizontal scaling shall not be more than 2 to not loose the @@ -5159,17 +5185,18 @@ var CanvasGraphics = (function CanvasGra if (widthScale > 2 && paintWidth > 1) { newWidth = Math.ceil(paintWidth / 2); widthScale /= paintWidth / newWidth; } if (heightScale > 2 && paintHeight > 1) { newHeight = Math.ceil(paintHeight / 2); heightScale /= paintHeight / newHeight; } - tmpCanvas = CachedCanvases.getCanvas(tmpCanvasId, newWidth, newHeight); + tmpCanvas = this.cachedCanvases.getCanvas(tmpCanvasId, + newWidth, newHeight); tmpCtx = tmpCanvas.context; tmpCtx.clearRect(0, 0, newWidth, newHeight); tmpCtx.drawImage(imgToPaint, 0, 0, paintWidth, paintHeight, 0, 0, newWidth, newHeight); imgToPaint = tmpCanvas.canvas; paintWidth = newWidth; paintHeight = newHeight; tmpCanvasId = tmpCanvasId === 'prescale1' ? 'prescale2' : 'prescale1'; @@ -5191,17 +5218,17 @@ var CanvasGraphics = (function CanvasGra }, paintInlineImageXObjectGroup: function CanvasGraphics_paintInlineImageXObjectGroup(imgData, map) { var ctx = this.ctx; var w = imgData.width; var h = imgData.height; - var tmpCanvas = CachedCanvases.getCanvas('inlineImage', w, h); + var tmpCanvas = this.cachedCanvases.getCanvas('inlineImage', w, h); var tmpCtx = tmpCanvas.context; putBinaryImageData(tmpCtx, imgData); for (var i = 0, ii = map.length; i < ii; i++) { var entry = map[i]; ctx.save(); ctx.transform.apply(ctx, entry.transform); ctx.scale(1, -1); @@ -5849,17 +5876,17 @@ var createMeshCanvas = (function createM break; default: error('illigal figure'); break; } } function createMeshCanvas(bounds, combinesScale, coords, colors, figures, - backgroundColor) { + backgroundColor, cachedCanvases) { // we will increase scale on some weird factor to let antialiasing take // care of "rough" edges var EXPECTED_SCALE = 1.1; // MAX_PATTERN_SIZE is used to avoid OOM situation. var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough var offsetX = Math.floor(bounds[0]); var offsetY = Math.floor(bounds[1]); @@ -5883,21 +5910,21 @@ var createMeshCanvas = (function createM }; var canvas, tmpCanvas, i, ii; if (WebGLUtils.isEnabled) { canvas = WebGLUtils.drawFigures(width, height, backgroundColor, figures, context); // https://bugzilla.mozilla.org/show_bug.cgi?id=972126 - tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false); + tmpCanvas = cachedCanvases.getCanvas('mesh', width, height, false); tmpCanvas.context.drawImage(canvas, 0, 0); canvas = tmpCanvas.canvas; } else { - tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false); + 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]; @@ -5943,17 +5970,18 @@ ShadingIRs.Mesh = { scale[1] * matrixScale[1]]; } } // Rasterizing on the main thread since sending/queue large canvases // might cause OOM. var temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords, - colors, figures, shadingFill ? null : background); + colors, figures, shadingFill ? null : background, + owner.cachedCanvases); if (!shadingFill) { ctx.setTransform.apply(ctx, owner.baseTransform); if (matrix) { ctx.transform.apply(ctx, matrix); } } @@ -6046,17 +6074,18 @@ var TilingPattern = (function TilingPatt // result when the pattern is used. Too low value makes the pattern look // blurry. Too large value makes it look too crispy. width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])), MAX_PATTERN_SIZE); height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])), MAX_PATTERN_SIZE); - var tmpCanvas = CachedCanvases.getCanvas('pattern', width, height, true); + var tmpCanvas = owner.cachedCanvases.getCanvas('pattern', + width, height, true); var tmpCtx = tmpCanvas.context; var graphics = new CanvasGraphics(tmpCtx, commonObjs, objs); graphics.groupLevel = owner.groupLevel; this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color); this.setScale(width, height, xstep, ystep); this.transformToScale(graphics); @@ -6249,16 +6278,73 @@ var FontFaceObject = (function FontFaceO } return this.compiledGlyphs[character]; } }; return FontFaceObject; })(); +/** + * Optimised CSS custom property getter/setter. + * @class + */ +var CustomStyle = (function CustomStyleClosure() { + + // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ + // animate-css-transforms-firefox-webkit.html + // in some versions of IE9 it is critical that ms appear in this list + // before Moz + var prefixes = ['ms', 'Moz', 'Webkit', 'O']; + var _cache = {}; + + function CustomStyle() {} + + CustomStyle.getProp = function get(propName, element) { + // check cache only when no element is given + if (arguments.length === 1 && typeof _cache[propName] === 'string') { + return _cache[propName]; + } + + element = element || document.documentElement; + var style = element.style, prefixed, uPropName; + + // test standard property first + if (typeof style[propName] === 'string') { + return (_cache[propName] = propName); + } + + // capitalize + uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); + + // test vendor specific properties + for (var i = 0, l = prefixes.length; i < l; i++) { + prefixed = prefixes[i] + uPropName; + if (typeof style[prefixed] === 'string') { + return (_cache[propName] = prefixed); + } + } + + //if all fails then set to undefined + return (_cache[propName] = 'undefined'); + }; + + CustomStyle.setProp = function set(propName, element, str) { + var prop = this.getProp(propName); + if (prop !== 'undefined') { + element.style[prop] = str; + } + }; + + return CustomStyle; +})(); + +PDFJS.CustomStyle = CustomStyle; + + var ANNOT_MIN_SIZE = 10; // px var AnnotationUtils = (function AnnotationUtilsClosure() { // TODO(mack): This dupes some of the logic in CanvasGraphics.setFont() function setTextStyles(element, item, fontObj) { var style = element.style; style.fontSize = item.fontSize + 'px'; @@ -6521,14 +6607,233 @@ var AnnotationUtils = (function Annotati return { getHtmlElement: getHtmlElement }; })(); PDFJS.AnnotationUtils = AnnotationUtils; +/** + * Text layer render parameters. + * + * @typedef {Object} TextLayerRenderParameters + * @property {TextContent} textContent - Text content to render (the object is + * returned by the page's getTextContent() method). + * @property {HTMLElement} container - HTML element that will contain text runs. + * @property {PDFJS.PageViewport} viewport - The target viewport to properly + * layout the text runs. + * @property {Array} textDivs - (optional) HTML elements that are correspond + * the text items of the textContent input. This is output and shall be + * initially be set to empty array. + * @property {number} timeout - (optional) Delay in milliseconds before + * rendering of the text runs occurs. + */ +var renderTextLayer = (function renderTextLayerClosure() { + var MAX_TEXT_DIVS_TO_RENDER = 100000; + + var NonWhitespaceRegexp = /\S/; + + function isAllWhitespace(str) { + return !NonWhitespaceRegexp.test(str); + } + + function appendText(textDivs, viewport, geom, styles) { + var style = styles[geom.fontName]; + var textDiv = document.createElement('div'); + textDivs.push(textDiv); + if (isAllWhitespace(geom.str)) { + textDiv.dataset.isWhitespace = true; + return; + } + var tx = PDFJS.Util.transform(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 = fontHeight; + if (style.ascent) { + fontAscent = style.ascent * fontAscent; + } else if (style.descent) { + fontAscent = (1 + style.descent) * fontAscent; + } + + var left; + var top; + if (angle === 0) { + left = tx[4]; + top = tx[5] - fontAscent; + } else { + left = tx[4] + (fontAscent * Math.sin(angle)); + top = tx[5] - (fontAscent * Math.cos(angle)); + } + textDiv.style.left = left + 'px'; + textDiv.style.top = top + 'px'; + textDiv.style.fontSize = fontHeight + 'px'; + textDiv.style.fontFamily = style.fontFamily; + + textDiv.textContent = geom.str; + // |fontName| is only used by the Font Inspector. This test will succeed + // when e.g. the Font Inspector is off but the Stepper is on, but it's + // not worth the effort to do a more accurate test. + if (PDFJS.pdfBug) { + textDiv.dataset.fontName = geom.fontName; + } + // Storing into dataset will convert number into string. + if (angle !== 0) { + textDiv.dataset.angle = angle * (180 / Math.PI); + } + // We don't bother scaling single-char text divs, because it has very + // little effect on text highlighting. This makes scrolling on docs with + // lots of such divs a lot faster. + if (geom.str.length > 1) { + if (style.vertical) { + textDiv.dataset.canvasWidth = geom.height * viewport.scale; + } else { + textDiv.dataset.canvasWidth = geom.width * viewport.scale; + } + } + } + + function render(task) { + if (task._canceled) { + return; + } + var textLayerFrag = task._container; + var textDivs = task._textDivs; + var capability = task._capability; + var textDivsLength = textDivs.length; + + // No point in rendering many divs as it would make the browser + // unusable even after the divs are rendered. + if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) { + capability.resolve(); + return; + } + + var canvas = document.createElement('canvas'); + canvas.mozOpaque = true; + var ctx = canvas.getContext('2d', {alpha: false}); + + var lastFontSize; + var lastFontFamily; + for (var i = 0; i < textDivsLength; i++) { + var textDiv = textDivs[i]; + if (textDiv.dataset.isWhitespace !== undefined) { + continue; + } + + var fontSize = textDiv.style.fontSize; + var fontFamily = textDiv.style.fontFamily; + + // Only build font string and set to context if different from last. + if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) { + ctx.font = fontSize + ' ' + fontFamily; + lastFontSize = fontSize; + lastFontFamily = fontFamily; + } + + var width = ctx.measureText(textDiv.textContent).width; + if (width > 0) { + textLayerFrag.appendChild(textDiv); + var transform; + if (textDiv.dataset.canvasWidth !== undefined) { + // Dataset values come of type string. + var textScale = textDiv.dataset.canvasWidth / width; + transform = 'scaleX(' + textScale + ')'; + } else { + transform = ''; + } + var rotation = textDiv.dataset.angle; + if (rotation) { + transform = 'rotate(' + rotation + 'deg) ' + transform; + } + if (transform) { + PDFJS.CustomStyle.setProp('transform' , textDiv, transform); + } + } + } + capability.resolve(); + } + + /** + * Text layer rendering task. + * + * @param {TextContent} textContent + * @param {HTMLElement} container + * @param {PDFJS.PageViewport} viewport + * @param {Array} textDivs + * @private + */ + function TextLayerRenderTask(textContent, container, viewport, textDivs) { + this._textContent = textContent; + this._container = container; + this._viewport = viewport; + textDivs = textDivs || []; + this._textDivs = textDivs; + this._canceled = false; + this._capability = createPromiseCapability(); + this._renderTimer = null; + } + TextLayerRenderTask.prototype = { + get promise() { + return this._capability.promise; + }, + + cancel: function TextLayer_cancel() { + this._canceled = true; + if (this._renderTimer !== null) { + clearTimeout(this._renderTimer); + this._renderTimer = null; + } + this._capability.reject('canceled'); + }, + + _render: function TextLayer_render(timeout) { + var textItems = this._textContent.items; + var styles = this._textContent.styles; + var textDivs = this._textDivs; + var viewport = this._viewport; + for (var i = 0, len = textItems.length; i < len; i++) { + appendText(textDivs, viewport, textItems[i], styles); + } + + if (!timeout) { // Render right away + render(this); + } else { // Schedule + var self = this; + this._renderTimer = setTimeout(function() { + render(self); + self._renderTimer = null; + }, timeout); + } + } + }; + + + /** + * Starts rendering of the text layer. + * + * @param {TextLayerRenderParameters} renderParameters + * @returns {TextLayerRenderTask} + */ + function renderTextLayer(renderParameters) { + var task = new TextLayerRenderTask(renderParameters.textContent, + renderParameters.container, + renderParameters.viewport, + renderParameters.textDivs); + task._render(renderParameters.timeout); + return task; + } + + return renderTextLayer; +})(); + +PDFJS.renderTextLayer = renderTextLayer; + + }).call((typeof window === 'undefined') ? this : window);
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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 * @@ -17,18 +15,18 @@ /*jshint globalstrict: false */ /* globals PDFJS */ // Initializing PDFJS global object (if still undefined) if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.2.109'; -PDFJS.build = '875588d'; +PDFJS.version = '1.3.14'; +PDFJS.build = 'df46b64'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it 'use strict'; var globalScope = (typeof window === 'undefined') ? this : window; @@ -9659,16 +9657,21 @@ Shadings.RadialAxial = (function RadialA } else { error('getPattern type unknown: ' + shadingType); } var matrix = this.matrix; if (matrix) { p0 = Util.applyTransform(p0, matrix); p1 = Util.applyTransform(p1, matrix); + if (shadingType === ShadingType.RADIAL) { + var scale = Util.singularValueDecompose2dScale(matrix); + r0 *= scale[0]; + r1 *= scale[1]; + } } return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1]; } }; return RadialAxial; })(); @@ -34550,16 +34553,17 @@ var ArithmeticDecoder = (function Arithm return d; } }; return ArithmeticDecoder; })(); + var JpegImage = (function jpegImage() { var dctZigZag = new Uint8Array([ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, @@ -39228,16 +39232,17 @@ var bidi = PDFJS.bidi = (function bidiCl } return createBidiText(chars.join(''), isLTR); } return bidi; })(); + 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;
--- a/browser/extensions/pdfjs/content/network.js +++ b/browser/extensions/pdfjs/content/network.js @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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 *
--- a/browser/extensions/pdfjs/content/pdfjschildbootstrap.js +++ b/browser/extensions/pdfjs/content/pdfjschildbootstrap.js @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2014 Mozilla Foundation * * 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 *
--- a/browser/extensions/pdfjs/content/web/debugger.js +++ b/browser/extensions/pdfjs/content/web/debugger.js @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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 *
--- a/browser/extensions/pdfjs/content/web/l10n.js +++ b/browser/extensions/pdfjs/content/web/l10n.js @@ -1,10 +1,8 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* globals FirefoxCom */ 'use strict'; // Small subset of the webL10n API by Fabien Cazenave for pdf.js extension. (function(window) { var gLanguage = '';
--- a/browser/extensions/pdfjs/content/web/viewer.css +++ b/browser/extensions/pdfjs/content/web/viewer.css @@ -1569,17 +1569,35 @@ html[dir='rtl'] #documentPropertiesOverl } #PDFBug .stats .title { font-weight: bold; } #PDFBug table { font-size: 10px; } -#viewer.textLayer-visible .textLayer > div, +#viewer.textLayer-visible .textLayer { + opacity: 1.0; +} + +#viewer.textLayer-visible .canvasWrapper { + background-color: rgb(128,255,128); +} + +#viewer.textLayer-visible .canvasWrapper canvas { + mix-blend-mode: screen; +} + +#viewer.textLayer-visible .textLayer > div { + background-color: rgba(255, 255, 0, 0.1); + color: black; + border: solid 1px rgba(255, 0, 0, 0.5); + box-sizing: border-box; +} + #viewer.textLayer-hover .textLayer > div:hover { background-color: white; color: black; } #viewer.textLayer-shadow .textLayer > div { background-color: rgba(255,255,255, .6); color: black;
--- a/browser/extensions/pdfjs/content/web/viewer.js +++ b/browser/extensions/pdfjs/content/web/viewer.js @@ -1,36 +1,22 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ /* Copyright 2012 Mozilla Foundation * * 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. */ -/* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, ProgressBar, - DownloadManager, getFileName, getPDFFileNameFromURL, - PDFHistory, Preferences, SidebarView, ViewHistory, Stats, - PDFThumbnailViewer, URL, noContextMenuHandler, SecondaryToolbar, - PasswordPrompt, PDFPresentationMode, PDFDocumentProperties, HandTool, - Promise, PDFLinkService, PDFOutlineView, PDFAttachmentView, - OverlayManager, PDFFindController, PDFFindBar, PDFViewer, - PDFRenderingQueue, PresentationModeState, parseQueryString, - RenderingStates, UNKNOWN_SCALE, DEFAULT_SCALE_VALUE, - IGNORE_CURRENT_POSITION_ON_ZOOM: true */ - -'use strict'; var DEFAULT_URL = 'compressed.tracemonkey-pldi-09.pdf'; var DEFAULT_SCALE_DELTA = 1.1; var MIN_SCALE = 0.25; var MAX_SCALE = 10.0; var SCALE_SELECT_CONTAINER_PADDING = 8; var SCALE_SELECT_PADDING = 22; var PAGE_NUMBER_LOADING_INDICATOR = 'visiblePageIsLoading'; @@ -47,67 +33,16 @@ var mozL10n = document.mozL10n || docume var CSS_UNITS = 96.0 / 72.0; var DEFAULT_SCALE_VALUE = 'auto'; var DEFAULT_SCALE = 1.0; var UNKNOWN_SCALE = 0; var MAX_AUTO_SCALE = 1.25; var SCROLLBAR_PADDING = 40; var VERTICAL_PADDING = 5; -// optimised CSS custom property getter/setter -var CustomStyle = (function CustomStyleClosure() { - - // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ - // animate-css-transforms-firefox-webkit.html - // in some versions of IE9 it is critical that ms appear in this list - // before Moz - var prefixes = ['ms', 'Moz', 'Webkit', 'O']; - var _cache = {}; - - function CustomStyle() {} - - CustomStyle.getProp = function get(propName, element) { - // check cache only when no element is given - if (arguments.length === 1 && typeof _cache[propName] === 'string') { - return _cache[propName]; - } - - element = element || document.documentElement; - var style = element.style, prefixed, uPropName; - - // test standard property first - if (typeof style[propName] === 'string') { - return (_cache[propName] = propName); - } - - // capitalize - uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); - - // test vendor specific properties - for (var i = 0, l = prefixes.length; i < l; i++) { - prefixed = prefixes[i] + uPropName; - if (typeof style[prefixed] === 'string') { - return (_cache[propName] = prefixed); - } - } - - //if all fails then set to undefined - return (_cache[propName] = 'undefined'); - }; - - CustomStyle.setProp = function set(propName, element, str) { - var prop = this.getProp(propName); - if (prop !== 'undefined') { - element.style[prop] = str; - } - }; - - return CustomStyle; -})(); - var NullCharactersRegExp = /\x00/g; function removeNullCharacters(str) { return str.replace(NullCharactersRegExp, ''); } function getFileName(url) { var anchor = url.indexOf('#'); @@ -2634,33 +2569,16 @@ var PDFPresentationMode = (function PDFP delete this.fullscreenChangeBind; } }; return PDFPresentationMode; })(); -/* Copyright 2013 Rob Wu <gwnRob@gmail.com> - * https://github.com/Rob--W/grab-to-pan.js - * - * 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. - */ - -'use strict'; var GrabToPan = (function GrabToPanClosure() { /** * Construct a GrabToPan instance for a given HTML element. * @param options.element {Element} * @param options.ignoreTarget {function} optional. See `ignoreTarget(node)` * @param options.onActiveChanged {function(boolean)} optional. Called * when grab-to-pan is (de)activated. The first argument is a boolean that @@ -3513,16 +3431,18 @@ var TEXT_LAYER_RENDER_DELAY = 200; // ms * @property {IPDFAnnotationsLayerFactory} annotationsLayerFactory */ /** * @class * @implements {IRenderableView} */ var PDFPageView = (function PDFPageViewClosure() { + var CustomStyle = PDFJS.CustomStyle; + /** * @constructs PDFPageView * @param {PDFPageViewOptions} options */ function PDFPageView(options) { var container = options.container; var id = options.id; var scale = options.scale; @@ -3530,17 +3450,17 @@ var PDFPageView = (function PDFPageViewC var renderingQueue = options.renderingQueue; var textLayerFactory = options.textLayerFactory; var annotationsLayerFactory = options.annotationsLayerFactory; this.id = id; this.renderingId = 'page' + id; this.rotation = 0; - this.scale = scale || 1.0; + this.scale = scale || DEFAULT_SCALE; this.viewport = defaultViewport; this.pdfPageRotate = defaultViewport.rotation; this.hasRestrictedScaling = false; this.renderingQueue = renderingQueue; this.textLayerFactory = textLayerFactory; this.annotationsLayerFactory = annotationsLayerFactory; @@ -3641,18 +3561,17 @@ var PDFPageView = (function PDFPageViewC var totalRotation = (this.rotation + this.pdfPageRotate) % 360; this.viewport = this.viewport.clone({ scale: this.scale * CSS_UNITS, rotation: totalRotation }); var isScalingRestricted = false; if (this.canvas && PDFJS.maxCanvasPixels > 0) { - var ctx = this.canvas.getContext('2d'); - var outputScale = getOutputScale(ctx); + var outputScale = this.outputScale; var pixelsInViewport = this.viewport.width * this.viewport.height; var maxScale = Math.sqrt(PDFJS.maxCanvasPixels / pixelsInViewport); if (((Math.floor(this.viewport.width) * outputScale.sx) | 0) * ((Math.floor(this.viewport.height) * outputScale.sy) | 0) > PDFJS.maxCanvasPixels) { isScalingRestricted = true; } } @@ -3786,27 +3705,34 @@ var PDFPageView = (function PDFPageViewC // will be hidden in FF. var canvasWrapper = document.createElement('div'); canvasWrapper.style.width = div.style.width; canvasWrapper.style.height = div.style.height; canvasWrapper.classList.add('canvasWrapper'); var canvas = document.createElement('canvas'); canvas.id = 'page' + this.id; + // Keep the canvas hidden until the first draw callback, or until drawing + // is complete when `!this.renderingQueue`, to prevent black flickering. + canvas.setAttribute('hidden', 'hidden'); + var isCanvasHidden = true; + canvasWrapper.appendChild(canvas); if (this.annotationLayer && this.annotationLayer.div) { // annotationLayer needs to stay on top div.insertBefore(canvasWrapper, this.annotationLayer.div); } else { div.appendChild(canvasWrapper); } this.canvas = canvas; - var ctx = canvas.getContext('2d'); + canvas.mozOpaque = true; + var ctx = canvas.getContext('2d', {alpha: false}); var outputScale = getOutputScale(ctx); + this.outputScale = outputScale; if (PDFJS.useOnlyCssZoom) { var actualSizeViewport = viewport.clone({scale: CSS_UNITS}); // Use a scale that will make the canvas be the original intended size // of the page. outputScale.sx *= actualSizeViewport.width / viewport.width; outputScale.sy *= actualSizeViewport.height / viewport.height; outputScale.scaled = true; @@ -3849,20 +3775,16 @@ var PDFPageView = (function PDFPageViewC } textLayer = this.textLayerFactory.createTextLayerBuilder(textLayerDiv, this.id - 1, this.viewport); } this.textLayer = textLayer; - if (outputScale.scaled) { - ctx.scale(outputScale.sx, outputScale.sy); - } - var resolveRenderPromise, rejectRenderPromise; var promise = new Promise(function (resolve, reject) { resolveRenderPromise = resolve; rejectRenderPromise = reject; }); // Rendering area @@ -3877,16 +3799,21 @@ var PDFPageView = (function PDFPageViewC if (error === 'cancelled') { rejectRenderPromise(error); return; } self.renderingState = RenderingStates.FINISHED; + if (isCanvasHidden) { + self.canvas.removeAttribute('hidden'); + isCanvasHidden = false; + } + if (self.loadingIconDiv) { div.removeChild(self.loadingIconDiv); delete self.loadingIconDiv; } if (self.zoomLayer) { // Zeroing the width and height causes Firefox to release graphics // resources immediately, which can greatly reduce memory consumption. @@ -3923,22 +3850,29 @@ var PDFPageView = (function PDFPageViewC if (!self.renderingQueue.isHighestPriority(self)) { self.renderingState = RenderingStates.PAUSED; self.resume = function resumeCallback() { self.renderingState = RenderingStates.RUNNING; cont(); }; return; } + if (isCanvasHidden) { + self.canvas.removeAttribute('hidden'); + isCanvasHidden = false; + } cont(); }; } + var transform = !outputScale.scaled ? null : + [outputScale.sx, 0, 0, outputScale.sy, 0, 0]; var renderContext = { canvasContext: ctx, + transform: transform, viewport: this.viewport, // intent: 'default', // === 'display' }; var renderTask = this.renderTask = this.pdfPage.render(renderContext); renderTask.onContinue = renderContinueCallback; this.renderTask.promise.then( function pdfPageRenderCallback() { @@ -4032,24 +3966,16 @@ var PDFPageView = (function PDFPageViewC }; }, }; return PDFPageView; })(); -var MAX_TEXT_DIVS_TO_RENDER = 100000; - -var NonWhitespaceRegexp = /\S/; - -function isAllWhitespace(str) { - return !NonWhitespaceRegexp.test(str); -} - /** * @typedef {Object} TextLayerBuilderOptions * @property {HTMLDivElement} textLayerDiv - The text layer container. * @property {number} pageIndex - The page index. * @property {PageViewport} viewport - The viewport of the text layer. * @property {PDFFindController} findController */ @@ -4066,16 +3992,17 @@ var TextLayerBuilder = (function TextLay this.renderingDone = false; this.divContentDone = false; this.pageIdx = options.pageIndex; this.pageNumber = this.pageIdx + 1; this.matches = []; this.viewport = options.viewport; this.textDivs = []; this.findController = options.findController || null; + this.textLayerRenderTask = null; this._bindMouse(); } TextLayerBuilder.prototype = { _finishRendering: function TextLayerBuilder_finishRendering() { this.renderingDone = true; var endOfContent = document.createElement('div'); @@ -4084,165 +4011,55 @@ var TextLayerBuilder = (function TextLay var event = document.createEvent('CustomEvent'); event.initCustomEvent('textlayerrendered', true, true, { pageNumber: this.pageNumber }); this.textLayerDiv.dispatchEvent(event); }, - renderLayer: function TextLayerBuilder_renderLayer() { - var textLayerFrag = document.createDocumentFragment(); - var textDivs = this.textDivs; - var textDivsLength = textDivs.length; - var canvas = document.createElement('canvas'); - var ctx = canvas.getContext('2d'); - - // No point in rendering many divs as it would make the browser - // unusable even after the divs are rendered. - if (textDivsLength > MAX_TEXT_DIVS_TO_RENDER) { - this._finishRendering(); - return; - } - - var lastFontSize; - var lastFontFamily; - for (var i = 0; i < textDivsLength; i++) { - var textDiv = textDivs[i]; - if (textDiv.dataset.isWhitespace !== undefined) { - continue; - } - - var fontSize = textDiv.style.fontSize; - var fontFamily = textDiv.style.fontFamily; - - // Only build font string and set to context if different from last. - if (fontSize !== lastFontSize || fontFamily !== lastFontFamily) { - ctx.font = fontSize + ' ' + fontFamily; - lastFontSize = fontSize; - lastFontFamily = fontFamily; - } - - var width = ctx.measureText(textDiv.textContent).width; - if (width > 0) { - textLayerFrag.appendChild(textDiv); - var transform; - if (textDiv.dataset.canvasWidth !== undefined) { - // Dataset values come of type string. - var textScale = textDiv.dataset.canvasWidth / width; - transform = 'scaleX(' + textScale + ')'; - } else { - transform = ''; - } - var rotation = textDiv.dataset.angle; - if (rotation) { - transform = 'rotate(' + rotation + 'deg) ' + transform; - } - if (transform) { - CustomStyle.setProp('transform' , textDiv, transform); - } - } - } - - this.textLayerDiv.appendChild(textLayerFrag); - this._finishRendering(); - this.updateMatches(); - }, - /** * Renders the text layer. * @param {number} timeout (optional) if specified, the rendering waits * for specified amount of ms. */ render: function TextLayerBuilder_render(timeout) { if (!this.divContentDone || this.renderingDone) { return; } - if (this.renderTimer) { - clearTimeout(this.renderTimer); - this.renderTimer = null; - } - - if (!timeout) { // Render right away - this.renderLayer(); - } else { // Schedule - var self = this; - this.renderTimer = setTimeout(function() { - self.renderLayer(); - self.renderTimer = null; - }, timeout); - } - }, - - appendText: function TextLayerBuilder_appendText(geom, styles) { - var style = styles[geom.fontName]; - var textDiv = document.createElement('div'); - this.textDivs.push(textDiv); - if (isAllWhitespace(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 = fontHeight; - if (style.ascent) { - fontAscent = style.ascent * fontAscent; - } else if (style.descent) { - fontAscent = (1 + style.descent) * fontAscent; - } - - var left; - var top; - if (angle === 0) { - left = tx[4]; - top = tx[5] - fontAscent; - } else { - left = tx[4] + (fontAscent * Math.sin(angle)); - top = tx[5] - (fontAscent * Math.cos(angle)); - } - textDiv.style.left = left + 'px'; - textDiv.style.top = top + 'px'; - textDiv.style.fontSize = fontHeight + 'px'; - textDiv.style.fontFamily = style.fontFamily; - - textDiv.textContent = geom.str; - // |fontName| is only used by the Font Inspector. This test will succeed - // when e.g. the Font Inspector is off but the Stepper is on, but it's - // not worth the effort to do a more accurate test. - if (PDFJS.pdfBug) { - textDiv.dataset.fontName = geom.fontName; - } - // Storing into dataset will convert number into string. - if (angle !== 0) { - textDiv.dataset.angle = angle * (180 / Math.PI); - } - // We don't bother scaling single-char text divs, because it has very - // little effect on text highlighting. This makes scrolling on docs with - // lots of such divs a lot faster. - if (geom.str.length > 1) { - if (style.vertical) { - textDiv.dataset.canvasWidth = geom.height * this.viewport.scale; - } else { - textDiv.dataset.canvasWidth = geom.width * this.viewport.scale; - } - } + if (this.textLayerRenderTask) { + this.textLayerRenderTask.cancel(); + this.textLayerRenderTask = null; + } + + this.textDivs = []; + var textLayerFrag = document.createDocumentFragment(); + this.textLayerRenderTask = PDFJS.renderTextLayer({ + textContent: this.textContent, + container: textLayerFrag, + viewport: this.viewport, + textDivs: this.textDivs, + timeout: timeout + }); + this.textLayerRenderTask.promise.then(function () { + this.textLayerDiv.appendChild(textLayerFrag); + this._finishRendering(); + this.updateMatches(); + }.bind(this), function (reason) { + // canceled or failed to render text layer -- skipping errors + }); }, setTextContent: function TextLayerBuilder_setTextContent(textContent) { + if (this.textLayerRenderTask) { + this.textLayerRenderTask.cancel(); + this.textLayerRenderTask = null; + } this.textContent = textContent; - - var textItems = textContent.items; - for (var i = 0, len = textItems.length; i < len; i++) { - this.appendText(textItems[i], textContent.styles); - } this.divContentDone = true; }, convertMatches: function TextLayerBuilder_convertMatches(matches) { var i = 0; var iIndex = 0; var bidiTexts = this.textContent.items; var end = bidiTexts.length - 1; @@ -4473,16 +4290,18 @@ DefaultTextLayerFactory.prototype = { * @property {PDFPage} pdfPage * @property {IPDFLinkService} linkService */ /** * @class */ var AnnotationsLayerBuilder = (function AnnotationsLayerBuilderClosure() { + var CustomStyle = PDFJS.CustomStyle; + /** * @param {AnnotationsLayerBuilderOptions} options * @constructs AnnotationsLayerBuilder */ function AnnotationsLayerBuilder(options) { this.pageDiv = options.pageDiv; this.pdfPage = options.pdfPage; this.linkService = options.linkService; @@ -5437,17 +5256,18 @@ var PDFThumbnailView = (function PDFThum tempCanvas = document.createElement('canvas'); PDFThumbnailView.tempImageCache = tempCanvas; } tempCanvas.width = width; tempCanvas.height = height; // Since this is a temporary canvas, we need to fill the canvas with a white // background ourselves. |_getPageDrawContext| uses CSS rules for this. - var ctx = tempCanvas.getContext('2d'); + tempCanvas.mozOpaque = true; + var ctx = tempCanvas.getContext('2d', {alpha: false}); ctx.save(); ctx.fillStyle = 'rgb(255, 255, 255)'; ctx.fillRect(0, 0, width, height); ctx.restore(); return tempCanvas; } /** @@ -5577,17 +5397,18 @@ var PDFThumbnailView = (function PDFThum /** * @private */ _getPageDrawContext: function PDFThumbnailView_getPageDrawContext(noCtxScale) { var canvas = document.createElement('canvas'); this.canvas = canvas; - var ctx = canvas.getContext('2d'); + canvas.mozOpaque = true; + var ctx = canvas.getContext('2d', {alpha: false}); var outputScale = getOutputScale(ctx); canvas.width = (this.canvasWidth * outputScale.sx) | 0; canvas.height = (this.canvasHeight * outputScale.sy) | 0; canvas.style.width = this.canvasWidth + 'px'; canvas.style.height = this.canvasHeight + 'px'; if (!noCtxScale && outputScale.scaled) {