diff --git a/src/canvas.js b/src/canvas.js index a1c4fb40c..3fd55b45d 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -255,6 +255,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } // Scale so that canvas units are the same as PDF user space units this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height); + // Move the media left-top corner to the (0,0) canvas position + this.ctx.translate(-mediaBox.x, -mediaBox.y); if (this.textLayer) this.textLayer.beginLayout(); @@ -341,6 +343,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { setDash: function canvasGraphicsSetDash(dashArray, dashPhase) { this.ctx.mozDash = dashArray; this.ctx.mozDashOffset = dashPhase; + this.ctx.webkitLineDash = dashArray; + this.ctx.webkitLineDashOffset = dashPhase; }, setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) { TODO('set rendering intent: ' + intent); diff --git a/src/core.js b/src/core.js index 0c1e4b6cb..8b5fbea75 100644 --- a/src/core.js +++ b/src/core.js @@ -70,8 +70,7 @@ var Page = (function PageClosure() { this.xref = xref; this.ref = ref; - this.ctx = null; - this.callback = null; + this.displayReadyPromise = null; } Page.prototype = { @@ -110,9 +109,11 @@ var Page = (function PageClosure() { width: this.width, height: this.height }; + var mediaBox = this.mediaBox; + var offsetX = mediaBox[0], offsetY = mediaBox[1]; if (isArray(obj) && obj.length == 4) { - var tl = this.rotatePoint(obj[0], obj[1]); - var br = this.rotatePoint(obj[2], obj[3]); + var tl = this.rotatePoint(obj[0] - offsetX, obj[1] - offsetY); + var br = this.rotatePoint(obj[2] - offsetX, obj[3] - offsetY); view.x = Math.min(tl.x, br.x); view.y = Math.min(tl.y, br.y); view.width = Math.abs(tl.x - br.x); @@ -165,20 +166,12 @@ var Page = (function PageClosure() { IRQueue, fonts) { var self = this; this.IRQueue = IRQueue; - var gfx = new CanvasGraphics(this.ctx, this.objs, this.textLayer); var displayContinuation = function pageDisplayContinuation() { // Always defer call to display() to work around bug in // Firefox error reporting from XHR callbacks. setTimeout(function pageSetTimeout() { - try { - self.display(gfx, self.callback); - } catch (e) { - if (self.callback) - self.callback(e); - else - throw e; - } + self.displayReadyPromise.resolve(); }); }; @@ -395,12 +388,27 @@ var Page = (function PageClosure() { return items; }, startRendering: function pageStartRendering(ctx, callback, textLayer) { - this.ctx = ctx; - this.callback = callback; - this.textLayer = textLayer; - this.startRenderingTime = Date.now(); - this.pdf.startRendering(this); + + // If there is no displayReadyPromise yet, then the IRQueue was never + // requested before. Make the request and create the promise. + if (!this.displayReadyPromise) { + this.pdf.startRendering(this); + this.displayReadyPromise = new Promise(); + } + + // Once the IRQueue and fonts are loaded, perform the actual rendering. + this.displayReadyPromise.then(function pageDisplayReadyPromise() { + var gfx = new CanvasGraphics(ctx, this.objs, textLayer); + try { + this.display(gfx, callback); + } catch (e) { + if (self.callback) + self.callback(e); + else + throw e; + } + }.bind(this)); } }; diff --git a/src/fonts.js b/src/fonts.js index 83ce4abaa..1b959d6c2 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -2092,7 +2092,7 @@ var Font = (function FontClosure() { window.btoa(data) + ');'); var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}'; - document.documentElement.firstChild.appendChild( + document.documentElement.getElementsByTagName('head')[0].appendChild( document.createElement('style')); var styleSheet = document.styleSheets[document.styleSheets.length - 1]; diff --git a/src/function.js b/src/function.js index 83db53ba1..26b8fe679 100644 --- a/src/function.js +++ b/src/function.js @@ -270,7 +270,6 @@ var PDFFunction = (function PDFFunctionClosure() { constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) { var domain = dict.get('Domain'); - var range = dict.get('Range'); if (!domain) error('No domain'); @@ -279,13 +278,13 @@ var PDFFunction = (function PDFFunctionClosure() { if (inputSize != 1) error('Bad domain for stiched function'); - var fnRefs = dict.get('Functions'); + var fnRefs = xref.fetchIfRef(dict.get('Functions')); var fns = []; for (var i = 0, ii = fnRefs.length; i < ii; ++i) fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i]))); - var bounds = dict.get('Bounds'); - var encode = dict.get('Encode'); + var bounds = xref.fetchIfRef(dict.get('Bounds')); + var encode = xref.fetchIfRef(dict.get('Encode')); return [CONSTRUCT_STICHED, domain, bounds, encode, fns]; }, diff --git a/src/util.js b/src/util.js index 57dbca4bb..4d8918e48 100644 --- a/src/util.js +++ b/src/util.js @@ -284,7 +284,7 @@ var Promise = (function PromiseClosure() { } this.isResolved = true; - this.data = data; + this.data = data || null; var callbacks = this.callbacks; for (var i = 0, ii = callbacks.length; i < ii; i++) { diff --git a/test/pdfs/issue1001.pdf.link b/test/pdfs/issue1001.pdf.link new file mode 100644 index 000000000..24e1bebc2 --- /dev/null +++ b/test/pdfs/issue1001.pdf.link @@ -0,0 +1 @@ +http://www.myhillsapartment.com/island_club/floorplans/images/links/Island_IC_brochure.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 9c722f98f..dbb83604b 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -356,6 +356,13 @@ "rounds": 1, "type": "eq" }, + { "id": "issue1001", + "file": "pdfs/issue1001.pdf", + "md5": "0f1496e80a82a923e91d9e74c55ad94e", + "rounds": 1, + "link": true, + "type": "eq" + }, { "id": "aboutstacks", "file": "pdfs/aboutstacks.pdf", "md5": "6e7c8416a293ba2d83bc8dd20c6ccf51", diff --git a/web/compatibility.js b/web/compatibility.js index e4e2f2440..66e46b51b 100644 --- a/web/compatibility.js +++ b/web/compatibility.js @@ -5,8 +5,13 @@ // Checking if the typed arrays are supported (function checkTypedArrayCompatibility() { - if (typeof Uint8Array !== 'undefined') + if (typeof Uint8Array !== 'undefined') { + // some mobile version might not support Float64Array + if (typeof Float64Array === 'undefined') + window.Float64Array = Float32Array; + return; + } function subarray(start, end) { return this.slice(start, end); @@ -46,6 +51,8 @@ window.Uint32Array = TypedArray; window.Int32Array = TypedArray; window.Uint16Array = TypedArray; + window.Float32Array = TypedArray; + window.Float64Array = TypedArray; })(); // Object.create() ? diff --git a/web/viewer.html b/web/viewer.html index 53ca2a247..869bf0f2e 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -67,10 +67,11 @@ - + +
diff --git a/web/viewer.js b/web/viewer.js index 9a7bbb527..70ac8c3ec 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -4,7 +4,7 @@ 'use strict'; var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf'; -var kDefaultScale = 1.5; +var kDefaultScale = 'auto'; var kDefaultScaleDelta = 1.1; var kCacheSize = 20; var kCssUnits = 96.0 / 72.0; @@ -146,9 +146,13 @@ var PDFView = { pages: [], thumbnails: [], currentScale: 0, + currentScaleValue: null, initialBookmark: document.location.hash.substring(1), setScale: function pdfViewSetScale(val, resetAutoSettings) { + if (val == this.currentScale) + return; + var pages = this.pages; for (var i = 0; i < pages.length; i++) pages[i].update(val * kCssUnits); @@ -169,6 +173,7 @@ var PDFView = { return; var scale = parseFloat(value); + this.currentScaleValue = value; if (scale) { this.setScale(scale, true); return; @@ -187,6 +192,10 @@ var PDFView = { this.setScale( Math.min(pageWidthScale, pageHeightScale), resetAutoSettings); } + if ('auto' == value) + this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings); + + selectScaleOption(value); }, zoomIn: function pdfViewZoomIn() { @@ -463,8 +472,16 @@ var PDFView = { if ('zoom' in params) { var zoomArgs = params.zoom.split(','); // scale,left,top // building destination array + + // If the zoom value, it has to get divided by 100. If it is a string, + // it should stay as it is. + var zoomArg = zoomArgs[0]; + var zoomArgNumber = parseFloat(zoomArg); + if (zoomArgNumber) + zoomArg = zoomArgNumber / 100; + var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0), - (zoomArgs[2] | 0), (zoomArgs[0] | 0) / 100]; + (zoomArgs[2] | 0), zoomArg]; var currentPage = this.pages[pageNumber - 1]; currentPage.scrollIntoView(dest); } else @@ -999,10 +1016,15 @@ function updateViewarea() { PDFView.page = firstPage.id; updateViewarea.inProgress = false; + var currentScale = PDFView.currentScale; + var currentScaleValue = PDFView.currentScaleValue; + var normalizedScaleValue = currentScaleValue == currentScale ? + currentScale * 100 : currentScaleValue; + var kViewerTopMargin = 52; var pageNumber = firstPage.id; var pdfOpenParams = '#page=' + pageNumber; - pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100); + pdfOpenParams += '&zoom=' + normalizedScaleValue; var currentPage = PDFView.pages[pageNumber - 1]; var topLeft = currentPage.getPagePoint(window.pageXOffset, window.pageYOffset - firstPage.y - kViewerTopMargin); @@ -1011,7 +1033,7 @@ function updateViewarea() { var store = PDFView.store; store.set('exists', true); store.set('page', pageNumber); - store.set('zoom', Math.round(PDFView.currentScale * 100)); + store.set('zoom', normalizedScaleValue); store.set('scrollLeft', Math.round(topLeft.x)); store.set('scrollTop', Math.round(topLeft.y)); @@ -1047,7 +1069,8 @@ window.addEventListener('webkitTransitionEnd', updateThumbViewArea, true); window.addEventListener('resize', function webViewerResize(evt) { if (document.getElementById('pageWidthOption').selected || - document.getElementById('pageFitOption').selected) + document.getElementById('pageFitOption').selected || + document.getElementById('pageAutoOption').selected) PDFView.parseScale(document.getElementById('scaleSelect').value); updateViewarea(); }); @@ -1084,20 +1107,9 @@ window.addEventListener('change', function webViewerChange(evt) { document.getElementById('download').setAttribute('hidden', 'true'); }, true); -window.addEventListener('scalechange', function scalechange(evt) { - var customScaleOption = document.getElementById('customScaleOption'); - customScaleOption.selected = false; - - if (!evt.resetAutoSettings && - (document.getElementById('pageWidthOption').selected || - document.getElementById('pageFitOption').selected)) { - updateViewarea(); - return; - } - +function selectScaleOption(value) { var options = document.getElementById('scaleSelect').options; var predefinedValueFound = false; - var value = '' + evt.scale; for (var i = 0; i < options.length; i++) { var option = options[i]; if (option.value != value) { @@ -1107,7 +1119,21 @@ window.addEventListener('scalechange', function scalechange(evt) { option.selected = true; predefinedValueFound = true; } + return predefinedValueFound; +} + +window.addEventListener('scalechange', function scalechange(evt) { + var customScaleOption = document.getElementById('customScaleOption'); + customScaleOption.selected = false; + + if (!evt.resetAutoSettings && + (document.getElementById('pageWidthOption').selected || + document.getElementById('pageFitOption').selected)) { + updateViewarea(); + return; + } + var predefinedValueFound = selectScaleOption('' + evt.scale); if (!predefinedValueFound) { customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%'; customScaleOption.selected = true;