From bd6d89e1a861db721d06c2dc1cf39775cc938545 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 11 Jan 2012 16:48:51 -0800 Subject: [PATCH 001/285] Start of the benchmark recording framework. --- src/core.js | 33 +++++++++++++++++--------------- src/util.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ web/viewer.css | 6 ++++++ web/viewer.js | 22 ++++++++++++++------- 4 files changed, 91 insertions(+), 22 deletions(-) diff --git a/src/core.js b/src/core.js index 765a239b7..90fcb09d5 100644 --- a/src/core.js +++ b/src/core.js @@ -60,13 +60,8 @@ var Page = (function PageClosure() { function Page(xref, pageNumber, pageDict, ref) { this.pageNumber = pageNumber; this.pageDict = pageDict; - this.stats = { - create: Date.now(), - compile: 0.0, - fonts: 0.0, - images: 0.0, - render: 0.0 - }; + this.bench = new Bench(); + this.bench.enabled = !!globalScope.PDFJS.enableBench; this.xref = xref; this.ref = ref; @@ -187,6 +182,8 @@ var Page = (function PageClosure() { return this.IRQueue; } + this.bench.time('Build IR Queue'); + var xref = this.xref; var content = xref.fetchIfRef(this.content); var resources = xref.fetchIfRef(this.resources); @@ -201,11 +198,14 @@ var Page = (function PageClosure() { var pe = this.pe = new PartialEvaluator( xref, handler, 'p' + this.pageNumber + '_'); var IRQueue = {}; - return (this.IRQueue = pe.getIRQueue(content, resources, IRQueue, - dependency)); + this.IRQueue = pe.getIRQueue(content, resources, IRQueue, dependency); + + this.bench.timeEnd('Build IR Queue'); + return this.IRQueue; }, ensureFonts: function pageEnsureFonts(fonts, callback) { + this.bench.time('Font Loading'); // Convert the font names to the corresponding font obj. for (var i = 0, ii = fonts.length; i < ii; i++) { fonts[i] = this.objs.objs[fonts[i]].data; @@ -215,7 +215,7 @@ var Page = (function PageClosure() { var fontObjs = FontLoader.bind( fonts, function pageEnsureFontsFontObjs(fontObjs) { - this.stats.fonts = Date.now(); + this.bench.timeEnd('Font Loading'); callback.call(this); }.bind(this), @@ -224,6 +224,8 @@ var Page = (function PageClosure() { }, display: function pageDisplay(gfx, callback) { + var bench = this.bench; + bench.time('Rendering'); var xref = this.xref; var resources = xref.fetchIfRef(this.resources); var mediaBox = xref.fetchIfRef(this.mediaBox); @@ -244,8 +246,9 @@ var Page = (function PageClosure() { function next() { startIdx = gfx.executeIRQueue(IRQueue, startIdx, next); if (startIdx == length) { - self.stats.render = Date.now(); gfx.endDrawing(); + bench.timeEnd('Rendering'); + bench.timeEnd('Overall'); if (callback) callback(); } } @@ -388,15 +391,14 @@ var Page = (function PageClosure() { return items; }, startRendering: function pageStartRendering(ctx, callback, textLayer) { - this.startRenderingTime = Date.now(); - + var bench = this.bench; + bench.time('Overall'); // 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() { @@ -677,7 +679,7 @@ var PDFDoc = (function PDFDocClosure() { var pageNum = data.pageNum; var page = this.pageCache[pageNum]; var depFonts = data.depFonts; - + page.bench.timeEnd('Page Request'); page.startRenderingFromIRQueue(data.IRQueue, depFonts); }, this); @@ -786,6 +788,7 @@ var PDFDoc = (function PDFDocClosure() { startRendering: function pdfDocStartRendering(page) { // The worker might not be ready to receive the page request yet. this.workerReadyPromise.then(function pdfDocStartRenderingThen() { + page.bench.time('Page Request'); this.messageHandler.send('page_request', page.pageNumber + 1); }.bind(this)); }, diff --git a/src/util.js b/src/util.js index 99b422296..80cf937c4 100644 --- a/src/util.js +++ b/src/util.js @@ -338,3 +338,55 @@ var Promise = (function PromiseClosure() { return Promise; })(); +var Bench = (function BenchClosure() { + function rpad(str, pad, length) { + while (str.length < length) + str += pad; + return str; + } + function Bench() { + this.started = {}; + this.times = []; + this.enabled = true; + } + Bench.prototype = { + time: function benchTime(name) { + if (!this.enabled) + return; + if (name in this.started) + throw 'Timer is already running for ' + name; + this.started[name] = Date.now(); + }, + timeEnd: function benchTimeEnd(name) { + if (!this.enabled) + return; + if (!(name in this.started)) + throw 'Timer has not been started for ' + name; + this.times.push({ + 'name': name, + 'start': this.started[name], + 'end': Date.now() + }); + // Remove timer from started so it can be called again. + delete this.started[name]; + }, + toString: function benchToString() { + 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) { + var name = times[i]['name']; + if (name.length > longest) + longest = name.length; + } + for (var 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'; + } + return out; + } + } + return Bench; +})(); diff --git a/web/viewer.css b/web/viewer.css index e355f7fc2..d5c18504c 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -67,6 +67,12 @@ body { span#info { display: none; + position: fixed; + top: 32px; + right: 0px; + font-size: 10px; + white-space: pre; + font-family: courier; } @-moz-document regexp("http:.*debug=1.*") { diff --git a/web/viewer.js b/web/viewer.js index ac3fbff0c..fc7d99cdd 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -222,6 +222,7 @@ var PDFView = { return; } + pages[val - 1].updateStats(); currentPageNumber = val; var event = document.createEvent('UIEvents'); event.initUIEvent('pagechange', false, false, window, 0); @@ -421,7 +422,7 @@ var PDFView = { for (var i = 1; i <= pagesCount; i++) { var page = pdf.getPage(i); var pageView = new PageView(container, page, i, page.width, page.height, - page.stats, this.navigateTo.bind(this)); + page.bench, this.navigateTo.bind(this)); var thumbnailView = new ThumbnailView(sidebar, page, i, page.width / page.height); bindOnAfterDraw(pageView, thumbnailView); @@ -581,7 +582,7 @@ var PDFView = { }; var PageView = function pageView(container, content, id, pageWidth, pageHeight, - stats, navigateTo) { + bench, navigateTo) { this.id = id; this.content = content; @@ -800,11 +801,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, ctx.restore(); ctx.translate(-this.x * scale, -this.y * scale); - stats.begin = Date.now(); this.content.startRendering(ctx, (function pageViewDrawCallback(error) { if (error) PDFView.error('An error occurred while rendering the page.', error); + this.stats = content.bench; this.updateStats(); if (this.onAfterDraw) this.onAfterDraw(); @@ -819,10 +820,12 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, }; this.updateStats = function pageViewUpdateStats() { - var t1 = stats.compile, t2 = stats.fonts, t3 = stats.render; - var str = 'Time to compile/fonts/render: ' + - (t1 - stats.begin) + '/' + (t2 - t1) + '/' + (t3 - t2) + ' ms'; - document.getElementById('info').innerHTML = str; + if (!PDFJS.enableBench || !this.stats || PDFView.page != this.id) + return; + var stats = this.stats; + var statsHtml = 'Page ' + this.id + '\n'; + statsHtml += stats.toString().replace(/\n/g, '
'); + document.getElementById('info').innerHTML = statsHtml; }; }; @@ -1016,6 +1019,11 @@ window.addEventListener('load', function webViewerLoad(evt) { if ('disableTextLayer' in params) PDFJS.disableTextLayer = (params['disableTextLayer'] === 'true'); + if ('enableBench' in params) + PDFJS.enableBench = (params['enableBench'] === 'true'); + if (PDFJS.enableBench) + document.getElementById('info').style.display = 'block'; + var sidebarScrollView = document.getElementById('sidebarScrollView'); sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true); }, true); From ada3f333754771da8fdff6c4d0378fab971a1791 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Thu, 19 Jan 2012 19:25:29 +0100 Subject: [PATCH 002/285] Replace some setScale by parseScale calls to update value --- web/viewer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index ac3fbff0c..5da8b8314 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -203,12 +203,12 @@ var PDFView = { zoomIn: function pdfViewZoomIn() { var newScale = Math.min(kMaxScale, this.currentScale * kDefaultScaleDelta); - this.setScale(newScale, true); + this.parseScale(newScale, true); }, zoomOut: function pdfViewZoomOut() { var newScale = Math.max(kMinScale, this.currentScale / kDefaultScaleDelta); - this.setScale(newScale, true); + this.parseScale(newScale, true); }, set page(val) { @@ -1234,7 +1234,7 @@ window.addEventListener('keydown', function keydown(evt) { handled = true; break; case 48: // '0' - PDFView.setScale(kDefaultScale, true); + PDFView.parseScale(kDefaultScale, true); handled = true; break; case 37: // left arrow From 7f3d5ae6d3e82b3a14129b219a210e794a927188 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 19 Jan 2012 14:19:19 -0500 Subject: [PATCH 003/285] Fix worker message, better error handling --- src/evaluator.js | 11 +++++++---- src/worker.js | 24 ++++++++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/evaluator.js b/src/evaluator.js index 21530f42f..1c277e027 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -159,6 +159,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // a Stream in the main thread. if (translated.file) translated.file = translated.file.getBytes(); + if (translated.properties.file) { + translated.properties.file = + translated.properties.file.getBytes(); + } handler.send('obj', [ loadedName, @@ -779,12 +783,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { dict: baseDict, properties: properties }; - } - - } + } // if (type.name == 'Type3') + } // if (!descriptor) // According to the spec if 'FontDescriptor' is declared, 'FirstChar', - // 'LastChar' and 'Widths' should exists too, but some PDF encoders seems + // 'LastChar' and 'Widths' should exist too, but some PDF encoders seem // to ignore this rule when a variant of a standart font is used. // TODO Fill the width array depending on which of the base font this is // a variant. diff --git a/src/worker.js b/src/worker.js index 4d9dd1bb6..468cce019 100644 --- a/src/worker.js +++ b/src/worker.js @@ -109,11 +109,27 @@ var WorkerMessageHandler = { // Pre compile the pdf page and fetch the fonts/images. IRQueue = page.getIRQueue(handler, dependency); } catch (e) { + var minimumStackMessage = + 'worker.js: while trying to getPage() and getIRQueue()'; + // Turn the error into an obj that can be serialized - e = { - message: typeof e === 'object' ? e.message : e, - stack: typeof e === 'object' ? e.stack : null - }; + if (typeof e === 'string') { + e = { + message: e, + stack: minimumStackMessage + }; + } else if (typeof e === 'object') { + e = { + message: e.message || e.toString(), + stack: e.stack || minimumStackMessage + }; + } else { + e = { + message: 'Unknown exception type: ' + (typeof e), + stack: minimumStackMessage + } + } + handler.send('page_error', { pageNum: pageNum, error: e From 38d28ecb2e81eaeeb319c76719878cb22f7b8ad5 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 19 Jan 2012 16:02:27 -0500 Subject: [PATCH 004/285] Improved error handling/message --- web/viewer.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/viewer.js b/web/viewer.js index ac3fbff0c..9f8b772ab 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -353,8 +353,14 @@ var PDFView = { if (moreInfo) { errorMoreInfo.value += 'Message: ' + moreInfo.message; - if (moreInfo.stack) + if (moreInfo.stack) { errorMoreInfo.value += '\n' + 'Stack: ' + moreInfo.stack; + } else { + if (moreInfo.filename) + errorMoreInfo.value += '\n' + 'File: ' + moreInfo.filename; + if (moreInfo.filename) + errorMoreInfo.value += '\n' + 'Line: ' + moreInfo.lineNumber; + } } errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1; }, From 66eff7a5cb6fea0dbb8737bc40bcc3f7ee23c8c0 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Fri, 20 Jan 2012 14:55:52 -0500 Subject: [PATCH 005/285] more robust fontMatrix parsing, error checking --- src/canvas.js | 10 ++++++++++ src/fonts.js | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/canvas.js b/src/canvas.js index 5ef900861..6ec8076cf 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -551,6 +551,16 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { throw 'Can\'t find font for ' + fontRefName; } + // If any of the diagonal elements of a transformation matrix are null + // ctx.restore() will fail in FF. See bugzilla bug #719844. + if (fontObj.fontMatrix[0] === 0 || + fontObj.fontMatrix[3] === 0 ) { + warn('Invalid font matrix for font ' + fontRefName); + + // Fallback + fontObj.fontMatrix = IDENTITY_MATRIX; + } + var name = fontObj.loadedName || 'sans-serif'; this.current.font = fontObj; diff --git a/src/fonts.js b/src/fonts.js index f96c15458..96a11d1fa 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -2594,7 +2594,15 @@ var Type1Parser = function type1Parser() { while (str[index++] != ']') count++; - var array = str.substr(start, count).split(' '); + str = str.substr(start, count); + + // Trim + str = str.replace(/^\s+/, ''); + str = str.replace(/\s+$/, ''); + // Remove adjacent spaces + str = str.replace(/\s+/g, ' '); + + var array = str.split(' '); for (var i = 0, ii = array.length; i < ii; i++) array[i] = parseFloat(array[i] || 0); return array; From 357f4cc6659ca8ff2dfefcc2878416c11401265a Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Fri, 20 Jan 2012 15:20:25 -0500 Subject: [PATCH 006/285] Clarifying variable role --- src/canvas.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 6ec8076cf..792a09c97 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -703,12 +703,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { if (textSelection) text.geom = this.getTextGeometry(); - var width = 0; + var x = 0; for (var i = 0; i < glyphsLength; ++i) { var glyph = glyphs[i]; if (glyph === null) { // word break - width += wordSpacing; + x += wordSpacing; continue; } @@ -719,28 +719,28 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { default: // other unsupported rendering modes case TextRenderingMode.FILL: case TextRenderingMode.FILL_ADD_TO_PATH: - ctx.fillText(char, width, 0); + ctx.fillText(char, x, 0); break; case TextRenderingMode.STROKE: case TextRenderingMode.STROKE_ADD_TO_PATH: - ctx.strokeText(char, width, 0); + ctx.strokeText(char, x, 0); break; case TextRenderingMode.FILL_STROKE: case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: - ctx.fillText(char, width, 0); - ctx.strokeText(char, width, 0); + ctx.fillText(char, x, 0); + ctx.strokeText(char, x, 0); break; case TextRenderingMode.INVISIBLE: break; } - width += charWidth; + x += charWidth; text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode; text.length++; text.canvasWidth += charWidth; } - current.x += width * textHScale2; + current.x += x * textHScale2; ctx.restore(); } From 86de8aca3368f6aa63cd49ebf68cf0c6d4471e41 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Fri, 20 Jan 2012 16:25:06 -0500 Subject: [PATCH 007/285] Nit --- src/fonts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fonts.js b/src/fonts.js index 96a11d1fa..5d0114476 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -3603,7 +3603,7 @@ var Type2CFF = (function Type2CFFClosure() { dict['cidOperatorPresent'] = true; break; default: - TODO('interpret top dict key'); + TODO('interpret top dict key: ' + key); } } return dict; From edc632e4696c460ea4f76b079187177f2d91c3cd Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 20 Jan 2012 14:48:57 -0800 Subject: [PATCH 008/285] Fix innerHtml warnings. Remove compatibility.js from the extension. --- Makefile | 7 +++---- web/viewer.js | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index c9de61c1c..fe5a3fc42 100644 --- a/Makefile +++ b/Makefile @@ -207,9 +207,8 @@ pages-repo: | $(BUILD_DIR) # copy of the pdf.js source. CONTENT_DIR := content BUILD_NUMBER := `git log --format=oneline $(EXTENSION_BASE_VERSION).. | wc -l | awk '{print $$1}'` -PDF_WEB_FILES = \ +EXTENSION_WEB_FILES = \ web/images \ - web/compatibility.js \ web/viewer.css \ web/viewer.js \ web/viewer-production.html \ @@ -249,7 +248,7 @@ extension: | production @cd extensions/firefox; cp -r $(FIREFOX_EXTENSION_FILES_TO_COPY) ../../$(FIREFOX_BUILD_DIR)/ # Copy a standalone version of pdf.js inside the content directory @cp $(BUILD_TARGET) $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/ - @cp -r $(PDF_WEB_FILES) $(FIREFOX_BUILD_CONTENT)/web/ + @cp -r $(EXTENSION_WEB_FILES) $(FIREFOX_BUILD_CONTENT)/web/ @mv -f $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html $(FIREFOX_BUILD_CONTENT)/web/viewer.html # Update the build version number @sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/install.rdf @@ -272,7 +271,7 @@ extension: | production @cp -R $(CHROME_EXTENSION_FILES) $(CHROME_BUILD_DIR)/ # Copy a standalone version of pdf.js inside the content directory @cp $(BUILD_TARGET) $(CHROME_BUILD_CONTENT)/$(BUILD_DIR)/ - @cp -r $(PDF_WEB_FILES) $(CHROME_BUILD_CONTENT)/web/ + @cp -r $(EXTENSION_WEB_FILES) $(CHROME_BUILD_CONTENT)/web/ @mv -f $(CHROME_BUILD_CONTENT)/web/viewer-production.html $(CHROME_BUILD_CONTENT)/web/viewer.html # Create the crx diff --git a/web/viewer.js b/web/viewer.js index ac3fbff0c..6c72df3d0 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -257,7 +257,7 @@ var PDFView = { }, error: function getPdfError(e) { var loadingIndicator = document.getElementById('loading'); - loadingIndicator.innerHTML = 'Error'; + loadingIndicator.textContent = 'Error'; var moreInfo = { message: 'Unexpected server response of ' + e.target.status + '.' }; @@ -327,7 +327,7 @@ var PDFView = { errorWrapper.removeAttribute('hidden'); var errorMessage = document.getElementById('errorMessage'); - errorMessage.innerHTML = message; + errorMessage.textContent = message; var closeButton = document.getElementById('errorClose'); closeButton.onclick = function() { @@ -362,7 +362,7 @@ var PDFView = { progress: function pdfViewProgress(level) { var percent = Math.round(level * 100); var loadingIndicator = document.getElementById('loading'); - loadingIndicator.innerHTML = 'Loading... ' + percent + '%'; + loadingIndicator.textContent = 'Loading... ' + percent + '%'; }, load: function pdfViewLoad(data, scale) { @@ -402,7 +402,7 @@ var PDFView = { var pagesCount = pdf.numPages; var id = pdf.fingerprint; var storedHash = null; - document.getElementById('numPages').innerHTML = pagesCount; + document.getElementById('numPages').textContent = pagesCount; document.getElementById('pageNumber').max = pagesCount; PDFView.documentFingerprint = id; var store = PDFView.store = new Settings(id); @@ -648,7 +648,15 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, if (!item.content) { content.setAttribute('hidden', true); } else { - text.innerHTML = item.content.replace('\n', '
'); + var e = document.createElement('span'); + var lines = item.content.split('\n'); + for (var i = 0, ii = lines.length; i < ii; ++i) { + var line = lines[i]; + e.appendChild(document.createTextNode(line)); + if (i < (ii - 1)) + e.appendChild(document.createElement('br')); + } + text.appendChild(e); image.addEventListener('mouseover', function annotationImageOver() { this.nextSibling.removeAttribute('hidden'); }, false); @@ -822,7 +830,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, var t1 = stats.compile, t2 = stats.fonts, t3 = stats.render; var str = 'Time to compile/fonts/render: ' + (t1 - stats.begin) + '/' + (t2 - t1) + '/' + (t3 - t2) + ' ms'; - document.getElementById('info').innerHTML = str; + document.getElementById('info').textContent = str; }; }; From 786cccf636a17c8a8644cf5529c0fb67e4c5a4a5 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Fri, 20 Jan 2012 18:41:01 -0500 Subject: [PATCH 009/285] setFont() supports negative size, closes #1049 --- src/canvas.js | 32 ++++++++++++++++++++++++-------- test/pdfs/issue1049.pdf.link | 1 + test/test_manifest.json | 7 +++++++ 3 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 test/pdfs/issue1049.pdf.link diff --git a/src/canvas.js b/src/canvas.js index 792a09c97..b36772fe0 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -551,8 +551,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { throw 'Can\'t find font for ' + fontRefName; } - // If any of the diagonal elements of a transformation matrix are null - // ctx.restore() will fail in FF. See bugzilla bug #719844. + // A valid matrix needs all main diagonal elements to be non-zero + // This also ensures we bypass FF bugzilla bug #719844. if (fontObj.fontMatrix[0] === 0 || fontObj.fontMatrix[3] === 0 ) { warn('Invalid font matrix for font ' + fontRefName); @@ -563,9 +563,23 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var name = fontObj.loadedName || 'sans-serif'; + // Clone fontMatrix so we can manipulate it without affecting original + this.current.fontMatrix = fontObj.fontMatrix.slice(0); + + // The spec for Tf (setFont) says that 'size' specifies the font 'scale', + // and in some docs this can be negative. We implement this in fontMatrix. + if (size < 0) { + size = -size; + this.current.fontMatrix[0] = -fontObj.fontMatrix[0]; + this.current.fontMatrix[3] = -fontObj.fontMatrix[3]; + } + this.current.font = fontObj; this.current.fontSize = size; + // Cache font matrix sign + this.current.fontMatrixXSign = this.current.fontMatrix[0] > 0 ? 1 : -1; + var name = fontObj.loadedName || 'sans-serif'; var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') : (fontObj.bold ? 'bold' : 'normal'); @@ -605,7 +619,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var ctx = this.ctx; var current = this.current; var textHScale = current.textHScale; - var fontMatrix = current.font.fontMatrix || IDENTITY_MATRIX; + var fontMatrix = current.fontMatrix || IDENTITY_MATRIX; ctx.transform.apply(ctx, current.textMatrix); ctx.scale(1, -1); @@ -639,7 +653,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var charSpacing = current.charSpacing; var wordSpacing = current.wordSpacing; var textHScale = current.textHScale; - var fontMatrix = font.fontMatrix || IDENTITY_MATRIX; + var fontMatrix = current.fontMatrix || IDENTITY_MATRIX; var textHScale2 = textHScale * fontMatrix[0]; var glyphsLength = glyphs.length; var textLayer = this.textLayer; @@ -677,7 +691,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.restore(); var transformed = Util.applyTransform([glyph.width, 0], fontMatrix); - var width = transformed[0] * fontSize + charSpacing; + var width = transformed[0] * fontSize + + current.fontMatrixXSign * charSpacing; ctx.translate(width, 0); current.x += width * textHScale; @@ -708,12 +723,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var glyph = glyphs[i]; if (glyph === null) { // word break - x += wordSpacing; + x += current.fontMatrixXSign * wordSpacing; continue; } var char = glyph.fontChar; - var charWidth = glyph.width * fontSize * 0.001 + charSpacing; + var charWidth = glyph.width * fontSize * 0.001 + + current.fontMatrixXSign * charSpacing; switch (textRenderingMode) { default: // other unsupported rendering modes @@ -756,7 +772,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var fontSize = current.fontSize; var textHScale = current.textHScale; if (!font.coded) - textHScale *= (font.fontMatrix || IDENTITY_MATRIX)[0]; + textHScale *= (current.fontMatrix || IDENTITY_MATRIX)[0]; var arrLength = arr.length; var textLayer = this.textLayer; var text = {str: '', length: 0, canvasWidth: 0, geom: {}}; diff --git a/test/pdfs/issue1049.pdf.link b/test/pdfs/issue1049.pdf.link new file mode 100644 index 000000000..c486dda43 --- /dev/null +++ b/test/pdfs/issue1049.pdf.link @@ -0,0 +1 @@ +http://ernestinefont.com/wp-content/themes/iA3%201.2.1/assets/pdf/ErnestinePro-InfoGuide.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 26ddceaf9..f934f3509 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -423,5 +423,12 @@ "rounds": 1, "link": false, "type": "eq" + }, + { "id": "issue1049", + "file": "pdfs/issue1049.pdf", + "md5": "15473fffcdde9fb8f3756a4cf1aab347", + "rounds": 1, + "link": true, + "type": "eq" } ] From bbdec90c0dc9aefe93b7a356a60c8176d6f2e3cd Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Fri, 20 Jan 2012 18:44:51 -0500 Subject: [PATCH 010/285] Lint --- src/canvas.js | 2 +- src/worker.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index b36772fe0..e6ba9f88d 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -554,7 +554,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { // A valid matrix needs all main diagonal elements to be non-zero // This also ensures we bypass FF bugzilla bug #719844. if (fontObj.fontMatrix[0] === 0 || - fontObj.fontMatrix[3] === 0 ) { + fontObj.fontMatrix[3] === 0) { warn('Invalid font matrix for font ' + fontRefName); // Fallback diff --git a/src/worker.js b/src/worker.js index 468cce019..3bf935f17 100644 --- a/src/worker.js +++ b/src/worker.js @@ -127,7 +127,7 @@ var WorkerMessageHandler = { e = { message: 'Unknown exception type: ' + (typeof e), stack: minimumStackMessage - } + }; } handler.send('page_error', { From c6662d12e1bc354ace66c4650160d05b4b22cfa8 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 21 Jan 2012 17:18:36 -0600 Subject: [PATCH 011/285] Changing glyphNameMap and GlyphUnicode lookup order --- src/fonts.js | 5 ++--- test/pdfs/issue1096.pdf.link | 1 + test/test_manifest.json | 8 ++++++++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 test/pdfs/issue1096.pdf.link diff --git a/src/fonts.js b/src/fonts.js index f96c15458..adcedd55c 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -2235,9 +2235,8 @@ var Font = (function FontClosure() { } // MacRoman encoding address by re-encoding the cmap table - unicode = glyphName in GlyphsUnicode ? - GlyphsUnicode[glyphName] : - this.glyphNameMap[glyphName]; + unicode = glyphName in this.glyphNameMap ? + this.glyphNameMap[glyphName] : GlyphsUnicode[glyphName]; break; default: warn('Unsupported font type: ' + this.type); diff --git a/test/pdfs/issue1096.pdf.link b/test/pdfs/issue1096.pdf.link new file mode 100644 index 000000000..aa07f14dd --- /dev/null +++ b/test/pdfs/issue1096.pdf.link @@ -0,0 +1 @@ +http://www.faithaliveresources.org/Content/Site135/FilesSamples/105315400440pdf_00000009843.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 5bc344abf..648d1b49b 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -402,6 +402,14 @@ "link": true, "type": "eq" }, + { "id": "issue1096", + "file": "pdfs/issue1096.pdf", + "md5": "7f75d2b4b93c78d401ff39e8c1b00612", + "rounds": 1, + "pageLimit": 10, + "link": true, + "type": "eq" + }, { "id": "liveprogramming", "file": "pdfs/liveprogramming.pdf", "md5": "7bd4dad1188232ef597d36fd72c33e52", From 5c8753dcbac9e7870d1a66efbc5bb484efc07864 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Mon, 23 Jan 2012 15:23:09 -0500 Subject: [PATCH 012/285] Fixing regression errors, better logic --- src/canvas.js | 39 ++++++++++++++++++--------------------- src/util.js | 4 ++++ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index e6ba9f88d..0a73e034c 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -23,6 +23,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() { this.alphaIsShape = false; this.fontSize = 0; this.textMatrix = IDENTITY_MATRIX; + this.fontMatrix = IDENTITY_MATRIX; this.leading = 0; // Current point (in user coordinates) this.x = 0; @@ -546,40 +547,36 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { }, setFont: function canvasGraphicsSetFont(fontRefName, size) { var fontObj = this.objs.get(fontRefName).fontObj; + var current = this.current; - if (!fontObj) { + if (!fontObj) throw 'Can\'t find font for ' + fontRefName; - } + + // Slice-clone matrix so we can manipulate it without affecting original + if (fontObj.fontMatrix) + current.fontMatrix = fontObj.fontMatrix.slice(0); + else + current.fontMatrix = IDENTITY_MATRIX.slice(0); // A valid matrix needs all main diagonal elements to be non-zero // This also ensures we bypass FF bugzilla bug #719844. - if (fontObj.fontMatrix[0] === 0 || - fontObj.fontMatrix[3] === 0) { + if (current.fontMatrix[0] === 0 || + current.fontMatrix[3] === 0) { warn('Invalid font matrix for font ' + fontRefName); - - // Fallback - fontObj.fontMatrix = IDENTITY_MATRIX; } - var name = fontObj.loadedName || 'sans-serif'; - - // Clone fontMatrix so we can manipulate it without affecting original - this.current.fontMatrix = fontObj.fontMatrix.slice(0); - // The spec for Tf (setFont) says that 'size' specifies the font 'scale', - // and in some docs this can be negative. We implement this in fontMatrix. + // and in some docs this can be negative (inverted x-y axes). + // We implement this condition with fontMatrix. if (size < 0) { size = -size; - this.current.fontMatrix[0] = -fontObj.fontMatrix[0]; - this.current.fontMatrix[3] = -fontObj.fontMatrix[3]; + current.fontMatrix[0] *= -1; + current.fontMatrix[3] *= -1; } this.current.font = fontObj; this.current.fontSize = size; - // Cache font matrix sign - this.current.fontMatrixXSign = this.current.fontMatrix[0] > 0 ? 1 : -1; - var name = fontObj.loadedName || 'sans-serif'; var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') : (fontObj.bold ? 'bold' : 'normal'); @@ -692,7 +689,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var transformed = Util.applyTransform([glyph.width, 0], fontMatrix); var width = transformed[0] * fontSize + - current.fontMatrixXSign * charSpacing; + Util.sign(current.fontMatrix[0]) * charSpacing; ctx.translate(width, 0); current.x += width * textHScale; @@ -723,13 +720,13 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var glyph = glyphs[i]; if (glyph === null) { // word break - x += current.fontMatrixXSign * wordSpacing; + x += Util.sign(current.fontMatrix[0]) * wordSpacing; continue; } var char = glyph.fontChar; var charWidth = glyph.width * fontSize * 0.001 + - current.fontMatrixXSign * charSpacing; + Util.sign(current.fontMatrix[0]) * charSpacing; switch (textRenderingMode) { default: // other unsupported rendering modes diff --git a/src/util.js b/src/util.js index 99b422296..ce0daa17d 100644 --- a/src/util.js +++ b/src/util.js @@ -93,6 +93,10 @@ var Util = (function UtilClosure() { return [xt, yt]; }; + Util.sign = function sign(num) { + return num < 0 ? -1 : 1; + }; + return Util; })(); From b34c55cc3cdaecd7aad66a7fbf613f90637b48d7 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Mon, 23 Jan 2012 15:29:15 -0500 Subject: [PATCH 013/285] Use ES5 .trim() --- src/fonts.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 5d0114476..f68ccd9ff 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -2596,9 +2596,7 @@ var Type1Parser = function type1Parser() { str = str.substr(start, count); - // Trim - str = str.replace(/^\s+/, ''); - str = str.replace(/\s+$/, ''); + str = str.trim(); // Remove adjacent spaces str = str.replace(/\s+/g, ' '); From 178b89342af2185de7771fba8112cb92cd6dfaa9 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 23 Jan 2012 16:50:45 -0800 Subject: [PATCH 014/285] Switch to stream converter for extension. --- Makefile | 14 ++- extensions/firefox/bootstrap.js | 3 - extensions/firefox/chrome.manifest | 4 +- .../firefox/components/pdfContentHandler.js | 93 ++++++++++++------- src/core.js | 20 +++- web/viewer-snippet-firefox-extension.html | 14 +++ web/viewer.html | 4 +- web/viewer.js | 35 ++----- 8 files changed, 115 insertions(+), 72 deletions(-) create mode 100644 web/viewer-snippet-firefox-extension.html diff --git a/Makefile b/Makefile index fe5a3fc42..eaaa3e81f 100644 --- a/Makefile +++ b/Makefile @@ -211,6 +211,7 @@ EXTENSION_WEB_FILES = \ web/images \ web/viewer.css \ web/viewer.js \ + web/viewer.html \ web/viewer-production.html \ $(NULL) @@ -249,7 +250,18 @@ extension: | production # Copy a standalone version of pdf.js inside the content directory @cp $(BUILD_TARGET) $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/ @cp -r $(EXTENSION_WEB_FILES) $(FIREFOX_BUILD_CONTENT)/web/ - @mv -f $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html $(FIREFOX_BUILD_CONTENT)/web/viewer.html + @rm $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html + # Copy over the firefox extension snippet so we can inline pdf.js in it + cp web/viewer-snippet-firefox-extension.html $(FIREFOX_BUILD_CONTENT)/web/ + # Modify the viewer so it does all the extension only stuff. + cd $(FIREFOX_BUILD_CONTENT)/web; \ + sed -i.bak '/PDFJSSCRIPT_INCLUDE_BUNDLE/ r ../build/pdf.js' viewer-snippet-firefox-extension.html; \ + sed -i.bak '/PDFJSSCRIPT_REMOVE/d' viewer.html; \ + sed -i.bak '/PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION/d' viewer.html; \ + sed -i.bak '/PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION/ r viewer-snippet-firefox-extension.html' viewer.html; \ + rm -f *.bak; + # We don't need pdf.js anymore since its inlined + rm -Rf $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/; # Update the build version number @sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/install.rdf @sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/update.rdf diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js index e51df28f8..bbc53195e 100644 --- a/extensions/firefox/bootstrap.js +++ b/extensions/firefox/bootstrap.js @@ -34,13 +34,10 @@ function shutdown(aData, aReason) { } function install(aData, aReason) { - let url = 'chrome://pdf.js/content/web/viewer.html?file=%s'; - Services.prefs.setCharPref('extensions.pdf.js.url', url); Services.prefs.setBoolPref('extensions.pdf.js.active', false); } function uninstall(aData, aReason) { - Services.prefs.clearUserPref('extensions.pdf.js.url'); Services.prefs.clearUserPref('extensions.pdf.js.active'); } diff --git a/extensions/firefox/chrome.manifest b/extensions/firefox/chrome.manifest index d7db20b38..ec7c9a964 100644 --- a/extensions/firefox/chrome.manifest +++ b/extensions/firefox/chrome.manifest @@ -1,5 +1,5 @@ -content pdf.js content/ +resource pdf.js content/ component {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} components/pdfContentHandler.js -contract @mozilla.org/uriloader/content-handler;1?type=application/pdf {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} +contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index 67459b759..fa9f329fc 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -21,47 +21,74 @@ function log(aMsg) { } const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; + function pdfContentHandler() { -} +}; pdfContentHandler.prototype = { - handleContent: function handleContent(aMimetype, aContext, aRequest) { - if (aMimetype != PDF_CONTENT_TYPE) - throw NS_ERROR_WONT_HANDLE_CONTENT; - if (!(aRequest instanceof Ci.nsIChannel)) - throw NS_ERROR_WONT_HANDLE_CONTENT; + // properties required for XPCOM registration: + classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'), + classDescription: 'pdf.js Component', + contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*', + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsISupports, + Ci.nsIStreamConverter, + Ci.nsIStreamListener, + Ci.nsIRequestObserver + ]), - if (!Services.prefs.getBoolPref('extensions.pdf.js.active')) - throw NS_ERROR_WONT_HANDLE_CONTENT; + /* + * This component works as such: + * 1. asyncConvertData stores the listener + * 2. onStartRequest creates a new channel, streams the viewer and cancels + * the request so pdf.js can do the request + * Since the request is cancelled onDataAvailable should not be called. The + * onStopRequest does nothing. The convert function just returns the stream, + * it's just the synchronous version of asyncConvertData. + */ - let window = null; - let callbacks = aRequest.notificationCallbacks || - aRequest.loadGroup.notificationCallbacks; - if (!callbacks) - return; - - window = callbacks.getInterface(Ci.nsIDOMWindow); - - let url = null; - try { - url = Services.prefs.getCharPref('extensions.pdf.js.url'); - } catch (e) { - log('Error retrieving the pdf.js base url - ' + e); - throw NS_ERROR_WONT_HANDLE_CONTENT; - } - - let targetUrl = aRequest.URI.spec; - if (targetUrl.indexOf('#pdfjs.action=download') >= 0) - throw NS_ERROR_WONT_HANDLE_CONTENT; - - aRequest.cancel(Cr.NS_BINDING_ABORTED); - window.location = url.replace('%s', encodeURIComponent(targetUrl)); + // nsIStreamConverter::convert + convert: function (aFromStream, aFromType, aToType, aCtxt) { + return aFromStream; }, - classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]) + // nsIStreamConverter::asyncConvertData + asyncConvertData: function (aFromType, aToType, aListener, aCtxt) { + // Store the listener passed to us + this.listener = aListener; + }, + + // nsIStreamListener::onDataAvailable + onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) { + // Do nothing since all the data loading is handled by the viewer. + log("SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!"); + }, + + // nsIRequestObserver::onStartRequest + onStartRequest: function (aRequest, aContext) { + // Setup the request so we can use it below. + aRequest.QueryInterface(Ci.nsIChannel); + + // Create a new channel that is viewer loaded as a resource. + var ioService = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var channel = ioService.newChannel( + 'resource://pdf.js/web/viewer.html', null, null); + // Keep the URL the same so the browser sees it as the same. + channel.originalURI = aRequest.originalURI; + channel.asyncOpen(this.listener, aContext); + + // Cancel the request so the viewer can handle it. + aRequest.cancel(Cr.NS_BINDING_ABORTED); + }, + + // nsIRequestObserver::onStopRequest + onStopRequest: function (aRequest, aContext, aStatusCode) { + // Do nothing. + return; + } }; var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]); - diff --git a/src/core.js b/src/core.js index 7a9f3ee03..3e3d991a9 100644 --- a/src/core.js +++ b/src/core.js @@ -624,9 +624,19 @@ var PDFDoc = (function PDFDocClosure() { } try { - // Some versions of FF can't create a worker on localhost, see: - // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 - var worker = new Worker(workerSrc); + var worker; + if (PDFJS.isFirefoxExtension) { + // The firefox extension can't load the worker from the resource:// + // url so we have to inline the script and then use the blob loader. + var bb = new MozBlobBuilder(); + bb.append(document.querySelector('#PDFJS_SCRIPT_TAG').textContent); + var blobUrl = window.URL.createObjectURL(bb.getBlob()); + worker = new Worker(blobUrl); + } else { + // Some versions of FF can't create a worker on localhost, see: + // https://bugzilla.mozilla.org/show_bug.cgi?id=683280 + worker = new Worker(workerSrc); + } var messageHandler = new MessageHandler('main', worker); @@ -645,7 +655,9 @@ var PDFDoc = (function PDFDocClosure() { // serializing the typed array. messageHandler.send('test', testObj); return; - } catch (e) {} + } catch (e) { + warn('The worker has been disabled.') + } } // Either workers are disabled, not supported or have thrown an exception. // Thus, we fallback to a faked worker. diff --git a/web/viewer-snippet-firefox-extension.html b/web/viewer-snippet-firefox-extension.html new file mode 100644 index 000000000..a3d3502a8 --- /dev/null +++ b/web/viewer-snippet-firefox-extension.html @@ -0,0 +1,14 @@ + + + + diff --git a/web/viewer.html b/web/viewer.html index 40e99004f..f395292f9 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -2,9 +2,11 @@ Simple pdf.js page viewer + + - + diff --git a/web/viewer.js b/web/viewer.js index 6c72df3d0..f65c75434 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -73,23 +73,11 @@ var Settings = (function SettingsClosure() { } return true; })(); - var extPrefix = 'extensions.uriloader@pdf.js'; - var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled; - var inPrivateBrowsing = false; - if (isExtension) { - var pbs = Components.classes['@mozilla.org/privatebrowsing;1'] - .getService(Components.interfaces.nsIPrivateBrowsingService); - inPrivateBrowsing = pbs.privateBrowsingEnabled; - } function Settings(fingerprint) { var database = null; var index; - if (inPrivateBrowsing) - return false; - else if (isExtension) - database = Application.prefs.getValue(extPrefix + '.database', '{}'); - else if (isLocalStorageEnabled) + if (isLocalStorageEnabled) database = localStorage.getItem('database') || '{}'; else return false; @@ -110,31 +98,20 @@ var Settings = (function SettingsClosure() { index = database.files.push({fingerprint: fingerprint}) - 1; this.file = database.files[index]; this.database = database; - if (isExtension) - Application.prefs.setValue(extPrefix + '.database', - JSON.stringify(database)); - else if (isLocalStorageEnabled) + if (isLocalStorageEnabled) localStorage.setItem('database', JSON.stringify(database)); } Settings.prototype = { set: function settingsSet(name, val) { - if (inPrivateBrowsing) - return false; var file = this.file; file[name] = val; - if (isExtension) - Application.prefs.setValue(extPrefix + '.database', - JSON.stringify(this.database)); - else if (isLocalStorageEnabled) + if (isLocalStorageEnabled) localStorage.setItem('database', JSON.stringify(this.database)); }, get: function settingsGet(name, defaultValue) { - if (inPrivateBrowsing) - return defaultValue; - else - return this.file[name] || defaultValue; + return this.file[name] || defaultValue; } }; @@ -1011,7 +988,9 @@ window.addEventListener('load', function webViewerLoad(evt) { } var scale = ('scale' in params) ? params.scale : 0; - PDFView.open(params.file || kDefaultURL, parseFloat(scale)); + var file = PDFJS.isFirefoxExtension ? + window.location.toString() : params.file || kDefaultURL; + PDFView.open(file, parseFloat(scale)); if (!window.File || !window.FileReader || !window.FileList || !window.Blob) document.getElementById('fileInput').setAttribute('hidden', 'true'); From 4d3057aba747ffc2cc6ca54ca475504aac57608e Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Mon, 23 Jan 2012 17:52:53 -0800 Subject: [PATCH 015/285] Fix lint. --- .../firefox/components/pdfContentHandler.js | 18 +++++++++--------- src/core.js | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index fa9f329fc..fc42b9260 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -23,7 +23,7 @@ function log(aMsg) { const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; function pdfContentHandler() { -}; +} pdfContentHandler.prototype = { @@ -31,7 +31,7 @@ pdfContentHandler.prototype = { classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'), classDescription: 'pdf.js Component', contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*', - + QueryInterface: XPCOMUtils.generateQI([ Ci.nsISupports, Ci.nsIStreamConverter, @@ -50,29 +50,29 @@ pdfContentHandler.prototype = { */ // nsIStreamConverter::convert - convert: function (aFromStream, aFromType, aToType, aCtxt) { + convert: function(aFromStream, aFromType, aToType, aCtxt) { return aFromStream; }, // nsIStreamConverter::asyncConvertData - asyncConvertData: function (aFromType, aToType, aListener, aCtxt) { + asyncConvertData: function(aFromType, aToType, aListener, aCtxt) { // Store the listener passed to us this.listener = aListener; }, // nsIStreamListener::onDataAvailable - onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) { + onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) { // Do nothing since all the data loading is handled by the viewer. - log("SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!"); + log('SANITY CHECK: onDataAvailable SHOULD NOT BE CALLED!'); }, // nsIRequestObserver::onStartRequest - onStartRequest: function (aRequest, aContext) { + onStartRequest: function(aRequest, aContext) { // Setup the request so we can use it below. aRequest.QueryInterface(Ci.nsIChannel); // Create a new channel that is viewer loaded as a resource. - var ioService = Cc["@mozilla.org/network/io-service;1"] + var ioService = Cc['@mozilla.org/network/io-service;1'] .getService(Ci.nsIIOService); var channel = ioService.newChannel( 'resource://pdf.js/web/viewer.html', null, null); @@ -85,7 +85,7 @@ pdfContentHandler.prototype = { }, // nsIRequestObserver::onStopRequest - onStopRequest: function (aRequest, aContext, aStatusCode) { + onStopRequest: function(aRequest, aContext, aStatusCode) { // Do nothing. return; } diff --git a/src/core.js b/src/core.js index 3e3d991a9..93dde00ed 100644 --- a/src/core.js +++ b/src/core.js @@ -656,7 +656,7 @@ var PDFDoc = (function PDFDocClosure() { messageHandler.send('test', testObj); return; } catch (e) { - warn('The worker has been disabled.') + warn('The worker has been disabled.'); } } // Either workers are disabled, not supported or have thrown an exception. From 5c62f99fee6bad235d80d337ede1a3620c031652 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 24 Jan 2012 10:08:18 -0500 Subject: [PATCH 016/285] Fix localStorage feature detection. Close #1099 --- web/viewer.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index b6b62af83..87f1c4b37 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -66,12 +66,12 @@ var RenderingQueue = (function RenderingQueueClosure() { // If not, we use FUEL in FF var Settings = (function SettingsClosure() { var isLocalStorageEnabled = (function localStorageEnabledTest() { + // Feature test as per http://diveintohtml5.info/storage.html try { - localStorage; + return 'localStorage' in window && window['localStorage'] !== null; } catch (e) { return false; } - return true; })(); var extPrefix = 'extensions.uriloader@pdf.js'; var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled; @@ -119,8 +119,9 @@ var Settings = (function SettingsClosure() { Settings.prototype = { set: function settingsSet(name, val) { - if (inPrivateBrowsing) + if (inPrivateBrowsing || !('file' in this)) return false; + var file = this.file; file[name] = val; if (isExtension) @@ -131,10 +132,10 @@ var Settings = (function SettingsClosure() { }, get: function settingsGet(name, defaultValue) { - if (inPrivateBrowsing) + if (inPrivateBrowsing || !('file' in this)) return defaultValue; - else - return this.file[name] || defaultValue; + + return this.file[name] || defaultValue; } }; From 843830cea0be94083f66bf65f525d66fe097fa61 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 24 Jan 2012 10:39:57 -0500 Subject: [PATCH 017/285] Workaround for FF bug --- web/viewer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index 87f1c4b37..11c0769a3 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -62,13 +62,16 @@ var RenderingQueue = (function RenderingQueueClosure() { })(); // Settings Manager - This is a utility for saving settings -// First we see if localStorage is available, FF bug #495747 +// First we see if localStorage is available // If not, we use FUEL in FF var Settings = (function SettingsClosure() { var isLocalStorageEnabled = (function localStorageEnabledTest() { // Feature test as per http://diveintohtml5.info/storage.html + // The additional localStorage call is to get around a FF quirk, see + // bug #495747 in bugzilla try { - return 'localStorage' in window && window['localStorage'] !== null; + return 'localStorage' in window && window['localStorage'] !== null && + localStorage; } catch (e) { return false; } From 66e3441e0eccc2bea2d608fd02f1c4795f1c02f6 Mon Sep 17 00:00:00 2001 From: Kalervo Kujala Date: Tue, 24 Jan 2012 22:04:59 +0200 Subject: [PATCH 018/285] Change throws to errors. --- src/canvas.js | 4 ++-- src/core.js | 10 +++++----- src/jpx.js | 16 ++++++++-------- src/obj.js | 4 ++-- src/stream.js | 22 +++++++++++++--------- src/util.js | 16 ++++++++-------- src/worker.js | 8 ++++---- 7 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/canvas.js b/src/canvas.js index 5ef900861..d0b0064f6 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -548,7 +548,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var fontObj = this.objs.get(fontRefName).fontObj; if (!fontObj) { - throw 'Can\'t find font for ' + fontRefName; + error('Can\'t find font for ' + fontRefName); } var name = fontObj.loadedName || 'sans-serif'; @@ -866,7 +866,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } else if (IR[0] == 'RadialAxial' || IR[0] == 'Dummy') { var pattern = Pattern.shadingFromIR(this.ctx, IR); } else { - throw 'Unkown IR type'; + error('Unkown IR type ' + IR[0]); } return pattern; }, diff --git a/src/core.js b/src/core.js index 7a9f3ee03..df38c086f 100644 --- a/src/core.js +++ b/src/core.js @@ -410,14 +410,14 @@ var Page = (function PageClosure() { if (callback) callback(e); else - throw e; + error(e); } }.bind(this), function pageDisplayReadPromiseError(reason) { if (callback) callback(reason); else - throw reason; + error(reason); } ); } @@ -620,7 +620,7 @@ var PDFDoc = (function PDFDocClosure() { if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') { var workerSrc = PDFJS.workerSrc; if (typeof workerSrc === 'undefined') { - throw 'No PDFJS.workerSrc specified'; + error('No PDFJS.workerSrc specified'); } try { @@ -716,7 +716,7 @@ var PDFDoc = (function PDFDocClosure() { }); break; default: - throw 'Got unkown object type ' + type; + error('Got unkown object type ' + type); } }, this); @@ -737,7 +737,7 @@ var PDFDoc = (function PDFDocClosure() { if (page.displayReadyPromise) page.displayReadyPromise.reject(data.error); else - throw data.error; + error(data.error); }, this); messageHandler.on('jpeg_decode', function(data, promise) { diff --git a/src/jpx.js b/src/jpx.js index 61a8f4487..c212c6fd0 100644 --- a/src/jpx.js +++ b/src/jpx.js @@ -1052,7 +1052,7 @@ var JpxImage = (function JpxImageClosure() { } r = 0; } - throw 'Out of packets'; + error('Out of packets'); }; } function ResolutionLayerComponentPositionIterator(context) { @@ -1091,7 +1091,7 @@ var JpxImage = (function JpxImageClosure() { } l = 0; } - throw 'Out of packets'; + error('Out of packets'); }; } function buildPackets(context) { @@ -1187,7 +1187,7 @@ var JpxImage = (function JpxImageClosure() { new ResolutionLayerComponentPositionIterator(context); break; default: - throw 'Unsupported progression order'; + error('Unsupported progression order ' + progressionOrder); } } function parseTilePackets(context, data, offset, dataLength) { @@ -1589,7 +1589,7 @@ var JpxImage = (function JpxImageClosure() { if (lbox == 0) lbox = length - position + headerSize; if (lbox < headerSize) - throw 'Invalid box field size'; + error('Invalid box field size'); var dataLength = lbox - headerSize; var jumpDataLength = true; switch (tbox) { @@ -1675,7 +1675,7 @@ var JpxImage = (function JpxImageClosure() { scalarExpounded = true; break; default: - throw 'Invalid SQcd value'; + error('Invalid SQcd value ' + sqcd); } qcd.noQuantization = spqcdSize == 8; qcd.scalarExpounded = scalarExpounded; @@ -1728,7 +1728,7 @@ var JpxImage = (function JpxImageClosure() { scalarExpounded = true; break; default: - throw 'Invalid SQcd value'; + error('Invalid SQcd value ' + sqcd); } qcc.noQuantization = spqcdSize == 8; qcc.scalarExpounded = scalarExpounded; @@ -1795,7 +1795,7 @@ var JpxImage = (function JpxImageClosure() { cod.terminationOnEachCodingPass || cod.verticalyStripe || cod.predictableTermination || cod.segmentationSymbolUsed) - throw 'Unsupported COD options: ' + uneval(cod); + error('Unsupported COD options: ' + uneval(cod)); if (context.mainHeader) context.COD = cod; @@ -1840,7 +1840,7 @@ var JpxImage = (function JpxImageClosure() { // skipping content break; default: - throw 'Unknown codestream code: ' + code.toString(16); + error('Unknown codestream code: ' + code.toString(16)); } position += length; } diff --git a/src/obj.js b/src/obj.js index ef7932546..5b87fec13 100644 --- a/src/obj.js +++ b/src/obj.js @@ -574,7 +574,7 @@ var XRef = (function XRefClosure() { var stream, parser; if (e.uncompressed) { if (e.gen != gen) - throw ('inconsistent generation in XRef'); + error('inconsistent generation in XRef'); stream = this.stream.makeSubStream(e.offset); parser = new Parser(new Lexer(stream), true, this); var obj1 = parser.getObj(); @@ -703,7 +703,7 @@ var PDFObjects = (function PDFObjectsClosure() { // If there isn't an object yet or the object isn't resolved, then the // data isn't ready yet! if (!obj || !obj.isResolved) { - throw 'Requesting object that isn\'t resolved yet ' + objId; + error('Requesting object that isn\'t resolved yet ' + objId); return null; } else { return obj.data; diff --git a/src/stream.js b/src/stream.js index 610a54d38..c7b7c83e6 100644 --- a/src/stream.js +++ b/src/stream.js @@ -821,15 +821,19 @@ var JpegStream = (function JpegStreamClosure() { JpegStream.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) { if (this.bufferLength) return; - var jpegImage = new JpegImage(); - if (this.colorTransform != -1) - jpegImage.colorTransform = this.colorTransform; - jpegImage.parse(this.bytes); - var width = jpegImage.width; - var height = jpegImage.height; - var data = jpegImage.getData(width, height); - this.buffer = data; - this.bufferLength = data.length; + try { + var jpegImage = new JpegImage(); + if (this.colorTransform != -1) + jpegImage.colorTransform = this.colorTransform; + jpegImage.parse(this.bytes); + var width = jpegImage.width; + var height = jpegImage.height; + var data = jpegImage.getData(width, height); + this.buffer = data; + this.bufferLength = data.length; + } catch (e) { + error(e); + } }; JpegStream.prototype.getIR = function jpegStreamGetIR() { return bytesToString(this.bytes); diff --git a/src/util.js b/src/util.js index 99b422296..759908e9e 100644 --- a/src/util.js +++ b/src/util.js @@ -255,8 +255,8 @@ var Promise = (function PromiseClosure() { return; } if (this._data !== EMPTY_PROMISE) { - throw 'Promise ' + this.name + - ': Cannot set the data of a promise twice'; + error('Promise ' + this.name + + ': Cannot set the data of a promise twice'); } this._data = value; this.hasData = true; @@ -268,7 +268,7 @@ var Promise = (function PromiseClosure() { get data() { if (this._data === EMPTY_PROMISE) { - throw 'Promise ' + this.name + ': Cannot get data that isn\'t set'; + error('Promise ' + this.name + ': Cannot get data that isn\'t set'); } return this._data; }, @@ -283,10 +283,10 @@ var Promise = (function PromiseClosure() { resolve: function promiseResolve(data) { if (this.isResolved) { - throw 'A Promise can be resolved only once ' + this.name; + error('A Promise can be resolved only once ' + this.name); } if (this.isRejected) { - throw 'The Promise was already rejected ' + this.name; + error('The Promise was already rejected ' + this.name); } this.isResolved = true; @@ -300,10 +300,10 @@ var Promise = (function PromiseClosure() { reject: function proimseReject(reason) { if (this.isRejected) { - throw 'A Promise can be rejected only once ' + this.name; + error('A Promise can be rejected only once ' + this.name); } if (this.isResolved) { - throw 'The Promise was already resolved ' + this.name; + error('The Promise was already resolved ' + this.name); } this.isRejected = true; @@ -317,7 +317,7 @@ var Promise = (function PromiseClosure() { then: function promiseThen(callback, errback) { if (!callback) { - throw 'Requiring callback' + this.name; + error('Requiring callback' + this.name); } // If the promise is already resolved, call the callback directly. diff --git a/src/worker.js b/src/worker.js index 4d9dd1bb6..f9777d7df 100644 --- a/src/worker.js +++ b/src/worker.js @@ -26,7 +26,7 @@ function MessageHandler(name, comObj) { delete callbacks[callbackId]; callback(data.data); } else { - throw 'Cannot resolve callback ' + callbackId; + error('Cannot resolve callback ' + callbackId); } } else if (data.action in ah) { var action = ah[data.action]; @@ -44,7 +44,7 @@ function MessageHandler(name, comObj) { action[0].call(action[1], data.data); } } else { - throw 'Unkown action from worker: ' + data.action; + error('Unkown action from worker: ' + data.action); } }; } @@ -53,7 +53,7 @@ MessageHandler.prototype = { on: function messageHandlerOn(actionName, handler, scope) { var ah = this.actionHandler; if (ah[actionName]) { - throw 'There is already an actionName called "' + actionName + '"'; + error('There is already an actionName called "' + actionName + '"'); } ah[actionName] = [handler, scope]; }, @@ -217,7 +217,7 @@ var workerConsole = { timeEnd: function timeEnd(name) { var time = consoleTimer[name]; if (time == null) { - throw 'Unkown timer name ' + name; + error('Unkown timer name ' + name); } this.log('Timer:', name, Date.now() - time); } From 19622406da911244d88ca15a073c185d0d83612d Mon Sep 17 00:00:00 2001 From: Jeff Wagner Date: Tue, 24 Jan 2012 12:19:52 -0800 Subject: [PATCH 019/285] define console for IE9 when debugging tools is not opened --- web/compatibility.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/web/compatibility.js b/web/compatibility.js index 26405ad8f..7addbe3e2 100644 --- a/web/compatibility.js +++ b/web/compatibility.js @@ -224,3 +224,10 @@ } }); })(); + +//IE9 console +(function checkConsoleCompatibility() { + if (typeof console == "undefined") { + console = {log: function() {}}; + } +})(); From 08f8aed5215d4ba861d4675f91aabb9bffa5997f Mon Sep 17 00:00:00 2001 From: Jeff Wagner Date: Tue, 24 Jan 2012 14:19:41 -0800 Subject: [PATCH 020/285] define console for IE9; updated to fix lint errors and comment --- web/compatibility.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/compatibility.js b/web/compatibility.js index 7addbe3e2..b22153516 100644 --- a/web/compatibility.js +++ b/web/compatibility.js @@ -225,9 +225,9 @@ }); })(); -//IE9 console +// Check console compatability (function checkConsoleCompatibility() { - if (typeof console == "undefined") { + if (typeof console == 'undefined') { console = {log: function() {}}; } })(); From 23bf35d5ef34039fd181b3f25b973a4d30f1720d Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 24 Jan 2012 14:55:07 -0800 Subject: [PATCH 021/285] Fix anchor links. --- web/viewer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index f65c75434..1ed9019ff 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -269,15 +269,18 @@ var PDFView = { }, getDestinationHash: function pdfViewGetDestinationHash(dest) { + // We add the full url for the extension so the anchor links don't come up + // as resource:// urls and so open in new tab/window works. + var url = PDFJS.isFirefoxExtension ? this.url.split('#')[0] : ''; if (typeof dest === 'string') - return '#' + escape(dest); + return url + '#' + escape(dest); if (dest instanceof Array) { var destRef = dest[0]; // see navigateTo method for dest format var pageNumber = destRef instanceof Object ? this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1); if (pageNumber) { - var pdfOpenParams = '#page=' + pageNumber; + var pdfOpenParams = url + '#page=' + pageNumber; var destKind = dest[1]; if ('name' in destKind && destKind.name == 'XYZ') { var scale = (dest[4] || this.currentScale); From f3b2a03de63b4216790a00c3ebc95b7e449211e6 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 24 Jan 2012 15:13:50 -0800 Subject: [PATCH 022/285] Hide the browse bar for the ff extension. --- web/viewer.css | 2 +- web/viewer.html | 2 +- web/viewer.js | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index e355f7fc2..b9fd3e9e4 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -9,7 +9,7 @@ body { } [hidden] { - display: none; + display: none !important; } /* === Toolbar === */ diff --git a/web/viewer.html b/web/viewer.html index f395292f9..ad9d1189e 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -92,7 +92,7 @@ -
+
Bookmark diff --git a/web/viewer.js b/web/viewer.js index 1ed9019ff..131d12b42 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -995,10 +995,14 @@ window.addEventListener('load', function webViewerLoad(evt) { window.location.toString() : params.file || kDefaultURL; PDFView.open(file, parseFloat(scale)); - if (!window.File || !window.FileReader || !window.FileList || !window.Blob) + if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader || + !window.FileList || !window.Blob) { document.getElementById('fileInput').setAttribute('hidden', 'true'); - else + document.getElementById('fileInputSeperator') + .setAttribute('hidden', 'true'); + } else { document.getElementById('fileInput').value = null; + } if ('disableWorker' in params) PDFJS.disableWorker = (params['disableWorker'] === 'true'); From dd8c39d8e14d283777f8c4e5788ada2c719d9542 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 24 Jan 2012 15:46:53 -0800 Subject: [PATCH 023/285] Fix the bookmark button and redo the anchor prefix. --- web/viewer.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index 131d12b42..b784e6f5a 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -269,18 +269,15 @@ var PDFView = { }, getDestinationHash: function pdfViewGetDestinationHash(dest) { - // We add the full url for the extension so the anchor links don't come up - // as resource:// urls and so open in new tab/window works. - var url = PDFJS.isFirefoxExtension ? this.url.split('#')[0] : ''; if (typeof dest === 'string') - return url + '#' + escape(dest); + return PDFView.getAnchorUrl('#' + escape(dest)); if (dest instanceof Array) { var destRef = dest[0]; // see navigateTo method for dest format var pageNumber = destRef instanceof Object ? this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] : (destRef + 1); if (pageNumber) { - var pdfOpenParams = url + '#page=' + pageNumber; + var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber); var destKind = dest[1]; if ('name' in destKind && destKind.name == 'XYZ') { var scale = (dest[4] || this.currentScale); @@ -295,6 +292,17 @@ var PDFView = { return ''; }, + /** + * For the firefox extension we prefix the full url on anchor links so they + * don't come up as resource:// urls and so open in new tab/window works. + * @param {String} anchor The anchor hash include the #. + */ + getAnchorUrl: function getAnchorUrl(anchor) { + if (PDFJS.isFirefoxExtension) + return this.url.split('#')[0] + anchor; + return anchor; + }, + /** * Show the error box. * @param {String} message A message that is human readable. @@ -1087,8 +1095,8 @@ function updateViewarea() { store.set('zoom', normalizedScaleValue); store.set('scrollLeft', Math.round(topLeft.x)); store.set('scrollTop', Math.round(topLeft.y)); - - document.getElementById('viewBookmark').href = pdfOpenParams; + var href = PDFView.getAnchorUrl(pdfOpenParams); + document.getElementById('viewBookmark').href = href; } window.addEventListener('scroll', function webViewerScroll(evt) { From 858aab008f236f4cfdba79be62d22677b9699e60 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 24 Jan 2012 21:33:03 -0800 Subject: [PATCH 024/285] Fix the download button. --- extensions/firefox/components/pdfContentHandler.js | 14 +++++++++++--- web/viewer.js | 9 ++++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index fc42b9260..320d69d30 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -70,6 +70,17 @@ pdfContentHandler.prototype = { onStartRequest: function(aRequest, aContext) { // Setup the request so we can use it below. aRequest.QueryInterface(Ci.nsIChannel); + // Cancel the request so the viewer can handle it. + aRequest.cancel(Cr.NS_BINDING_ABORTED); + + // Check if we should download. + var targetUrl = aRequest.originalURI.spec; + var downloadHash = targetUrl.indexOf('?#pdfjs.action=download'); + if (downloadHash >= 0) { + targetUrl = targetUrl.substring(0, downloadHash); + Services.wm.getMostRecentWindow("navigator:browser").saveURL(targetUrl); + return; + } // Create a new channel that is viewer loaded as a resource. var ioService = Cc['@mozilla.org/network/io-service;1'] @@ -79,9 +90,6 @@ pdfContentHandler.prototype = { // Keep the URL the same so the browser sees it as the same. channel.originalURI = aRequest.originalURI; channel.asyncOpen(this.listener, aContext); - - // Cancel the request so the viewer can handle it. - aRequest.cancel(Cr.NS_BINDING_ABORTED); }, // nsIRequestObserver::onStopRequest diff --git a/web/viewer.js b/web/viewer.js index b784e6f5a..55d0a595c 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -249,7 +249,14 @@ var PDFView = { }, download: function pdfViewDownload() { - window.open(this.url + '#pdfjs.action=download', '_parent'); + var url = this.url.split('#')[0]; + // For the extension we add an extra '?' to force the page to reload, its + // stripped off by the extension. + if (PDFJS.isFirefoxExtension) + url += '?#pdfjs.action=download'; + else + url += '#pdfjs.action=download', '_parent'; + window.open(url, '_parent'); }, navigateTo: function pdfViewNavigateTo(dest) { From 0d839c1c597b8054cb259d469c02300543023fca Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Wed, 25 Jan 2012 17:40:08 -0800 Subject: [PATCH 025/285] Fix how we're storing settings and change how the save pdf works. --- extensions/firefox/bootstrap.js | 152 +++++++++++++++++- .../firefox/components/pdfContentHandler.js | 15 +- web/viewer.js | 49 ++++-- 3 files changed, 195 insertions(+), 21 deletions(-) diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js index bbc53195e..06fcf182f 100644 --- a/extensions/firefox/bootstrap.js +++ b/extensions/firefox/bootstrap.js @@ -3,17 +3,155 @@ 'use strict'; +const EXT_PREFIX = 'extensions.uriloader@pdf.js'; +const PDFJS_EVENT_ID = 'pdf.js.message'; let Cc = Components.classes; let Ci = Components.interfaces; let Cm = Components.manager; let Cu = Components.utils; +let application = Cc['@mozilla.org/fuel/application;1'] + .getService(Ci.fuelIApplication); +let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1'] + .getService(Ci.nsIPrivateBrowsingService); Cu.import('resource://gre/modules/Services.jsm'); function log(str) { dump(str + '\n'); } +// watchWindows() and unload() are from Ed Lee's examples at +// https://github.com/Mardak/restartless/blob/watchWindows/bootstrap.js +/** + * Apply a callback to each open and new browser windows. + * + * @param {function} callback 1-parameter function that gets a browser window. + */ +function watchWindows(callback) { + // Wrap the callback in a function that ignores failures + function watcher(window) { + try { + // Now that the window has loaded, only handle browser windows + let {documentElement} = window.document; + if (documentElement.getAttribute('windowtype') == 'navigator:browser') + callback(window); + } + catch (ex) {} + } + // Wait for the window to finish loading before running the callback + function runOnLoad(window) { + // Listen for one load event before checking the window type + window.addEventListener('load', function runOnce() { + window.removeEventListener('load', runOnce, false); + watcher(window); + }, false); + } + + // Add functionality to existing windows + let windows = Services.wm.getEnumerator(null); + while (windows.hasMoreElements()) { + // Only run the watcher immediately if the window is completely loaded + let window = windows.getNext(); + if (window.document.readyState == 'complete') + watcher(window); + // Wait for the window to load before continuing + else + runOnLoad(window); + } + + // Watch for new browser windows opening then wait for it to load + function windowWatcher(subject, topic) { + if (topic == 'domwindowopened') + runOnLoad(subject); + } + Services.ww.registerNotification(windowWatcher); + + // Make sure to stop watching for windows if we're unloading + unload(function() Services.ww.unregisterNotification(windowWatcher)); +} + +/** + * Save callbacks to run when unloading. Optionally scope the callback to a + * container, e.g., window. Provide a way to run all the callbacks. + * + * @param {function} callback 0-parameter function to call on unload. + * @param {node} container Remove the callback when this container unloads. + * @return {function} A 0-parameter function that undoes adding the callback. + */ +function unload(callback, container) { + // Initialize the array of unloaders on the first usage + let unloaders = unload.unloaders; + if (unloaders == null) + unloaders = unload.unloaders = []; + + // Calling with no arguments runs all the unloader callbacks + if (callback == null) { + unloaders.slice().forEach(function(unloader) unloader()); + unloaders.length = 0; + return; + } + + // The callback is bound to the lifetime of the container if we have one + if (container != null) { + // Remove the unloader when the container unloads + container.addEventListener('unload', removeUnloader, false); + + // Wrap the callback to additionally remove the unload listener + let origCallback = callback; + callback = function() { + container.removeEventListener('unload', removeUnloader, false); + origCallback(); + } + } + + // Wrap the callback in a function that ignores failures + function unloader() { + try { + callback(); + } + catch (ex) {} + } + unloaders.push(unloader); + + // Provide a way to remove the unloader + function removeUnloader() { + let index = unloaders.indexOf(unloader); + if (index != -1) + unloaders.splice(index, 1); + } + return removeUnloader; +} + +function messageCallback(event) { + log(event.target.ownerDocument.currentScript); + var message = event.target, doc = message.ownerDocument; + var inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled; + // Verify the message came from a PDF. + // TODO + var action = message.getUserData('action'); + var data = message.getUserData('data'); + switch (action) { + case 'download': + Services.wm.getMostRecentWindow('navigator:browser').saveURL(data); + break; + case 'setDatabase': + if (inPrivateBrowswing) + return; + application.prefs.setValue(EXT_PREFIX + '.database', data); + break; + case 'getDatabase': + var response; + if (inPrivateBrowswing) + response = '{}'; + else + response = application.prefs.getValue(EXT_PREFIX + '.database', '{}'); + message.setUserData('response', response, null); + break; + } +} + + +// All the boostrap functions: function startup(aData, aReason) { let manifestPath = 'chrome.manifest'; let manifest = Cc['@mozilla.org/file/local;1'] @@ -26,11 +164,22 @@ function startup(aData, aReason) { } catch (e) { log(e); } + + watchWindows(function(window) { + window.addEventListener(PDFJS_EVENT_ID, messageCallback, false, true); + unload(function() { + window.removeEventListener(PDFJS_EVENT_ID, messageCallback, false, true); + }); + }); } function shutdown(aData, aReason) { - if (Services.prefs.getBoolPref('extensions.pdf.js.active')) + if (Services.prefs.getBoolPref('extensions.pdf.js.active')) { Services.prefs.setBoolPref('extensions.pdf.js.active', false); + // Clean up with unloaders when we're deactivating + if (aReason != APP_SHUTDOWN) + unload(); + } } function install(aData, aReason) { @@ -39,5 +188,6 @@ function install(aData, aReason) { function uninstall(aData, aReason) { Services.prefs.clearUserPref('extensions.pdf.js.active'); + application.prefs.setValue(EXT_PREFIX + '.database', '{}'); } diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index 320d69d30..edd8ee3e0 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -20,7 +20,7 @@ function log(aMsg) { dump(msg + '\n'); } -const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; +const NS_ERROR_NOT_IMPLEMENTED = 0x80004001; function pdfContentHandler() { } @@ -51,11 +51,13 @@ pdfContentHandler.prototype = { // nsIStreamConverter::convert convert: function(aFromStream, aFromType, aToType, aCtxt) { - return aFromStream; + return aFromStream; }, // nsIStreamConverter::asyncConvertData asyncConvertData: function(aFromType, aToType, aListener, aCtxt) { + if (!Services.prefs.getBoolPref('extensions.pdf.js.active')) + throw NS_ERROR_NOT_IMPLEMENTED; // Store the listener passed to us this.listener = aListener; }, @@ -73,15 +75,6 @@ pdfContentHandler.prototype = { // Cancel the request so the viewer can handle it. aRequest.cancel(Cr.NS_BINDING_ABORTED); - // Check if we should download. - var targetUrl = aRequest.originalURI.spec; - var downloadHash = targetUrl.indexOf('?#pdfjs.action=download'); - if (downloadHash >= 0) { - targetUrl = targetUrl.substring(0, downloadHash); - Services.wm.getMostRecentWindow("navigator:browser").saveURL(targetUrl); - return; - } - // Create a new channel that is viewer loaded as a resource. var ioService = Cc['@mozilla.org/network/io-service;1'] .getService(Ci.nsIIOService); diff --git a/web/viewer.js b/web/viewer.js index 55d0a595c..790b5ae63 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -61,6 +61,31 @@ var RenderingQueue = (function RenderingQueueClosure() { return RenderingQueue; })(); +var FirefoxCom = (function FirefoxComClosure() { + return { + /** + * Creates an event that hopefully the extension is listening for and will + * synchronously respond to. + * @param {String} action The action to trigger. + * @param {String} data Optional data to send. + * @return {*} The response. + */ + request: function(action, data) { + var request = document.createTextNode(''); + request.setUserData('action', action, null); + request.setUserData('data', data, null); + document.documentElement.appendChild(request); + + var sender = document.createEvent('Events'); + sender.initEvent('pdf.js.message', true, false); + request.dispatchEvent(sender); + var response = request.getUserData('response'); + document.documentElement.removeChild(request); + return response; + } + }; +})(); + // Settings Manager - This is a utility for saving settings // First we see if localStorage is available, FF bug #495747 // If not, we use FUEL in FF @@ -74,10 +99,14 @@ var Settings = (function SettingsClosure() { return true; })(); + var isFirefoxExtension = PDFJS.isFirefoxExtension; + function Settings(fingerprint) { var database = null; var index; - if (isLocalStorageEnabled) + if (isFirefoxExtension) + database = FirefoxCom.request('getDatabase', null); + else if (isLocalStorageEnabled) database = localStorage.getItem('database') || '{}'; else return false; @@ -106,8 +135,11 @@ var Settings = (function SettingsClosure() { set: function settingsSet(name, val) { var file = this.file; file[name] = val; - if (isLocalStorageEnabled) - localStorage.setItem('database', JSON.stringify(this.database)); + var database = JSON.stringify(this.database); + if (isFirefoxExtension) + FirefoxCom.request('setDatabase', database); + else if (isLocalStorageEnabled) + localStorage.setItem('database', database); }, get: function settingsGet(name, defaultValue) { @@ -250,13 +282,12 @@ var PDFView = { download: function pdfViewDownload() { var url = this.url.split('#')[0]; - // For the extension we add an extra '?' to force the page to reload, its - // stripped off by the extension. - if (PDFJS.isFirefoxExtension) - url += '?#pdfjs.action=download'; - else + if (PDFJS.isFirefoxExtension) { + FirefoxCom.request('download', url); + } else { url += '#pdfjs.action=download', '_parent'; - window.open(url, '_parent'); + window.open(url, '_parent'); + } }, navigateTo: function pdfViewNavigateTo(dest) { From 2b2e4b19abf2fc5728cf5551455174b8dbb60e22 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 25 Jan 2012 21:52:10 -0600 Subject: [PATCH 026/285] Fixing initial scale when named destination is specified --- web/viewer.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index dcd3f51b6..dd16b0282 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -6,6 +6,7 @@ var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf'; var kDefaultScale = 'auto'; var kDefaultScaleDelta = 1.1; +var kUnknownScale = 0; var kCacheSize = 20; var kCssUnits = 96.0 / 72.0; var kScrollbarPadding = 40; @@ -148,7 +149,7 @@ var currentPageNumber = 1; var PDFView = { pages: [], thumbnails: [], - currentScale: 0, + currentScale: kUnknownScale, currentScaleValue: null, initialBookmark: document.location.hash.substring(1), @@ -452,10 +453,16 @@ var PDFView = { } else if (storedHash) this.setHash(storedHash); - else { - this.parseScale(scale || kDefaultScale, true); + else if (scale) { + this.parseScale(scale, true); this.page = 1; } + + if (PDFView.currentScale === kUnknownScale) { + // Scale was not initialized: invalid bookmark or scale was not specified. + // Setting the default one. + this.parseScale(kDefaultScale, true); + } }, setHash: function pdfViewSetHash(hash) { @@ -742,6 +749,8 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, if (scale && scale !== PDFView.currentScale) PDFView.parseScale(scale, true); + else if (PDFView.currentScale === kUnknownScale) + PDFView.parseScale(kDefaultScale, true); setTimeout(function pageViewScrollIntoViewRelayout() { // letting page to re-layout before scrolling From dd066f8369cad79ca534c077af9f421da3f611e3 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 26 Jan 2012 18:51:58 -0600 Subject: [PATCH 027/285] Fixing standard encoding mapping --- src/fonts.js | 27 ++++++++++++++------------- test/pdfs/issue1127.pdf.link | 1 + test/test_manifest.json | 7 +++++++ 3 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 test/pdfs/issue1127.pdf.link diff --git a/src/fonts.js b/src/fonts.js index adcedd55c..7f72c8086 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -160,19 +160,20 @@ var Encodings = { 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'braceleft', 'bar', 'braceright', 'asciitilde', '', '', 'exclamdown', - 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', - 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', - 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger', 'daggerdbl', - 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase', - 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', - 'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex', - 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring', - 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron', 'emdash', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', 'AE', '', - 'ordfeminine', '', '', '', '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', - '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '', 'lslash', - 'oslash', 'oe', 'germandbls' + 'braceleft', 'bar', 'braceright', 'asciitilde', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', 'exclamdown', 'cent', 'sterling', + 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', + 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', + 'fl', '', 'endash', 'dagger', 'daggerdbl', 'periodcentered', '', + 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', + 'guillemotright', 'ellipsis', 'perthousand', '', 'questiondown', '', + 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', + 'dieresis', '', 'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron', + 'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'AE', '', 'ordfeminine', '', '', '', '', 'Lslash', 'Oslash', 'OE', + 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '', + 'lslash', 'oslash', 'oe', 'germandbls' ]); }, get WinAnsiEncoding() { diff --git a/test/pdfs/issue1127.pdf.link b/test/pdfs/issue1127.pdf.link new file mode 100644 index 000000000..2df2304ba --- /dev/null +++ b/test/pdfs/issue1127.pdf.link @@ -0,0 +1 @@ +https://vmp.ethz.ch/pdfs/diplome/vordiplome/Block%201/Algorithmen_%26_Komplexitaet/AlgoKo_f08_Aufg.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 648d1b49b..c6fed0a35 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -410,6 +410,13 @@ "link": true, "type": "eq" }, + { "id": "issue1127", + "file": "pdfs/issue1127.pdf", + "md5": "4fb2be5ffefeafda4ba977de2a1bb4d8", + "rounds": 1, + "link": true, + "type": "eq" + }, { "id": "liveprogramming", "file": "pdfs/liveprogramming.pdf", "md5": "7bd4dad1188232ef597d36fd72c33e52", From e7a0a2e1291fb744cdfa769fa3a8409d246078da Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 27 Jan 2012 10:53:07 -0800 Subject: [PATCH 028/285] Better way to listen to events and verify them. --- extensions/firefox/bootstrap.js | 148 +----------------- .../firefox/components/pdfContentHandler.js | 70 ++++++++- 2 files changed, 68 insertions(+), 150 deletions(-) diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js index 06fcf182f..f1a712c0c 100644 --- a/extensions/firefox/bootstrap.js +++ b/extensions/firefox/bootstrap.js @@ -9,149 +9,14 @@ let Cc = Components.classes; let Ci = Components.interfaces; let Cm = Components.manager; let Cu = Components.utils; -let application = Cc['@mozilla.org/fuel/application;1'] - .getService(Ci.fuelIApplication); -let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1'] - .getService(Ci.nsIPrivateBrowsingService); Cu.import('resource://gre/modules/Services.jsm'); function log(str) { dump(str + '\n'); } -// watchWindows() and unload() are from Ed Lee's examples at -// https://github.com/Mardak/restartless/blob/watchWindows/bootstrap.js -/** - * Apply a callback to each open and new browser windows. - * - * @param {function} callback 1-parameter function that gets a browser window. - */ -function watchWindows(callback) { - // Wrap the callback in a function that ignores failures - function watcher(window) { - try { - // Now that the window has loaded, only handle browser windows - let {documentElement} = window.document; - if (documentElement.getAttribute('windowtype') == 'navigator:browser') - callback(window); - } - catch (ex) {} - } - - // Wait for the window to finish loading before running the callback - function runOnLoad(window) { - // Listen for one load event before checking the window type - window.addEventListener('load', function runOnce() { - window.removeEventListener('load', runOnce, false); - watcher(window); - }, false); - } - - // Add functionality to existing windows - let windows = Services.wm.getEnumerator(null); - while (windows.hasMoreElements()) { - // Only run the watcher immediately if the window is completely loaded - let window = windows.getNext(); - if (window.document.readyState == 'complete') - watcher(window); - // Wait for the window to load before continuing - else - runOnLoad(window); - } - - // Watch for new browser windows opening then wait for it to load - function windowWatcher(subject, topic) { - if (topic == 'domwindowopened') - runOnLoad(subject); - } - Services.ww.registerNotification(windowWatcher); - - // Make sure to stop watching for windows if we're unloading - unload(function() Services.ww.unregisterNotification(windowWatcher)); -} - -/** - * Save callbacks to run when unloading. Optionally scope the callback to a - * container, e.g., window. Provide a way to run all the callbacks. - * - * @param {function} callback 0-parameter function to call on unload. - * @param {node} container Remove the callback when this container unloads. - * @return {function} A 0-parameter function that undoes adding the callback. - */ -function unload(callback, container) { - // Initialize the array of unloaders on the first usage - let unloaders = unload.unloaders; - if (unloaders == null) - unloaders = unload.unloaders = []; - - // Calling with no arguments runs all the unloader callbacks - if (callback == null) { - unloaders.slice().forEach(function(unloader) unloader()); - unloaders.length = 0; - return; - } - - // The callback is bound to the lifetime of the container if we have one - if (container != null) { - // Remove the unloader when the container unloads - container.addEventListener('unload', removeUnloader, false); - - // Wrap the callback to additionally remove the unload listener - let origCallback = callback; - callback = function() { - container.removeEventListener('unload', removeUnloader, false); - origCallback(); - } - } - - // Wrap the callback in a function that ignores failures - function unloader() { - try { - callback(); - } - catch (ex) {} - } - unloaders.push(unloader); - - // Provide a way to remove the unloader - function removeUnloader() { - let index = unloaders.indexOf(unloader); - if (index != -1) - unloaders.splice(index, 1); - } - return removeUnloader; -} - -function messageCallback(event) { - log(event.target.ownerDocument.currentScript); - var message = event.target, doc = message.ownerDocument; - var inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled; - // Verify the message came from a PDF. - // TODO - var action = message.getUserData('action'); - var data = message.getUserData('data'); - switch (action) { - case 'download': - Services.wm.getMostRecentWindow('navigator:browser').saveURL(data); - break; - case 'setDatabase': - if (inPrivateBrowswing) - return; - application.prefs.setValue(EXT_PREFIX + '.database', data); - break; - case 'getDatabase': - var response; - if (inPrivateBrowswing) - response = '{}'; - else - response = application.prefs.getValue(EXT_PREFIX + '.database', '{}'); - message.setUserData('response', response, null); - break; - } -} -// All the boostrap functions: function startup(aData, aReason) { let manifestPath = 'chrome.manifest'; let manifest = Cc['@mozilla.org/file/local;1'] @@ -164,22 +29,11 @@ function startup(aData, aReason) { } catch (e) { log(e); } - - watchWindows(function(window) { - window.addEventListener(PDFJS_EVENT_ID, messageCallback, false, true); - unload(function() { - window.removeEventListener(PDFJS_EVENT_ID, messageCallback, false, true); - }); - }); } function shutdown(aData, aReason) { - if (Services.prefs.getBoolPref('extensions.pdf.js.active')) { + if (Services.prefs.getBoolPref('extensions.pdf.js.active')) Services.prefs.setBoolPref('extensions.pdf.js.active', false); - // Clean up with unloaders when we're deactivating - if (aReason != APP_SHUTDOWN) - unload(); - } } function install(aData, aReason) { diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js index edd8ee3e0..1c6c72c81 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/pdfContentHandler.js @@ -7,8 +7,10 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; - +const PDFJS_EVENT_ID = 'pdf.js.message'; const PDF_CONTENT_TYPE = 'application/pdf'; +const NS_ERROR_NOT_IMPLEMENTED = 0x80004001; +const EXT_PREFIX = 'extensions.uriloader@pdf.js'; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); @@ -19,8 +21,50 @@ function log(aMsg) { .logStringMessage(msg); dump(msg + '\n'); } +let application = Cc['@mozilla.org/fuel/application;1'] + .getService(Ci.fuelIApplication); +let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1'] + .getService(Ci.nsIPrivateBrowsingService); +let inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled; + +// All the priviledged actions. +function ChromeActions() { + this.inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled; +} +ChromeActions.prototype = { + download: function(data) { + Services.wm.getMostRecentWindow('navigator:browser').saveURL(data); + }, + setDatabase: function() { + if (this.inPrivateBrowswing) + return; + application.prefs.setValue(EXT_PREFIX + '.database', data); + }, + getDatabase: function() { + if (this.inPrivateBrowswing) + return '{}'; + return application.prefs.getValue(EXT_PREFIX + '.database', '{}'); + } +}; + +// Event listener to trigger chrome privedged code. +function RequestListener(actions) { + this.actions = actions; +} +// Recieves an event and synchronously responds. +RequestListener.prototype.recieve = function(event) { + var message = event.target; + var action = message.getUserData('action'); + var data = message.getUserData('data'); + var actions = this.actions; + if (!(action in actions)) { + log('Unknown action: ' + action); + return; + } + var response = actions[action].call(this.actions, data); + message.setUserData('response', response, null); +}; -const NS_ERROR_NOT_IMPLEMENTED = 0x80004001; function pdfContentHandler() { } @@ -70,6 +114,7 @@ pdfContentHandler.prototype = { // nsIRequestObserver::onStartRequest onStartRequest: function(aRequest, aContext) { + // Setup the request so we can use it below. aRequest.QueryInterface(Ci.nsIChannel); // Cancel the request so the viewer can handle it. @@ -80,15 +125,34 @@ pdfContentHandler.prototype = { .getService(Ci.nsIIOService); var channel = ioService.newChannel( 'resource://pdf.js/web/viewer.html', null, null); + // Keep the URL the same so the browser sees it as the same. channel.originalURI = aRequest.originalURI; channel.asyncOpen(this.listener, aContext); + + // Setup a global listener waiting for the next DOM to be created and verfiy + // that its the one we want by its URL. When the correct DOM is found create + // an event listener on that window for the pdf.js events that require + // chrome priviledges. + var url = aRequest.originalURI.spec; + var gb = Services.wm.getMostRecentWindow('navigator:browser'); + var domListener = function domListener(event) { + var doc = event.originalTarget; + var win = doc.defaultView; + if (doc.location.href === url) { + gb.removeEventListener('DOMContentLoaded', domListener); + var requestListener = new RequestListener(new ChromeActions()); + win.addEventListener(PDFJS_EVENT_ID, function(event) { + requestListener.recieve(event); + }, false, true); + } + }; + gb.addEventListener('DOMContentLoaded', domListener, false); }, // nsIRequestObserver::onStopRequest onStopRequest: function(aRequest, aContext, aStatusCode) { // Do nothing. - return; } }; From 337806deed4f75ba7d93bd14963264588c11b72e Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 27 Jan 2012 10:53:37 -0800 Subject: [PATCH 029/285] Fix url for thumbnail. (still not working 100) --- web/viewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/viewer.js b/web/viewer.js index 790b5ae63..3431e18c4 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -862,7 +862,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, var ThumbnailView = function thumbnailView(container, page, id, pageRatio) { var anchor = document.createElement('a'); - anchor.href = '#' + id; + anchor.href = PDFView.getAnchorUrl('#page=' + id); anchor.onclick = function stopNivigation() { PDFView.page = id; return false; From f46b0474ce4bad106f3c6dd813f23af8459e1479 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 27 Jan 2012 11:02:27 -0800 Subject: [PATCH 030/285] Change name to reflect what it is now. Change GUID. --- extensions/firefox/chrome.manifest | 4 ++-- .../{pdfContentHandler.js => PdfStreamConverter.js} | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) rename extensions/firefox/components/{pdfContentHandler.js => PdfStreamConverter.js} (95%) diff --git a/extensions/firefox/chrome.manifest b/extensions/firefox/chrome.manifest index ec7c9a964..5351257e7 100644 --- a/extensions/firefox/chrome.manifest +++ b/extensions/firefox/chrome.manifest @@ -1,5 +1,5 @@ resource pdf.js content/ -component {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} components/pdfContentHandler.js -contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {2278dfd0-b75c-11e0-8257-1ba3d93c9f1a} +component {6457a96b-2d68-439a-bcfa-44465fbcdbb1} components/PdfStreamConverter.js +contract @mozilla.org/streamconv;1?from=application/pdf&to=*/* {6457a96b-2d68-439a-bcfa-44465fbcdbb1} diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/PdfStreamConverter.js similarity index 95% rename from extensions/firefox/components/pdfContentHandler.js rename to extensions/firefox/components/PdfStreamConverter.js index 1c6c72c81..83c930d51 100644 --- a/extensions/firefox/components/pdfContentHandler.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -16,7 +16,7 @@ Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); function log(aMsg) { - let msg = 'pdfContentHandler.js: ' + (aMsg.join ? aMsg.join('') : aMsg); + let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg); Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService) .logStringMessage(msg); dump(msg + '\n'); @@ -66,13 +66,13 @@ RequestListener.prototype.recieve = function(event) { }; -function pdfContentHandler() { +function PdfStreamConverter() { } -pdfContentHandler.prototype = { +PdfStreamConverter.prototype = { // properties required for XPCOM registration: - classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'), + classID: Components.ID('{6457a96b-2d68-439a-bcfa-44465fbcdbb1}'), classDescription: 'pdf.js Component', contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*', @@ -156,4 +156,4 @@ pdfContentHandler.prototype = { } }; -var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]); +var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfStreamConverter]); From 10a0a60f8e7ae231a44a91a7b1df9ab1ff166be8 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Fri, 27 Jan 2012 18:53:05 -0600 Subject: [PATCH 031/285] Fixing symbols encoding --- src/evaluator.js | 9 ++++++--- src/fonts.js | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/evaluator.js b/src/evaluator.js index 21530f42f..c70013d25 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -481,8 +481,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { properties.cidToGidMap = this.readCidToGidMap(cidToGidMap); } + var flags = properties.flags; var differences = []; - var baseEncoding = Encodings.StandardEncoding; + var baseEncoding = !!(flags & FontFlags.Symbolic) ? + Encodings.symbolsEncoding : Encodings.StandardEncoding; var hasEncoding = dict.has('Encoding'); if (hasEncoding) { var encoding = xref.fetchIfRef(dict.get('Encoding')); @@ -761,8 +763,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { // Simulating descriptor flags attribute var fontNameWoStyle = baseFontName.split('-')[0]; var flags = (serifFonts[fontNameWoStyle] || - (fontNameWoStyle.search(/serif/gi) != -1) ? 2 : 0) | - (symbolsFonts[fontNameWoStyle] ? 4 : 32); + (fontNameWoStyle.search(/serif/gi) != -1) ? FontFlags.Serif : 0) | + (symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic : + FontFlags.Nonsymbolic); var properties = { type: type.name, diff --git a/src/fonts.js b/src/fonts.js index 7f72c8086..bb91d7969 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -19,6 +19,18 @@ var kPDFGlyphSpaceUnits = 1000; // Until hinting is fully supported this constant can be used var kHintingEnabled = false; +var FontFlags = { + FixedPitch: 1, + Serif: 2, + Symbolic: 4, + Script: 8, + Nonsymbolic: 32, + Italic: 64, + AllCap: 65536, + SmallCap: 131072, + ForceBold: 262144 +}; + var Encodings = { get ExpertEncoding() { return shadow(this, 'ExpertEncoding', ['', '', '', '', '', '', '', '', '', @@ -762,8 +774,8 @@ var Font = (function FontClosure() { var names = name.split('+'); names = names.length > 1 ? names[1] : names[0]; names = names.split(/[-,_]/g)[0]; - this.isSerifFont = !!(properties.flags & 2); - this.isSymbolicFont = !!(properties.flags & 4); + this.isSerifFont = !!(properties.flags & FontFlags.Serif); + this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic); var type = properties.type; this.type = type; From 5415fed14d13a117438efef12a2ea616479785c1 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Fri, 27 Jan 2012 20:36:27 -0600 Subject: [PATCH 032/285] Mapping well-known chars to the similar equivalents in the normal characters range --- src/fonts.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index bb91d7969..3f618b82a 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -418,6 +418,19 @@ var symbolsFonts = { 'Dingbats': true, 'Symbol': true, 'ZapfDingbats': true }; +// Some characters, e.g. copyrightserif, mapped to the private use area and +// might not be displayed using standard fonts. Mapping/hacking well-known chars +// to the similar equivalents in the normal characters range. +function mapPrivateUseChars(code) { + switch (code) { + case 0xF8E9: // copyrightsans + case 0xF6D9: // copyrightserif + return 0x00A9; // copyright + default: + return code; + } +} + var FontLoader = { listeningForFontLoad: false, @@ -2199,7 +2212,7 @@ var Font = (function FontClosure() { case 'CIDFontType0': if (this.noUnicodeAdaptation) { width = this.widths[this.unicodeToCID[charcode] || charcode]; - unicode = charcode; + unicode = mapPrivateUseChars(charcode); break; } unicode = this.toUnicode[charcode] || charcode; @@ -2207,7 +2220,7 @@ var Font = (function FontClosure() { case 'CIDFontType2': if (this.noUnicodeAdaptation) { width = this.widths[this.unicodeToCID[charcode] || charcode]; - unicode = charcode; + unicode = mapPrivateUseChars(charcode); break; } unicode = this.toUnicode[charcode] || charcode; @@ -2217,7 +2230,7 @@ var Font = (function FontClosure() { if (!isNum(width)) width = this.widths[glyphName]; if (this.noUnicodeAdaptation) { - unicode = GlyphsUnicode[glyphName] || charcode; + unicode = mapPrivateUseChars(GlyphsUnicode[glyphName] || charcode); break; } unicode = this.glyphNameMap[glyphName] || From 28f82e3dd7c3075c221e8e01ab359c87470a3791 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 29 Jan 2012 11:09:33 -0800 Subject: [PATCH 033/285] rewrite CMYK to RGB conversion --- src/colorspace.js | 57 ++++++++--------------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) diff --git a/src/colorspace.js b/src/colorspace.js index 827fd2e19..ad808df95 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -369,55 +369,16 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { DeviceCmykCS.prototype = { getRgb: function cmykcs_getRgb(color) { var c = color[0], m = color[1], y = color[2], k = color[3]; - var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k; - var x, r, g, b; - // this is a matrix multiplication, unrolled for performance - // code is taken from the poppler implementation - x = c1 * m1 * y1 * k1; // 0 0 0 0 - r = g = b = x; - x = c1 * m1 * y1 * k; // 0 0 0 1 - r += 0.1373 * x; - g += 0.1216 * x; - b += 0.1255 * x; - x = c1 * m1 * y * k1; // 0 0 1 0 - r += x; - g += 0.9490 * x; - x = c1 * m1 * y * k; // 0 0 1 1 - r += 0.1098 * x; - g += 0.1020 * x; - x = c1 * m * y1 * k1; // 0 1 0 0 - r += 0.9255 * x; - b += 0.5490 * x; - x = c1 * m * y1 * k; // 0 1 0 1 - r += 0.1412 * x; - x = c1 * m * y * k1; // 0 1 1 0 - r += 0.9294 * x; - g += 0.1098 * x; - b += 0.1412 * x; - x = c1 * m * y * k; // 0 1 1 1 - r += 0.1333 * x; - x = c * m1 * y1 * k1; // 1 0 0 0 - g += 0.6784 * x; - b += 0.9373 * x; - x = c * m1 * y1 * k; // 1 0 0 1 - g += 0.0588 * x; - b += 0.1412 * x; - x = c * m1 * y * k1; // 1 0 1 0 - g += 0.6510 * x; - b += 0.3137 * x; - x = c * m1 * y * k; // 1 0 1 1 - g += 0.0745 * x; - x = c * m * y1 * k1; // 1 1 0 0 - r += 0.1804 * x; - g += 0.1922 * x; - b += 0.5725 * x; - x = c * m * y1 * k; // 1 1 0 1 - b += 0.0078 * x; - x = c * m * y * k1; // 1 1 1 0 - r += 0.2118 * x; - g += 0.2119 * x; - b += 0.2235 * x; + // CMYK -> CMY: http://www.easyrgb.com/index.php?X=MATH&H=14#text14 + c = (c * (1-k) + k); + m = (m * (1-k) + k); + y = (y * (1-k) + k); + + // CMY -> RGB: http://www.easyrgb.com/index.php?X=MATH&H=12#text12 + r = (1-c); + g = (1-m); + b = (1-y); return [r, g, b]; }, From e3a3ec6f2e3b57bd310ed8cb0d663946dae9b2d2 Mon Sep 17 00:00:00 2001 From: Kalervo Kujala Date: Sun, 29 Jan 2012 22:25:06 +0200 Subject: [PATCH 034/285] Use JPX and JPEG in error messages. Also throw in workerConsole. --- src/jpx.js | 16 ++++++++-------- src/stream.js | 2 +- src/worker.js | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/jpx.js b/src/jpx.js index c212c6fd0..a15c3db54 100644 --- a/src/jpx.js +++ b/src/jpx.js @@ -1052,7 +1052,7 @@ var JpxImage = (function JpxImageClosure() { } r = 0; } - error('Out of packets'); + error('JPX error: Out of packets'); }; } function ResolutionLayerComponentPositionIterator(context) { @@ -1091,7 +1091,7 @@ var JpxImage = (function JpxImageClosure() { } l = 0; } - error('Out of packets'); + error('JPX error: Out of packets'); }; } function buildPackets(context) { @@ -1187,7 +1187,7 @@ var JpxImage = (function JpxImageClosure() { new ResolutionLayerComponentPositionIterator(context); break; default: - error('Unsupported progression order ' + progressionOrder); + error('JPX error: Unsupported progression order ' + progressionOrder); } } function parseTilePackets(context, data, offset, dataLength) { @@ -1589,7 +1589,7 @@ var JpxImage = (function JpxImageClosure() { if (lbox == 0) lbox = length - position + headerSize; if (lbox < headerSize) - error('Invalid box field size'); + error('JPX error: Invalid box field size'); var dataLength = lbox - headerSize; var jumpDataLength = true; switch (tbox) { @@ -1675,7 +1675,7 @@ var JpxImage = (function JpxImageClosure() { scalarExpounded = true; break; default: - error('Invalid SQcd value ' + sqcd); + error('JPX error: Invalid SQcd value ' + sqcd); } qcd.noQuantization = spqcdSize == 8; qcd.scalarExpounded = scalarExpounded; @@ -1728,7 +1728,7 @@ var JpxImage = (function JpxImageClosure() { scalarExpounded = true; break; default: - error('Invalid SQcd value ' + sqcd); + error('JPX error: Invalid SQcd value ' + sqcd); } qcc.noQuantization = spqcdSize == 8; qcc.scalarExpounded = scalarExpounded; @@ -1795,7 +1795,7 @@ var JpxImage = (function JpxImageClosure() { cod.terminationOnEachCodingPass || cod.verticalyStripe || cod.predictableTermination || cod.segmentationSymbolUsed) - error('Unsupported COD options: ' + uneval(cod)); + error('JPX error: Unsupported COD options: ' + uneval(cod)); if (context.mainHeader) context.COD = cod; @@ -1840,7 +1840,7 @@ var JpxImage = (function JpxImageClosure() { // skipping content break; default: - error('Unknown codestream code: ' + code.toString(16)); + error('JPX error: Unknown codestream code: ' + code.toString(16)); } position += length; } diff --git a/src/stream.js b/src/stream.js index c7b7c83e6..fc163171f 100644 --- a/src/stream.js +++ b/src/stream.js @@ -832,7 +832,7 @@ var JpegStream = (function JpegStreamClosure() { this.buffer = data; this.bufferLength = data.length; } catch (e) { - error(e); + error('JPEG error: ' + e); } }; JpegStream.prototype.getIR = function jpegStreamGetIR() { diff --git a/src/worker.js b/src/worker.js index f9777d7df..b81ff0540 100644 --- a/src/worker.js +++ b/src/worker.js @@ -208,6 +208,7 @@ var workerConsole = { action: 'console_error', data: args }); + throw 'pdf.js execution error'; }, time: function time(name) { From 9650df5c10a51976d1b8600d79ff244274e5764c Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sun, 29 Jan 2012 15:05:26 -0800 Subject: [PATCH 035/285] fix style and add var declaration --- src/colorspace.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/colorspace.js b/src/colorspace.js index ad808df95..d67d928b1 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -371,14 +371,14 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { var c = color[0], m = color[1], y = color[2], k = color[3]; // CMYK -> CMY: http://www.easyrgb.com/index.php?X=MATH&H=14#text14 - c = (c * (1-k) + k); - m = (m * (1-k) + k); - y = (y * (1-k) + k); + c = (c * (1 - k) + k); + m = (m * (1 - k) + k); + y = (y * (1 - k) + k); // CMY -> RGB: http://www.easyrgb.com/index.php?X=MATH&H=12#text12 - r = (1-c); - g = (1-m); - b = (1-y); + var r = (1 - c); + var g = (1 - m); + var b = (1 - y); return [r, g, b]; }, From 59283bdf6d439fdcd1a0ab07b318b48031091b34 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Mon, 30 Jan 2012 01:38:28 -0800 Subject: [PATCH 036/285] implement sampled functions based on the PDF spec --- src/function.js | 126 ++++++++++++++++-------------------------------- 1 file changed, 42 insertions(+), 84 deletions(-) diff --git a/src/function.js b/src/function.js index 26b8fe679..3093b0a99 100644 --- a/src/function.js +++ b/src/function.js @@ -125,109 +125,67 @@ var PDFFunction = (function PDFFunctionClosure() { else decode = toMultiArray(decode); - // Precalc the multipliers - var inputMul = new Float64Array(inputSize); - for (var i = 0; i < inputSize; ++i) { - inputMul[i] = (encode[i][1] - encode[i][0]) / - (domain[i][1] - domain[i][0]); - } - - var idxMul = new Int32Array(inputSize); - idxMul[0] = outputSize; - for (i = 1; i < inputSize; ++i) { - idxMul[i] = idxMul[i - 1] * size[i - 1]; - } - - var nSamples = outputSize; - for (i = 0; i < inputSize; ++i) - nSamples *= size[i]; - var samples = this.getSampleArray(size, outputSize, bps, str); return [ CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size, - outputSize, bps, range, inputMul, idxMul, nSamples + outputSize, Math.pow(2, bps) - 1, range ]; }, constructSampledFromIR: function pdfFunctionConstructSampledFromIR(IR) { - var inputSize = IR[1]; - var domain = IR[2]; - var encode = IR[3]; - var decode = IR[4]; - var samples = IR[5]; - var size = IR[6]; - var outputSize = IR[7]; - var bps = IR[8]; - var range = IR[9]; - var inputMul = IR[10]; - var idxMul = IR[11]; - var nSamples = IR[12]; + // See chapter 3, page 109 of the PDF reference + function interpolate(x, xmin, xmax, ymin, ymax) { + return ymin + ((x - xmin) * ((ymax - ymin) / (xmax - xmin))); + } return function constructSampledFromIRResult(args) { - if (inputSize != args.length) + // See chapter 3, page 110 of the PDF reference. + var m = IR[1]; + var domain = IR[2]; + var encode = IR[3]; + var decode = IR[4]; + var samples = IR[5]; + var size = IR[6]; + var n = IR[7]; + var mask = IR[8]; + var range = IR[9]; + + if (m != args.length) error('Incorrect number of arguments: ' + inputSize + ' != ' + args.length); - // Most of the below is a port of Poppler's implementation. - // TODO: There's a few other ways to do multilinear interpolation such - // as piecewise, which is much faster but an approximation. - var out = new Float64Array(outputSize); - var x; - var e = new Array(inputSize); - var efrac0 = new Float64Array(inputSize); - var efrac1 = new Float64Array(inputSize); - var sBuf = new Float64Array(1 << inputSize); - var i, j, k, idx, t; - // map input values into sample array - for (i = 0; i < inputSize; ++i) { - x = (args[i] - domain[i][0]) * inputMul[i] + encode[i][0]; - if (x < 0) { - x = 0; - } else if (x > size[i] - 1) { - x = size[i] - 1; - } - e[i] = [Math.floor(x), 0]; - if ((e[i][1] = e[i][0] + 1) >= size[i]) { - // this happens if in[i] = domain[i][1] - e[i][1] = e[i][0]; - } - efrac1[i] = x - e[i][0]; - efrac0[i] = 1 - efrac1[i]; - } + var x = args; + var y = new Float64Array(n * m); - // for each output, do m-linear interpolation - for (i = 0; i < outputSize; ++i) { + // Map x_i to y_j for 0 <= i < m using the sampled function. + for (var i = 0; i < m; ++i) { + // x_i' = min(max(x_i, Domain_2i), Domain_2i+1) + var domain_2i = domain[2 * i]; + var domain_2i_1 = domain[2 * i + 1]; + var xi = Math.min(Math.max(x[i], domain_2i), domain_2i_1); - // pull 2^m values out of the sample array - for (j = 0; j < (1 << inputSize); ++j) { - idx = i; - for (k = 0, t = j; k < inputSize; ++k, t >>= 1) { - idx += idxMul[k] * (e[k][t & 1]); - } - if (idx >= 0 && idx < nSamples) { - sBuf[j] = samples[idx]; - } else { - sBuf[j] = 0; // TODO Investigate if this is what Adobe does - } - } + // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1, Encode_2i, Encode_2i+1) + var e = interpolate(xi, domain_2i, domain_2i_1, encode[2 * i], encode[2 * i + 1]); - // do m sets of interpolations - for (j = 0, t = (1 << inputSize); j < inputSize; ++j, t >>= 1) { - for (k = 0; k < t; k += 2) { - sBuf[k >> 1] = efrac0[j] * sBuf[k] + efrac1[j] * sBuf[k + 1]; - } - } + // e_i' = min(max(e_i, 0), Size_i - 1) + e = Math.min(Math.max(e, 0), size[i] - 1); - // map output value to range - out[i] = (sBuf[0] * (decode[i][1] - decode[i][0]) + decode[i][0]); - if (out[i] < range[i][0]) { - out[i] = range[i][0]; - } else if (out[i] > range[i][1]) { - out[i] = range[i][1]; + var in = i * n; + + for (var j = 0; j < n; ++j) { + // average the two nearest neighbors in the sampling table + var rj = (samples[Math.floor(e) * n + j] + samples[Math.ceil(e) * n + j]) / 2; + + // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1, Decode_2j, Decode_2j+1) + rj = interpolate(rj, 0, mask, 1, decode[2 * j], decode[2 * j + 1]); + + // y_j = min(max(r_j, range_2j, range_2j+1) + y[in + j] = Math.min(Math.max(rj, range[2 * j], range[2 * j + 1])); } } - return out; + + return y; } }, From 30a01c5da6eeef910228c234619a26fdaf99f524 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Mon, 30 Jan 2012 09:24:49 -0500 Subject: [PATCH 037/285] addressing reviewer comments, bug fix --- src/evaluator.js | 4 ++-- web/viewer.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/evaluator.js b/src/evaluator.js index 1c277e027..e1e064f07 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -783,8 +783,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { dict: baseDict, properties: properties }; - } // if (type.name == 'Type3') - } // if (!descriptor) + } + } // According to the spec if 'FontDescriptor' is declared, 'FirstChar', // 'LastChar' and 'Widths' should exist too, but some PDF encoders seem diff --git a/web/viewer.js b/web/viewer.js index 9f8b772ab..e0ab1cc90 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -358,7 +358,7 @@ var PDFView = { } else { if (moreInfo.filename) errorMoreInfo.value += '\n' + 'File: ' + moreInfo.filename; - if (moreInfo.filename) + if (moreInfo.lineNumber) errorMoreInfo.value += '\n' + 'Line: ' + moreInfo.lineNumber; } } From 8068ff242d0d4c067743f9d55ae3886a240e733c Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 09:01:04 -0500 Subject: [PATCH 038/285] readXRefTable rewrite, progress --- src/obj.js | 71 +++++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/obj.js b/src/obj.js index ef7932546..0bf29347c 100644 --- a/src/obj.js +++ b/src/obj.js @@ -287,44 +287,49 @@ var XRef = (function XRefClosure() { XRef.prototype = { readXRefTable: function readXRefTable(parser) { - var obj; - while (true) { - if (isCmd(obj = parser.getObj(), 'trailer')) - break; - if (!isInt(obj)) + // Example of cross-reference table: + // xref + // 0 1 <-- subsection header (first obj #, obj count) + // 0000000000 65535 f <-- actual object (offset, generation #, f/n) + // 23 2 <-- subsection header ... and so on ... + // 0000025518 00002 n + // 0000025635 00000 n + // trailer + // ... + + // Outer loop is over subsection headers + var first; + while (!isCmd(first = parser.getObj(), 'trailer')) { + var count = parser.getObj(); + + if (!isInt(first) || !isInt(count)) error('Invalid XRef table'); - var first = obj; - if (!isInt(obj = parser.getObj())) - error('Invalid XRef table'); - var n = obj; - if (first < 0 || n < 0 || (first + n) != ((first + n) | 0)) - error('Invalid XRef table: ' + first + ', ' + n); - for (var i = first; i < first + n; ++i) { - var entry = {}; - if (!isInt(obj = parser.getObj())) - error('Invalid XRef table: ' + first + ', ' + n); - entry.offset = obj; - if (!isInt(obj = parser.getObj())) - error('Invalid XRef table: ' + first + ', ' + n); - entry.gen = obj; - obj = parser.getObj(); - if (isCmd(obj, 'n')) { - entry.uncompressed = true; - } else if (isCmd(obj, 'f')) { + + // Inner loop is over objects themselves + for (var i = first; i < first + count; ++i) { + var entry = {}; + entry.offset = parser.getObj(); + entry.gen = parser.getObj(); + var type = parser.getObj(); + + if (type === 'f') entry.free = true; - } else { - error('Invalid XRef table: ' + first + ', ' + n); + else if (type === 'n') + entry.uncompressed = true; + + // Validate entry obj + if ( !isInt(entry.offset) || !isInt(entry.gen) || + !(('free' in entry) || ('uncompressed' in entry)) ) { + error('Invalid XRef table: ' + first + ', ' + count); } - if (!this.entries[i]) { - // In some buggy PDF files the xref table claims to start at 1 - // instead of 0. - if (i == 1 && first == 1 && - entry.offset == 0 && entry.gen == 65535 && entry.free) { - i = first = 0; - } + + if (!this.entries[i]) this.entries[i] = entry; - } } + + // No objects added? + if (i - first <= 0) + error('Invalid XRef table: ' + first + ', ' + count); } // read the trailer dictionary From 4375bd22194d78e5e123c100a7c5e93d325d36ab Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 09:57:12 -0500 Subject: [PATCH 039/285] progress --- src/obj.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/obj.js b/src/obj.js index 0bf29347c..0db057b68 100644 --- a/src/obj.js +++ b/src/obj.js @@ -303,32 +303,32 @@ var XRef = (function XRefClosure() { var count = parser.getObj(); if (!isInt(first) || !isInt(count)) - error('Invalid XRef table'); + error('Invalid XRef table: wrong types in subsection header'); // Inner loop is over objects themselves - for (var i = first; i < first + count; ++i) { + for (var i = 0; i < count; i++) { var entry = {}; entry.offset = parser.getObj(); entry.gen = parser.getObj(); var type = parser.getObj(); - if (type === 'f') + if (isCmd(type, 'f')) entry.free = true; - else if (type === 'n') + else if (isCmd(type, 'n')) entry.uncompressed = true; // Validate entry obj if ( !isInt(entry.offset) || !isInt(entry.gen) || - !(('free' in entry) || ('uncompressed' in entry)) ) { + !(entry.free || entry.uncompressed) ) { error('Invalid XRef table: ' + first + ', ' + count); } - if (!this.entries[i]) - this.entries[i] = entry; + if (!this.entries[i + first]) + this.entries[i + first] = entry; } // No objects added? - if (i - first <= 0) + if (!(i > 0)) error('Invalid XRef table: ' + first + ', ' + count); } @@ -339,7 +339,7 @@ var XRef = (function XRefClosure() { // get the 'Prev' pointer var prev; - obj = dict.get('Prev'); + var obj = dict.get('Prev'); if (isInt(obj)) { prev = obj; } else if (isRef(obj)) { From 0959cd35172558b5a22647e846d43ece40e56c0c Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 10:49:06 -0500 Subject: [PATCH 040/285] New readXRefTable, working --- src/obj.js | 101 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/src/obj.js b/src/obj.js index 0db057b68..d03ee4889 100644 --- a/src/obj.js +++ b/src/obj.js @@ -298,9 +298,10 @@ var XRef = (function XRefClosure() { // ... // Outer loop is over subsection headers - var first; - while (!isCmd(first = parser.getObj(), 'trailer')) { - var count = parser.getObj(); + var obj; + while (!isCmd(obj = parser.getObj(), 'trailer')) { + var first = obj, + count = parser.getObj(); if (!isInt(first) || !isInt(count)) error('Invalid XRef table: wrong types in subsection header'); @@ -320,46 +321,35 @@ var XRef = (function XRefClosure() { // Validate entry obj if ( !isInt(entry.offset) || !isInt(entry.gen) || !(entry.free || entry.uncompressed) ) { - error('Invalid XRef table: ' + first + ', ' + count); + error('Invalid entry in XRef subsection: ' + first + ', ' + count); } if (!this.entries[i + first]) this.entries[i + first] = entry; } - - // No objects added? - if (!(i > 0)) - error('Invalid XRef table: ' + first + ', ' + count); } - // read the trailer dictionary - var dict; - if (!isDict(dict = parser.getObj())) - error('Invalid XRef table'); + // Sanity check: as per spec, first object must have these properties + if ( this.entries[0] && + !(this.entries[0].gen === 65535 && this.entries[0].free) ) + error('Invalid XRef table: unexpected first object'); - // get the 'Prev' pointer - var prev; - var obj = dict.get('Prev'); - if (isInt(obj)) { - prev = obj; - } else if (isRef(obj)) { - // certain buggy PDF generators generate "/Prev NNN 0 R" instead - // of "/Prev NNN" - prev = obj.num; - } - if (prev) { - this.readXRef(prev); - } + // Sanity check + if (!isCmd(obj, 'trailer')) + error('Invalid XRef table: could not find trailer dictionary'); - // check for 'XRefStm' key - if (isInt(obj = dict.get('XRefStm'))) { - var pos = obj; - // ignore previously loaded xref streams (possible infinite recursion) - if (!(pos in this.xrefstms)) { - this.xrefstms[pos] = 1; - this.readXRef(pos); - } - } + // Read trailer dictionary, e.g. + // trailer + // << /Size 22 + // /Root 20R + // /Info 10R + // /ID [ <81b14aafa313db63dbd6f981e49f94f4> ] + // >> + // The parser goes through the entire stream << ... >> and provides + // a getter interface for the key-value table + var dict = parser.getObj(); + if (!isDict(dict)) + error('Invalid XRef table: could not parse trailer dictionary'); return dict; }, @@ -412,9 +402,6 @@ var XRef = (function XRefClosure() { } range.splice(0, 2); } - var prev = streamParameters.get('Prev'); - if (isInt(prev)) - this.readXRef(prev); return streamParameters; }, indexObjects: function indexObjects() { @@ -534,22 +521,48 @@ var XRef = (function XRefClosure() { try { var parser = new Parser(new Lexer(stream), true); var obj = parser.getObj(); + var dict; - // parse an old-style xref table - if (isCmd(obj, 'xref')) - return this.readXRefTable(parser); + // Get dictionary + if (isCmd(obj, 'xref')) { + // Parse end-of-file XRef + dict = this.readXRefTable(parser); - // parse an xref stream - if (isInt(obj)) { + // Recursively get other XRefs 'XRefStm', if any + obj = dict.get('XRefStm'); + if (isInt(obj)) { + var pos = obj; + // ignore previously loaded xref streams + // (possible infinite recursion) + if (!(pos in this.xrefstms)) { + this.xrefstms[pos] = 1; + this.readXRef(pos); + } + } + } else if (isInt(obj)) { + // Parse in-stream XRef if (!isInt(parser.getObj()) || !isCmd(parser.getObj(), 'obj') || !isStream(obj = parser.getObj())) { error('Invalid XRef stream'); } - return this.readXRefStream(obj); + dict = this.readXRefStream(obj); } + + // Recursively get previous dictionary, if any + obj = dict.get('Prev'); + if (isInt(obj)) + this.readXRef(obj); + else if (isRef(obj)) { + // The spec says Prev must not be a reference, i.e. "/Prev NNN" + // This is a fallback for non-compliant PDFs, i.e. "/Prev NNN 0 R" + this.readXRef(obj.num); + } + + return dict; } catch (e) { - log('Reading of the xref table/stream failed: ' + e); + // log('(while reading XRef): ' + e); +error('(while reading XRef): ' + e); } warn('Indexing all PDF objects'); From 9e9674d45c025d20ed1480d485e905b51aedef98 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 10:50:30 -0500 Subject: [PATCH 041/285] Remove debugging line --- src/obj.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/obj.js b/src/obj.js index d03ee4889..2088839c0 100644 --- a/src/obj.js +++ b/src/obj.js @@ -561,8 +561,7 @@ var XRef = (function XRefClosure() { return dict; } catch (e) { - // log('(while reading XRef): ' + e); -error('(while reading XRef): ' + e); + log('(while reading XRef): ' + e); } warn('Indexing all PDF objects'); From 775290d69806726652c4c1f3fa1184f40e7c0492 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 10:57:32 -0500 Subject: [PATCH 042/285] Lint --- src/obj.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/obj.js b/src/obj.js index 2088839c0..8ac4706c0 100644 --- a/src/obj.js +++ b/src/obj.js @@ -292,11 +292,11 @@ var XRef = (function XRefClosure() { // 0 1 <-- subsection header (first obj #, obj count) // 0000000000 65535 f <-- actual object (offset, generation #, f/n) // 23 2 <-- subsection header ... and so on ... - // 0000025518 00002 n + // 0000025518 00002 n // 0000025635 00000 n // trailer // ... - + // Outer loop is over subsection headers var obj; while (!isCmd(obj = parser.getObj(), 'trailer')) { @@ -308,7 +308,7 @@ var XRef = (function XRefClosure() { // Inner loop is over objects themselves for (var i = 0; i < count; i++) { - var entry = {}; + var entry = {}; entry.offset = parser.getObj(); entry.gen = parser.getObj(); var type = parser.getObj(); @@ -319,19 +319,19 @@ var XRef = (function XRefClosure() { entry.uncompressed = true; // Validate entry obj - if ( !isInt(entry.offset) || !isInt(entry.gen) || - !(entry.free || entry.uncompressed) ) { + if (!isInt(entry.offset) || !isInt(entry.gen) || + !(entry.free || entry.uncompressed)) { error('Invalid entry in XRef subsection: ' + first + ', ' + count); } - + if (!this.entries[i + first]) this.entries[i + first] = entry; } } // Sanity check: as per spec, first object must have these properties - if ( this.entries[0] && - !(this.entries[0].gen === 65535 && this.entries[0].free) ) + if (this.entries[0] && + !(this.entries[0].gen === 65535 && this.entries[0].free)) error('Invalid XRef table: unexpected first object'); // Sanity check @@ -532,7 +532,7 @@ var XRef = (function XRefClosure() { obj = dict.get('XRefStm'); if (isInt(obj)) { var pos = obj; - // ignore previously loaded xref streams + // ignore previously loaded xref streams // (possible infinite recursion) if (!(pos in this.xrefstms)) { this.xrefstms[pos] = 1; From 1047f0264aeeef7c176a3878dc501ec12e02537c Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 15:31:26 -0500 Subject: [PATCH 043/285] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f12fce934..09cc95039 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ rendering PDFs, and eventually release a PDF reader extension powered by pdf.js. Integration with Firefox is a possibility if the experiment proves successful. - + # Getting started From 47f24cd27bf524b9738129abc83bd68f4bc2aef4 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 16:20:09 -0500 Subject: [PATCH 044/285] Show animated loading icon if page renders slow --- web/images/loading-icon.gif | Bin 0 -> 2545 bytes web/viewer.css | 14 +++++++++++++ web/viewer.js | 39 +++++++++++++++++++++++++----------- 3 files changed, 41 insertions(+), 12 deletions(-) create mode 100644 web/images/loading-icon.gif diff --git a/web/images/loading-icon.gif b/web/images/loading-icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..1c72ebb554be018511ae972c3f2361dff02dce02 GIT binary patch literal 2545 zcma*pX;2es8VB%~zPr=ibVMCx-JQ^BhLDAsK)^**h(ZDp9YGuzZ%~j!}+w%FI;|aC7){7CdVvG)P{bng1y9Te*f}~*`1kQl$jwb z$tlW~rRS!X?#xfm_&6tTdp_`cjgYwbRFLNdoJCN$S-yhg`ZnC-yvedRSmOh%;Y`Gl6bY$Z-}#C=#F4%9!I1b zWQ~f+9P?;vhCxWwlwl=lrWG|7IYo;{jjmzJ5R9?f>n%-d@>kLINUc z4wM5dAO;kq<$}Dk{2-u0$I6@2N}&cUx9nmV1dYc8jfC}%=F9WCg^OQK9C6poh#2!A z3^EU*UFZvS^)?bu3T?J;@Ahb~%I?+@4!l5!*TjC}GIslNan-RCrrd~PdHYnNLJk+m&`$Y+NV(e>CCu%R#_8GqY4cv#j`#uRWdsg9DxWy(?oOvgCU}&@jy%c!H&-Q zqXJxajAtmQRoRa9V-RFXXh-bK*;Fum{BjpkYQGX~i@OZ^Dx0n&H}kvGKqQ?w(6iGXu_g08T|_hp#ZvFzIwKF*a=oMJ~3UGAjZ?g}GOxm44td zXoyYrU*I=y*vHv89hkYH(v5R#wc)BC3dZJKb3K)f>zaM3%JP(mpecViP0eKKYf3zy z->jx_mc?mCtPEvCQ?uppk?eLJt}_IR7giW%Jr)RyI!+E-voIs*lXI*z`GQc_&D#X( z{6G};HPYj6O|$lXxBJeDaweqa{4L=tOZCjTI^&UOxXg})LRG_cr^B9Rqt(i5ORbQX zq`_xCRsH>xEYY%&*Nyi#{S_JZNlTm#K56`RI%7^amom;*h90Si&g1CfaFV3D|a!`3Y-GKKbL*KSbl z>I96`TR@CqPJl(>QqB~RvK~-U)`e`l4LIqj+IU^~yyIe*|BRVB>4Bup%j{tLdKz4j zY^<8P8m~GRGz*yv0&-RJE+-keJ+%m3wNeopzsltWd->eWmBVwUr)pX` zK~CD<;~Z*Uy3W`3+MrEYxm5qYQ!z%YI;y7DTG`UVH0;@{M{!B&id_}3DBQ?zsotuR zEGLdRx25nLm%-wjlnEi;-aN_1S7???rO~WgA67jjr&(vRa3y$u#kqJbeKnw z{!T!1li9>M+sJ6AUe+*9d}2uGjhzd z|L1Rtp8uTGYyZoQ*`DS^m2dw-X{a)l+3m?ncvn^+O>)hdd3(hMtlhkRGns{<8c0I! zDDjpmwtj?@!6kA|iu3q+Ai;@JR+ zfk+ln&YFC{4bhK6IxVgLs4W%^8Lk`qzWU*L>yq0A3;l}{!wKZ!ue)C)SKI)9dl1hl zhIRLV@8E}rwvE{gX(}$f6x*k)_`*Ijt1=EU-Ls6-(phomeQBgtUs z5Xz~Cd*nE)Ac!0i4ep}Z1AugMB(&F?)#CU{Qc{Sp^vKsdL}vRB30H+Bbzrn`M##H3 z{W8dc_mDroEE+p8_}mnJtzZ4!RNe)zhB)Ds;S57nYSJxtek>^~&(7B+N5MPf2+2xx z5Dl&4X|c@f{Kd|z1r+N|$DmsoVp*3yOdxT^J^-VAk)Z@$4^XrPrFP-Co+MXZ+KJ(W z{JNYvraLLWA;&tRhIKOvhW|HC|L-dLvAUF(MG0(Nl?4tB{RzN7I(}Cb%hwN{crFC8 zji#aJElKvDFV+&VI1V?oUMA>*kto0^;3W8FQBSZ|{ z$v~TqE=(8DZa^i$^oht&h};P1N&wMXorKh*Z68gPV&ouy>%f36Oqkwemyeas$Qbz# zV?7Jy%o7KY6^I=P@eCji%W`o5sf(5hySYo9$l4e2`(hIV_?=H-#R6}0$WVA|*(K@3 z=5?@RlcLh(meW%A4)hGzcvEpm(_w?>zhL*i&s9$2>r zAtk{8Cia|+Y+V!uX9BtpXoF%lswuRKsM!pSs!?yhlCy!269K0|b M?FSZn2B>%I-}ej|s{jB1 literal 0 HcmV?d00001 diff --git a/web/viewer.css b/web/viewer.css index e355f7fc2..65f2928fa 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -235,6 +235,20 @@ canvas { -webkit-box-shadow: 0px 2px 10px #ff0; } +.loadingIcon { + position: absolute; + display: none; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: url('images/loading-icon.gif') center no-repeat; */ +} + +.loadingIcon.show { + display: block; +} + .textLayer { position: absolute; left: 0; diff --git a/web/viewer.js b/web/viewer.js index dd16b0282..f319fcdc1 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -790,6 +790,10 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, div.appendChild(canvas); this.canvas = canvas; + var loadingIconDiv = document.createElement('div'); + loadingIconDiv.className = 'loadingIcon'; + div.appendChild(loadingIconDiv); + var textLayerDiv = null; if (!PDFJS.disableTextLayer) { textLayerDiv = document.createElement('div'); @@ -809,19 +813,30 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, ctx.restore(); ctx.translate(-this.x * scale, -this.y * scale); - stats.begin = Date.now(); - this.content.startRendering(ctx, - (function pageViewDrawCallback(error) { - if (error) - PDFView.error('An error occurred while rendering the page.', error); - this.updateStats(); - if (this.onAfterDraw) - this.onAfterDraw(); + // Rendering area - cache.push(this); - callback(); - }).bind(this), textLayer - ); + var self = this; + + // Display loading icon if page hasn't finished rendering after XXXX ms + var loadingTimer = setTimeout(function loadingTimerCallback() { + loadingIconDiv.classList.add('show'); + }, 1000); + + stats.begin = Date.now(); + this.content.startRendering(ctx, function pageViewDrawCallback(error) { + clearTimeout(loadingTimer); + loadingIconDiv.classList.remove('show'); + + if (error) + PDFView.error('An error occurred while rendering the page.', error); + + self.updateStats(); + if (self.onAfterDraw) + self.onAfterDraw(); + + cache.push(self); + callback(); + }, textLayer); setupAnnotations(this.content, this.scale); div.setAttribute('data-loaded', true); From 5ef79dca14b2f54fbcd549c2d962f1a13c2d8be3 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 16:49:48 -0500 Subject: [PATCH 045/285] New version: show loading icon until page renders --- web/viewer.css | 6 +++--- web/viewer.js | 19 ++++++------------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index 65f2928fa..6f4856e17 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -237,7 +237,7 @@ canvas { .loadingIcon { position: absolute; - display: none; + display: block; left: 0; top: 0; right: 0; @@ -245,8 +245,8 @@ canvas { background: url('images/loading-icon.gif') center no-repeat; */ } -.loadingIcon.show { - display: block; +.loadingIcon.hide { + display: none; } .textLayer { diff --git a/web/viewer.js b/web/viewer.js index f319fcdc1..a999ec553 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -618,6 +618,10 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, div.removeAttribute('data-loaded'); delete this.canvas; + + this.loadingIconDiv = document.createElement('div'); + this.loadingIconDiv.className = 'loadingIcon'; + div.appendChild(this.loadingIconDiv); }; function setupAnnotations(content, scale) { @@ -774,7 +778,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, }; this.drawingRequired = function() { - return !div.hasChildNodes(); + return !div.querySelector('canvas'); }; this.draw = function pageviewDraw(callback) { @@ -790,10 +794,6 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, div.appendChild(canvas); this.canvas = canvas; - var loadingIconDiv = document.createElement('div'); - loadingIconDiv.className = 'loadingIcon'; - div.appendChild(loadingIconDiv); - var textLayerDiv = null; if (!PDFJS.disableTextLayer) { textLayerDiv = document.createElement('div'); @@ -816,16 +816,9 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, // Rendering area var self = this; - - // Display loading icon if page hasn't finished rendering after XXXX ms - var loadingTimer = setTimeout(function loadingTimerCallback() { - loadingIconDiv.classList.add('show'); - }, 1000); - stats.begin = Date.now(); this.content.startRendering(ctx, function pageViewDrawCallback(error) { - clearTimeout(loadingTimer); - loadingIconDiv.classList.remove('show'); + self.loadingIconDiv.classList.add('hide'); if (error) PDFView.error('An error occurred while rendering the page.', error); From 8950d0bca9429ab7f1040b209268823503fed29a Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Tue, 31 Jan 2012 16:53:54 -0500 Subject: [PATCH 046/285] Remove instead of hide icon --- web/viewer.css | 4 ---- web/viewer.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/web/viewer.css b/web/viewer.css index 6f4856e17..ddbba7f2f 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -245,10 +245,6 @@ canvas { background: url('images/loading-icon.gif') center no-repeat; */ } -.loadingIcon.hide { - display: none; -} - .textLayer { position: absolute; left: 0; diff --git a/web/viewer.js b/web/viewer.js index a999ec553..809966e84 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -818,7 +818,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight, var self = this; stats.begin = Date.now(); this.content.startRendering(ctx, function pageViewDrawCallback(error) { - self.loadingIconDiv.classList.add('hide'); + div.removeChild(self.loadingIconDiv); if (error) PDFView.error('An error occurred while rendering the page.', error); From 6cf3109329f122996d7343a67b4c67ebf7981ff9 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 31 Jan 2012 17:53:42 -0800 Subject: [PATCH 047/285] Address review concerns. --- Makefile | 6 +++--- extensions/firefox/components/PdfStreamConverter.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index eaaa3e81f..adeb91b12 100644 --- a/Makefile +++ b/Makefile @@ -252,16 +252,16 @@ extension: | production @cp -r $(EXTENSION_WEB_FILES) $(FIREFOX_BUILD_CONTENT)/web/ @rm $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html # Copy over the firefox extension snippet so we can inline pdf.js in it - cp web/viewer-snippet-firefox-extension.html $(FIREFOX_BUILD_CONTENT)/web/ + @cp web/viewer-snippet-firefox-extension.html $(FIREFOX_BUILD_CONTENT)/web/ # Modify the viewer so it does all the extension only stuff. - cd $(FIREFOX_BUILD_CONTENT)/web; \ + @cd $(FIREFOX_BUILD_CONTENT)/web; \ sed -i.bak '/PDFJSSCRIPT_INCLUDE_BUNDLE/ r ../build/pdf.js' viewer-snippet-firefox-extension.html; \ sed -i.bak '/PDFJSSCRIPT_REMOVE/d' viewer.html; \ sed -i.bak '/PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION/d' viewer.html; \ sed -i.bak '/PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION/ r viewer-snippet-firefox-extension.html' viewer.html; \ rm -f *.bak; # We don't need pdf.js anymore since its inlined - rm -Rf $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/; + @rm -Rf $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/; # Update the build version number @sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/install.rdf @sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/update.rdf diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 83c930d51..984915d23 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -35,7 +35,7 @@ ChromeActions.prototype = { download: function(data) { Services.wm.getMostRecentWindow('navigator:browser').saveURL(data); }, - setDatabase: function() { + setDatabase: function(data) { if (this.inPrivateBrowswing) return; application.prefs.setValue(EXT_PREFIX + '.database', data); @@ -51,8 +51,8 @@ ChromeActions.prototype = { function RequestListener(actions) { this.actions = actions; } -// Recieves an event and synchronously responds. -RequestListener.prototype.recieve = function(event) { +// Receive an event and synchronously responds. +RequestListener.prototype.receive = function(event) { var message = event.target; var action = message.getUserData('action'); var data = message.getUserData('data'); @@ -143,7 +143,7 @@ PdfStreamConverter.prototype = { gb.removeEventListener('DOMContentLoaded', domListener); var requestListener = new RequestListener(new ChromeActions()); win.addEventListener(PDFJS_EVENT_ID, function(event) { - requestListener.recieve(event); + requestListener.receive(event); }, false, true); } }; From 5fba376a336e57e5961a239f387d99b1cfb11c71 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Tue, 31 Jan 2012 20:19:44 -0600 Subject: [PATCH 048/285] Fixing interpolation (continuation of #1143) --- src/function.js | 66 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/src/function.js b/src/function.js index 3093b0a99..4f81158f0 100644 --- a/src/function.js +++ b/src/function.js @@ -156,33 +156,65 @@ var PDFFunction = (function PDFFunctionClosure() { args.length); var x = args; - var y = new Float64Array(n * m); + // Building the cube vertices: its part and sample index + // http://rjwagner49.com/Mathematics/Interpolation.pdf + var cubeVertices = 1 << m; + var cubeN = new Float64Array(cubeVertices); + var cubeVertex = new Uint32Array(cubeVertices); + for (var 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) { // x_i' = min(max(x_i, Domain_2i), Domain_2i+1) - var domain_2i = domain[2 * i]; - var domain_2i_1 = domain[2 * i + 1]; + var domain_2i = domain[i][0]; + var domain_2i_1 = domain[i][1]; var xi = Math.min(Math.max(x[i], domain_2i), domain_2i_1); - // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1, Encode_2i, Encode_2i+1) - var e = interpolate(xi, domain_2i, domain_2i_1, encode[2 * i], encode[2 * i + 1]); + // e_i = Interpolate(x_i', Domain_2i, Domain_2i+1, + // Encode_2i, Encode_2i+1) + var e = interpolate(xi, domain_2i, domain_2i_1, + encode[i][0], encode[i][1]); // e_i' = min(max(e_i, 0), Size_i - 1) - e = Math.min(Math.max(e, 0), size[i] - 1); + var size_i = size[i]; + e = Math.min(Math.max(e, 0), size_i - 1); - var in = i * n; - - for (var j = 0; j < n; ++j) { - // average the two nearest neighbors in the sampling table - var rj = (samples[Math.floor(e) * n + j] + samples[Math.ceil(e) * n + j]) / 2; - - // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1, Decode_2j, Decode_2j+1) - rj = interpolate(rj, 0, mask, 1, decode[2 * j], decode[2 * j + 1]); - - // y_j = min(max(r_j, range_2j, range_2j+1) - y[in + j] = Math.min(Math.max(rj, range[2 * j], range[2 * j + 1])); + // Adjusting the cube: N and vertex sample index + var e0 = e < size_i - 1 ? Math.floor(e) : e - 1; // e1 = e0 + 1; + var n0 = e0 + 1 - e; // (e1 - e) / (e1 - e0); + 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++) { + if (j & pos) { + cubeN[j] *= n1; + cubeVertex[j] += offset1; + } else { + cubeN[j] *= n0; + cubeVertex[j] += offset0; + } } + + k *= size_i; + pos <<= 1; + } + + var y = new Float64Array(n); + for (var j = 0; j < n; ++j) { + // Sum all cube vertices' samples portions + var rj = 0; + for (var i = 0; i < cubeVertices; i++) + rj += samples[cubeVertex[i] + j] * cubeN[i]; + + // r_j' = Interpolate(r_j, 0, 2^BitsPerSample - 1, + // Decode_2j, Decode_2j+1) + rj = interpolate(rj, 0, 1, decode[j][0], decode[j][1]); + + // y_j = min(max(r_j, range_2j), range_2j+1) + y[j] = Math.min(Math.max(rj, range[j][0]), range[j][1]); } return y; From 2f1252af364e2024e623f2fad4b6d742572e1628 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 1 Feb 2012 15:19:01 -0500 Subject: [PATCH 049/285] CSS fix --- web/viewer.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/viewer.css b/web/viewer.css index ddbba7f2f..94e48612f 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -242,7 +242,7 @@ canvas { top: 0; right: 0; bottom: 0; - background: url('images/loading-icon.gif') center no-repeat; */ + background: url('images/loading-icon.gif') center no-repeat; } .textLayer { From 74ec7a410389cd589400b30218021d8b0a7cb1a6 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 1 Feb 2012 16:04:12 -0500 Subject: [PATCH 050/285] LabCS infra working --- src/colorspace.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/colorspace.js b/src/colorspace.js index d67d928b1..0fb99bdcc 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -57,6 +57,8 @@ var ColorSpace = (function ColorSpaceClosure() { return new AlternateCS(numComps, ColorSpace.fromIR(alt), PDFFunction.fromIR(tintFnIR)); + case 'LabCS': + return new LabCS(); default: error('Unkown name ' + name); } @@ -146,6 +148,7 @@ var ColorSpace = (function ColorSpaceClosure() { var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); return ['AlternateCS', numComps, alt, tintFnIR]; case 'Lab': + return 'LabCS'; default: error('unimplemented color space object "' + mode + '"'); } @@ -409,3 +412,32 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { return DeviceCmykCS; })(); +var LabCS = (function LabCSClosure() { + function LabCS() { + this.name = 'Lab'; + this.numComps = 3; + this.defaultColor = [0, 0, 0]; + } + LabCS.prototype = { + getRgb: function labcs_getRgb(color) { + return [0, 0, 0]; + }, + getRgbBuffer: function labcs_getRgbBuffer(input, bits) { + if (bits == 8) + return input; + var scale = 255 / ((1 << bits) - 1); + var i, length = input.length; + var rgbBuf = new Uint8Array(length); + + for (i = 0; i < length; ++i) + rgbBuf[i] = 0; + + return rgbBuf; + }, + isDefaultDecode: function labcs_isDefaultDecode(decodeMap) { + // TODO: not sure about this yet + return true; + } + }; + return LabCS; +})(); From a39d4872837600bb20544f3099353a05fec624d8 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 1 Feb 2012 16:13:42 -0500 Subject: [PATCH 051/285] LabCS infra: args passed OK --- src/colorspace.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/colorspace.js b/src/colorspace.js index 0fb99bdcc..8acbe8cd3 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -58,7 +58,10 @@ var ColorSpace = (function ColorSpaceClosure() { return new AlternateCS(numComps, ColorSpace.fromIR(alt), PDFFunction.fromIR(tintFnIR)); case 'LabCS': - return new LabCS(); + var whitePoint = IR[1].WhitePoint; + var blackPoint = IR[1].BlackPoint; + var range = IR[1].Range; + return new LabCS(whitePoint, blackPoint, range); default: error('Unkown name ' + name); } @@ -148,7 +151,8 @@ var ColorSpace = (function ColorSpaceClosure() { var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3])); return ['AlternateCS', numComps, alt, tintFnIR]; case 'Lab': - return 'LabCS'; + var params = cs[1].map; + return ['LabCS', params]; default: error('unimplemented color space object "' + mode + '"'); } @@ -413,10 +417,13 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { })(); var LabCS = (function LabCSClosure() { - function LabCS() { + function LabCS(whitePoint, blackPoint, range) { this.name = 'Lab'; this.numComps = 3; this.defaultColor = [0, 0, 0]; + this.whitePoint = whitePoint; + this.blackPoint = blackPoint; + this.range = range; } LabCS.prototype = { getRgb: function labcs_getRgb(color) { From 0fc6c03956923e770808d6768a0ac76016545869 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 1 Feb 2012 17:48:44 -0500 Subject: [PATCH 052/285] Lab color space, closes #1133 --- src/colorspace.js | 89 +++++++++++++++++++++++++++++++++--- src/util.js | 18 ++++++++ test/pdfs/issue1133.pdf.link | 1 + test/test_manifest.json | 7 +++ 4 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 test/pdfs/issue1133.pdf.link diff --git a/src/colorspace.js b/src/colorspace.js index 8acbe8cd3..69da82536 100644 --- a/src/colorspace.js +++ b/src/colorspace.js @@ -416,28 +416,103 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() { return DeviceCmykCS; })(); +// +// LabCS: Based on "PDF Reference, Sixth Ed", p.250 +// var LabCS = (function LabCSClosure() { function LabCS(whitePoint, blackPoint, range) { this.name = 'Lab'; this.numComps = 3; this.defaultColor = [0, 0, 0]; - this.whitePoint = whitePoint; - this.blackPoint = blackPoint; - this.range = range; + + if (!whitePoint) + error('WhitePoint missing - required for color space Lab'); + blackPoint = blackPoint || [0, 0, 0]; + range = range || [-100, 100, -100, 100]; + + // Translate args to spec variables + this.XW = whitePoint[0]; + this.YW = whitePoint[1]; + this.ZW = whitePoint[2]; + this.amin = range[0]; + this.amax = range[1]; + this.bmin = range[2]; + this.bmax = range[3]; + + // These are here just for completeness - the spec doesn't offer any + // formulas that use BlackPoint in Lab + this.XB = blackPoint[0]; + this.YB = blackPoint[1]; + this.ZB = blackPoint[2]; + + // Validate vars as per spec + if (this.XW < 0 || this.ZW < 0 || this.YW !== 1) + error('Invalid WhitePoint components, no fallback available'); + + if (this.XB < 0 || this.YB < 0 || this.ZB < 0) { + warn('Invalid BlackPoint, falling back to default'); + this.XB = this.YB = this.ZB = 0; + } + + if (this.amin > this.amax || this.bmin > this.bmax) { + warn('Invalid Range, falling back to defaults'); + this.amin = -100; + this.amax = 100; + this.bmin = -100; + this.bmax = 100; + } + }; + + // Function g(x) from spec + function g(x) { + if (x >= 6 / 29) + return x * x * x; + else + return (108 / 841) * (x - 4 / 29); } + LabCS.prototype = { getRgb: function labcs_getRgb(color) { - return [0, 0, 0]; + // Ls,as,bs <---> L*,a*,b* in the spec + var Ls = color[0], as = color[1], bs = color[2]; + + // Adjust limits of 'as' and 'bs' + as = as > this.amax ? this.amax : as; + as = as < this.amin ? this.amin : as; + bs = bs > this.bmax ? this.bmax : bs; + bs = bs < this.bmin ? this.bmin : bs; + + // Computes intermediate variables X,Y,Z as per spec + var M = (Ls + 16) / 116; + var L = M + (as / 500); + var N = M - (bs / 200); + var X = this.XW * g(L); + var Y = this.YW * g(M); + var Z = this.ZW * g(N); + + // XYZ to RGB 3x3 matrix, from: + // http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC18 + var XYZtoRGB = [3.240479, -1.537150, -0.498535, + -0.969256, 1.875992, 0.041556, + 0.055648, -0.204043, 1.057311]; + + return Util.apply3dTransform(XYZtoRGB, [X, Y, Z]); }, getRgbBuffer: function labcs_getRgbBuffer(input, bits) { if (bits == 8) return input; var scale = 255 / ((1 << bits) - 1); - var i, length = input.length; + var i, length = input.length / 3; var rgbBuf = new Uint8Array(length); - for (i = 0; i < length; ++i) - rgbBuf[i] = 0; + var j = 0; + for (i = 0; i < length; ++i) { + // Convert L*, a*, s* into RGB + var rgb = this.getRgb([input[i], input[i + 1], input[i + 2]]); + rgbBuf[j++] = rgb[0]; + rgbBuf[j++] = rgb[1]; + rgbBuf[j++] = rgb[2]; + } return rgbBuf; }, diff --git a/src/util.js b/src/util.js index 759908e9e..caa523ec7 100644 --- a/src/util.js +++ b/src/util.js @@ -78,21 +78,39 @@ var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; var Util = (function UtilClosure() { function Util() {} + Util.makeCssRgb = function makergb(r, g, b) { var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0; return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }; + Util.makeCssCmyk = function makecmyk(c, m, y, k) { c = (new DeviceCmykCS()).getRgb([c, m, y, k]); var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0; return 'rgb(' + ri + ',' + gi + ',' + bi + ')'; }; + + // For 2d affine transforms Util.applyTransform = function apply(p, m) { var xt = p[0] * m[0] + p[1] * m[2] + m[4]; var yt = p[0] * m[1] + p[1] * m[3] + m[5]; return [xt, yt]; }; + // Apply a generic 3d matrix M on a 3-vector v: + // | a b c | | X | + // | d e f | x | Y | + // | g h i | | Z | + // M is assumed to be serialized as [a,b,c,d,e,f,g,h,i], + // with v as [X,Y,Z] + Util.apply3dTransform = function apply3d(m, v) { + return [ + m[0] * v[0] + m[1] * v[1] + m[2] * v[2], + m[3] * v[0] + m[4] * v[1] + m[5] * v[2], + m[6] * v[0] + m[7] * v[1] + m[8] * v[2] + ]; + } + return Util; })(); diff --git a/test/pdfs/issue1133.pdf.link b/test/pdfs/issue1133.pdf.link new file mode 100644 index 000000000..2480ba8f8 --- /dev/null +++ b/test/pdfs/issue1133.pdf.link @@ -0,0 +1 @@ +http://www.cscw2012.org/docs/program.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index c6fed0a35..dd5c54147 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -444,5 +444,12 @@ "rounds": 1, "link": false, "type": "eq" + }, + { "id": "issue1133", + "file": "pdfs/issue1133.pdf", + "md5": "d1b61580cb100e3df93d33703af1773a", + "rounds": 1, + "link": true, + "type": "eq" } ] From e73e10c4d9e1232fafa02a5c61a1a472bac519e4 Mon Sep 17 00:00:00 2001 From: Jeff Wagner Date: Thu, 2 Feb 2012 12:33:28 -0800 Subject: [PATCH 053/285] IE9 SVG images need viewport defined --- web/images/bookmark.svg | 3 ++- web/images/document-print.svg | 3 ++- web/images/download.svg | 3 ++- web/images/go-down.svg | 3 ++- web/images/go-up.svg | 3 ++- web/images/zoom-in.svg | 3 ++- web/images/zoom-out.svg | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/web/images/bookmark.svg b/web/images/bookmark.svg index 2c1fa130d..bee6efefd 100644 --- a/web/images/bookmark.svg +++ b/web/images/bookmark.svg @@ -20,7 +20,8 @@ height="48.000000px" width="48.000000px" inkscape:output_extension="org.inkscape.output.svg.inkscape" - version="1.1"> + version="1.1" + viewbox="0 0 48 48"> + inkscape:output_extension="org.inkscape.output.svg.inkscape" + viewbox="0 0 48 48"> + inkscape:output_extension="org.inkscape.output.svg.inkscape" + viewbox="0 0 48 48"> + inkscape:output_extension="org.inkscape.output.svg.inkscape" + viewbox="0 0 48 48"> + inkscape:output_extension="org.inkscape.output.svg.inkscape" + viewbox="0 0 48 48"> + inkscape:output_extension="org.inkscape.output.svg.inkscape" + viewbox="0 0 48 48"> + inkscape:output_extension="org.inkscape.output.svg.inkscape" + viewbox="0 0 48 48"> Date: Thu, 2 Feb 2012 18:29:08 -0600 Subject: [PATCH 054/285] Fix HTTP redirection for the extension --- extensions/firefox/components/PdfStreamConverter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 984915d23..bd3bfffcf 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -127,14 +127,14 @@ PdfStreamConverter.prototype = { 'resource://pdf.js/web/viewer.html', null, null); // Keep the URL the same so the browser sees it as the same. - channel.originalURI = aRequest.originalURI; + channel.originalURI = aRequest.URI; channel.asyncOpen(this.listener, aContext); // Setup a global listener waiting for the next DOM to be created and verfiy // that its the one we want by its URL. When the correct DOM is found create // an event listener on that window for the pdf.js events that require // chrome priviledges. - var url = aRequest.originalURI.spec; + var url = aRequest.URI.spec; var gb = Services.wm.getMostRecentWindow('navigator:browser'); var domListener = function domListener(event) { var doc = event.originalTarget; From 03cb546f29befdc959e551d742480550e24ad01a Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 2 Feb 2012 21:06:13 -0600 Subject: [PATCH 055/285] FTP protocol support (#1165) --- src/core.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core.js b/src/core.js index cb601398e..a34954ab1 100644 --- a/src/core.js +++ b/src/core.js @@ -33,7 +33,8 @@ function getPdf(arg, callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', params.url); xhr.mozResponseType = xhr.responseType = 'arraybuffer'; - xhr.expected = (params.url.indexOf('file:') === 0) ? 0 : 200; + xhr.expected = (params.url.indexOf('http:') === 0 || + params.url.indexOf('https:') === 0) ? 200 : 0; if ('progress' in params) xhr.onprogress = params.progress || undefined; From cba531e3cd7b3f4d37864c918e667c0ae1f90f07 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 2 Feb 2012 23:31:12 -0600 Subject: [PATCH 056/285] Fixing test execution / relative URL protocol detection --- src/core.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core.js b/src/core.js index a34954ab1..ae0f97231 100644 --- a/src/core.js +++ b/src/core.js @@ -33,8 +33,9 @@ function getPdf(arg, callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', params.url); xhr.mozResponseType = xhr.responseType = 'arraybuffer'; - xhr.expected = (params.url.indexOf('http:') === 0 || - params.url.indexOf('https:') === 0) ? 200 : 0; + var protocol = params.url.indexOf(':') < 0 ? window.location.protocol : + params.url.substring(0, params.url.indexOf(':') + 1); + xhr.expected = (protocol === 'http:' || protocol === 'https:') ? 200 : 0; if ('progress' in params) xhr.onprogress = params.progress || undefined; From 4994faa3c103a9672cdd48c7838d5073ddc86a48 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Fri, 3 Feb 2012 16:49:44 -0800 Subject: [PATCH 057/285] Use services where possible. --- extensions/firefox/components/PdfStreamConverter.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index bd3bfffcf..115db3e1f 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -17,8 +17,7 @@ Cu.import('resource://gre/modules/Services.jsm'); function log(aMsg) { let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg); - Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService) - .logStringMessage(msg); + Services.console.logStringMessage(msg); dump(msg + '\n'); } let application = Cc['@mozilla.org/fuel/application;1'] @@ -121,8 +120,7 @@ PdfStreamConverter.prototype = { aRequest.cancel(Cr.NS_BINDING_ABORTED); // Create a new channel that is viewer loaded as a resource. - var ioService = Cc['@mozilla.org/network/io-service;1'] - .getService(Ci.nsIIOService); + var ioService = Services.io; var channel = ioService.newChannel( 'resource://pdf.js/web/viewer.html', null, null); From 34e2aa5d21f3186ed8cb739d6c07da9e72d3e746 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 4 Feb 2012 12:45:18 -0600 Subject: [PATCH 058/285] Browser minimal font adjustment --- src/canvas.js | 25 ++++++++++++++++++++----- test/pdfs/issue1169.pdf.link | 1 + test/test_manifest.json | 7 +++++++ 3 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 test/pdfs/issue1169.pdf.link diff --git a/src/canvas.js b/src/canvas.js index f4815a655..131db979d 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -17,11 +17,14 @@ var TextRenderingMode = { ADD_TO_PATH: 7 }; +var MIN_FONT_SIZE = 8; + var CanvasExtraState = (function CanvasExtraStateClosure() { function CanvasExtraState(old) { // Are soft masks and alpha values shapes or opacities? this.alphaIsShape = false; this.fontSize = 0; + this.fontSizeScale = 1; this.textMatrix = IDENTITY_MATRIX; this.fontMatrix = IDENTITY_MATRIX; this.leading = 0; @@ -577,6 +580,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { this.current.font = fontObj; this.current.fontSize = size; + if (fontObj.coded) + return; // we don't need ctx.font for Type3 fonts + var name = fontObj.loadedName || 'sans-serif'; var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') : (fontObj.bold ? 'bold' : 'normal'); @@ -584,7 +590,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var italic = fontObj.italic ? 'italic' : 'normal'; var serif = fontObj.isSerifFont ? 'serif' : 'sans-serif'; var typeface = '"' + name + '", ' + serif; - var rule = italic + ' ' + bold + ' ' + size + 'px ' + typeface; + + var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE; + this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 : + size / MIN_FONT_SIZE; + + var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface; this.ctx.font = rule; }, setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) { @@ -647,6 +658,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var font = current.font; var glyphs = font.charsToGlyphs(str); var fontSize = current.fontSize; + var fontSizeScale = current.fontSizeScale; var charSpacing = current.charSpacing; var wordSpacing = current.wordSpacing; var textHScale = current.textHScale; @@ -715,6 +727,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { if (textSelection) text.geom = this.getTextGeometry(); + ctx.scale(fontSizeScale, fontSizeScale); + var x = 0; for (var i = 0; i < glyphsLength; ++i) { var glyph = glyphs[i]; @@ -728,20 +742,21 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var charWidth = glyph.width * fontSize * 0.001 + Util.sign(current.fontMatrix[0]) * charSpacing; + var scaledX = x / fontSizeScale; switch (textRenderingMode) { default: // other unsupported rendering modes case TextRenderingMode.FILL: case TextRenderingMode.FILL_ADD_TO_PATH: - ctx.fillText(char, x, 0); + ctx.fillText(char, scaledX, 0); break; case TextRenderingMode.STROKE: case TextRenderingMode.STROKE_ADD_TO_PATH: - ctx.strokeText(char, x, 0); + ctx.strokeText(char, scaledX, 0); break; case TextRenderingMode.FILL_STROKE: case TextRenderingMode.FILL_STROKE_ADD_TO_PATH: - ctx.fillText(char, x, 0); - ctx.strokeText(char, x, 0); + ctx.fillText(char, scaledX, 0); + ctx.strokeText(char, scaledX, 0); break; case TextRenderingMode.INVISIBLE: break; diff --git a/test/pdfs/issue1169.pdf.link b/test/pdfs/issue1169.pdf.link new file mode 100644 index 000000000..46559a078 --- /dev/null +++ b/test/pdfs/issue1169.pdf.link @@ -0,0 +1 @@ +http://www.cs.txstate.edu/~mb92/papers/gpgpu11.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 2b0541edb..f4d671a04 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -438,6 +438,13 @@ "link": true, "type": "eq" }, + { "id": "issue1169", + "file": "pdfs/issue1169.pdf", + "md5": "3df3ed21fd43ac7fdb21e2015c8a7809", + "rounds": 1, + "link": true, + "type": "eq" + }, { "id": "zerowidthline", "file": "pdfs/zerowidthline.pdf", "md5": "295d26e61a85635433f8e4b768953f60", From a617aff425d4106595abeacf5b85edc304676462 Mon Sep 17 00:00:00 2001 From: gigaherz Date: Sat, 4 Feb 2012 21:13:12 +0100 Subject: [PATCH 059/285] Quick & dirty sidebar pinning. --- web/images/pin_down.png | Bin 0 -> 210 bytes web/images/pin_up.png | Bin 0 -> 213 bytes web/viewer.css | 27 +++++++++++++++++++++--- web/viewer.html | 44 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 web/images/pin_down.png create mode 100644 web/images/pin_up.png diff --git a/web/images/pin_down.png b/web/images/pin_down.png new file mode 100644 index 0000000000000000000000000000000000000000..06c985b6a677ea334c7c3f4b4b78748ab5491987 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^+(693!3HF^taP6Tq*&4&eH|GXHuiJ>Nn{1`ISV`@ ziy0XB4uUY_j)~cCfr3&ct`Q}{`DrEPiAAXl0g0J;C3=3YAqr*2dZv1Y1_ob>76MiI zdAc};Xapz!`2XLYdE<<89ZJ6T-G@yGywq1 C|3ps! literal 0 HcmV?d00001 diff --git a/web/images/pin_up.png b/web/images/pin_up.png new file mode 100644 index 0000000000000000000000000000000000000000..aff30c15eeb573e9ba0a67cd7b8a5e840c069e4b GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^+(693!3HF^taP6Tq*&4&eH|GXHuiJ>Nn{1`ISV`@ ziy0XB4uLSEsD@VqP*AGGHKHUqKdq!Zu_%=xATcwqM9KLZGTZ zPZ!4!jo{=T|Nq-FZmdKI;Vst E0CuQC@Bjb+ literal 0 HcmV?d00001 diff --git a/web/viewer.css b/web/viewer.css index 681e5046a..e8071a5ce 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -76,11 +76,15 @@ span#info { } /* === Sidebar === */ + #sidebar { position: fixed; width: 350px; top: 62px; bottom: 18px; +} + +#sidebar.released { left: -290px; transition: left 0.25s ease-in-out 1s; -o-transition: left 0.25s ease-in-out 1s; @@ -89,7 +93,8 @@ span#info { z-index: 1; } -#sidebar:hover { +#sidebar.pinned, +#sidebar.released:hover { left: 0px; transition: left 0.25s ease-in-out 0s; -o-transition: left 0.25s ease-in-out 0s; @@ -97,6 +102,22 @@ span#info { -webkit-transition: left 0.25s ease-in-out 0s; } +#pinIcon { + position: absolute; + top: 4px; + right: 56px; + width: 11px; + height: 12px; +} + +#pinIcon.released { + background-image: url('images/pin_up.png'); +} + +#pinIcon.pinned { + background-image: url('images/pin_down.png'); +} + #sidebarBox { background-color: rgba(0, 0, 0, 0.7); width: 300px; @@ -116,7 +137,7 @@ span#info { position: absolute; overflow: hidden; overflow-y: auto; - top: 10px; + top: 20px; bottom: 10px; left: 10px; width: 280px; @@ -382,4 +403,4 @@ canvas { #loading { margin: 100px 0; text-align: center; -} +} \ No newline at end of file diff --git a/web/viewer.html b/web/viewer.html index 7c55ec735..c9a35816f 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -33,6 +33,45 @@ + + +
@@ -120,9 +159,10 @@
-