From 07254bb0a5459df1a9f7d451bbb4be73d00083f0 Mon Sep 17 00:00:00 2001 From: Kalervo Kujala Date: Thu, 15 Sep 2011 23:32:44 +0300 Subject: [PATCH 01/10] Fix small lint warnings. --- pdf.js | 4 ++-- test/driver.js | 25 +++++++++++++++---------- utils/cffStandardStrings.js | 2 +- utils/fonts_utils.js | 14 +++++++------- web/compatibility.js | 19 ++++++++++++------- web/viewer.js | 25 +++++++++++++------------ worker/canvas.js | 26 +++++++++++++------------- worker/client.js | 21 +++++++++++---------- worker/console.js | 1 + worker/font.js | 1 + worker/pdf.js | 2 +- 11 files changed, 77 insertions(+), 63 deletions(-) diff --git a/pdf.js b/pdf.js index 37afdc376..599e50af8 100644 --- a/pdf.js +++ b/pdf.js @@ -6061,8 +6061,8 @@ var PDFImage = (function() { this.decode = dict.get('Decode', 'D'); - var mask = xref.fetchIfRef(image.dict.get('Mask')); - var smask = xref.fetchIfRef(image.dict.get('SMask')); + var mask = xref.fetchIfRef(dict.get('Mask')); + var smask = xref.fetchIfRef(dict.get('SMask')); if (mask) { TODO('masked images'); diff --git a/test/driver.js b/test/driver.js index f3e45a53d..14f1280a6 100644 --- a/test/driver.js +++ b/test/driver.js @@ -8,6 +8,7 @@ 'use strict'; var appPath, browser, canvas, currentTaskIdx, manifest, stdout; +var inFlightRequests = 0; function queryParams() { var qs = window.location.search.substring(1); @@ -42,7 +43,8 @@ function load() { if (r.readyState == 4) { log('done\n'); manifest = JSON.parse(r.responseText); - currentTaskIdx = 0, nextTask(); + currentTaskIdx = 0; + nextTask(); } }; r.send(null); @@ -73,7 +75,8 @@ function nextTask() { failure = 'load PDF doc : ' + e.toString(); } - task.pageNum = 1, nextPage(task, failure); + task.pageNum = 1; + nextPage(task, failure); } }; r.send(null); @@ -89,7 +92,8 @@ function nextPage(task, loadError) { if (!task.pdfDoc) { sendTaskResult(canvas.toDataURL('image/png'), task, failure); log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n'); - ++currentTaskIdx, nextTask(); + ++currentTaskIdx; + nextTask(); return; } @@ -98,7 +102,8 @@ function nextPage(task, loadError) { log(' Round ' + (1 + task.round) + '\n'); task.pageNum = 1; } else { - ++currentTaskIdx, nextTask(); + ++currentTaskIdx; + nextTask(); return; } } @@ -123,7 +128,7 @@ function nextPage(task, loadError) { page.startRendering( ctx, function(e) { - snapshotCurrentPage(page, task, (!failure && e) ? + snapshotCurrentPage(task, (!failure && e) ? ('render : ' + e) : failure); } ); @@ -135,11 +140,11 @@ function nextPage(task, loadError) { if (failure) { // Skip right to snapshotting if there was a failure, since the // fonts might be in an inconsistent state. - snapshotCurrentPage(page, task, failure); + snapshotCurrentPage(task, failure); } } -function snapshotCurrentPage(page, task, failure) { +function snapshotCurrentPage(task, failure) { log('done, snapshotting... '); sendTaskResult(canvas.toDataURL('image/png'), task, failure); @@ -149,7 +154,8 @@ function snapshotCurrentPage(page, task, failure) { var backoff = (inFlightRequests > 0) ? inFlightRequests * 10 : 0; setTimeout( function() { - ++task.pageNum, nextPage(task); + ++task.pageNum; + nextPage(task); }, backoff ); @@ -182,7 +188,6 @@ function done() { } } -var inFlightRequests = 0; function sendTaskResult(snapshot, task, failure) { var result = { browser: browser, id: task.id, @@ -201,7 +206,7 @@ function sendTaskResult(snapshot, task, failure) { if (r.readyState == 4) { inFlightRequests--; } - } + }; document.getElementById('inFlightCount').innerHTML = inFlightRequests++; r.send(JSON.stringify(result)); } diff --git a/utils/cffStandardStrings.js b/utils/cffStandardStrings.js index 326f298b0..8a6570551 100644 --- a/utils/cffStandardStrings.js +++ b/utils/cffStandardStrings.js @@ -560,7 +560,7 @@ var CFFDictDataMap = { '18': { name: 'ExpansionFactor' }, - '9': { + '19': { name: 'initialRandomSeed' }, '20': { diff --git a/utils/fonts_utils.js b/utils/fonts_utils.js index 4826a09a1..a51469c53 100644 --- a/utils/fonts_utils.js +++ b/utils/fonts_utils.js @@ -69,7 +69,7 @@ function readCharstringEncoding(aString) { } else if (value <= 31) { token = CFFEncodingMap[value]; } else if (value < 247) { - token = parseInt(value) - 139; + token = parseInt(value, 10) - 139; } else if (value < 251) { token = ((value - 247) * 256) + aString[i++] + 108; } else if (value < 255) { @@ -113,7 +113,7 @@ function readFontDictData(aString, aMap) { while (!parsed) { var byte = aString[i++]; - var nibbles = [parseInt(byte / 16), parseInt(byte % 16)]; + var nibbles = [parseInt(byte / 16, 10), parseInt(byte % 16, 10)]; for (var j = 0; j < nibbles.length; j++) { var nibble = nibbles[j]; switch (nibble) { @@ -144,7 +144,7 @@ function readFontDictData(aString, aMap) { } else if (value <= 31) { token = aMap[value]; } else if (value <= 246) { - token = parseInt(value) - 139; + token = parseInt(value, 10) - 139; } else if (value <= 250) { token = ((value - 247) * 256) + aString[i++] + 108; } else if (value <= 254) { @@ -193,7 +193,7 @@ function readFontIndexData(aStream, aIsByte) { } error(offsize + ' is not a valid offset size'); return null; - }; + } var offsets = []; for (var i = 0; i < count + 1; i++) @@ -236,7 +236,7 @@ var Type2Parser = function(aFilePath) { function dump(aStr) { if (debug) log(aStr); - }; + } function parseAsToken(aString, aMap) { var decoded = readFontDictData(aString, aMap); @@ -277,7 +277,7 @@ var Type2Parser = function(aFilePath) { } } } - }; + } this.parse = function(aStream) { font.set('major', aStream.getByte()); @@ -353,7 +353,7 @@ var Type2Parser = function(aFilePath) { aStream.pos = charsetEntry; var charset = readCharset(aStream, charStrings); } - } + }; }; /* diff --git a/web/compatibility.js b/web/compatibility.js index 63ebecb63..2301678d5 100755 --- a/web/compatibility.js +++ b/web/compatibility.js @@ -1,6 +1,8 @@ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +'use strict'; + // Checking if the typed arrays are supported (function() { if (typeof Uint8Array !== 'undefined') @@ -10,8 +12,9 @@ return this.slice(start, end); } - function set_(array, offset) { - if (arguments.length < 2) offset = 0; + function set_function(array, offset) { + if (arguments.length < 2) + offset = 0; for (var i = 0, n = array.length; i < n; ++i, ++offset) this[offset] = array[i] & 0xFF; } @@ -19,15 +22,17 @@ function TypedArray(arg1) { var result; if (typeof arg1 === 'number') { - result = new Array(arg1); - for (var i = 0; i < arg1; ++i) - result[i] = 0; + result = []; + for (var i = 0; i < arg1; ++i) + result[i] = 0; } else - result = arg1.slice(0); + result = arg1.slice(0); + result.subarray = subarray; result.buffer = result; result.byteLength = result.length; - result.set = set_; + result.set = set_function; + if (typeof arg1 === 'object' && arg1.buffer) result.buffer = arg1.buffer; diff --git a/web/viewer.js b/web/viewer.js index d7c9d6b66..520cf4efa 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -98,7 +98,7 @@ var PDFView = { }, get page() { - return parseInt(document.location.hash.substring(1)) || 1; + return parseInt(document.location.hash.substring(1), 10) || 1; }, open: function(url, scale) { @@ -170,7 +170,7 @@ var PDFView = { } this.setScale(scale || kDefaultScale, true); - this.page = parseInt(document.location.hash.substring(1)) || 1; + this.page = parseInt(document.location.hash.substring(1), 10) || 1; this.pagesRefMap = pagesRefMap; this.destinations = pdf.catalog.destinations; if (pdf.catalog.documentOutline) { @@ -209,7 +209,7 @@ var PDFView = { var currentHeight = kBottomMargin; var windowTop = window.pageYOffset; - for (var i = 1; i <= pages.length; i++) { + for (var i = 1; i <= pages.length; ++i) { var page = pages[i - 1]; var pageHeight = page.height * page.scale + kBottomMargin; if (currentHeight + pageHeight > windowTop) @@ -219,10 +219,11 @@ var PDFView = { } var windowBottom = window.pageYOffset + window.innerHeight; - for (; i <= pages.length && currentHeight < windowBottom; i++) { - var page = pages[i - 1]; - visiblePages.push({ id: page.id, y: currentHeight, view: page }); - currentHeight += page.height * page.scale + kBottomMargin; + for (; i <= pages.length && currentHeight < windowBottom; ++i) { + var singlePage = pages[i - 1]; + visiblePages.push({ id: singlePage.id, y: currentHeight, + view: singlePage }); + currentHeight += singlePage.height * singlePage.scale + kBottomMargin; } return visiblePages; @@ -256,13 +257,13 @@ var PageView = function(container, content, id, width, height, div.removeAttribute('data-loaded'); }; - function setupLinks(canvas, content, scale) { + function setupLinks(content, scale) { function bindLink(link, dest) { link.onclick = function() { if (dest) PDFView.navigateTo(dest); return false; - } + }; } var links = content.getLinks(); for (var i = 0; i < links.length; i++) { @@ -283,8 +284,6 @@ var PageView = function(container, content, id, width, height, var width = 0, height = 0, widthScale, heightScale; var scale = 0; switch (dest[1].name) { - default: - return; case 'XYZ': x = dest[2]; y = dest[3]; @@ -315,6 +314,8 @@ var PageView = function(container, content, id, width, height, height / kCssUnits; scale = Math.min(widthScale, heightScale); break; + default: + return; } var boundingRect = [ @@ -369,7 +370,7 @@ var PageView = function(container, content, id, width, height, stats.begin = Date.now(); this.content.startRendering(ctx, this.updateStats); - setupLinks(canvas, this.content, this.scale); + setupLinks(this.content, this.scale); div.setAttribute('data-loaded', true); return true; diff --git a/worker/canvas.js b/worker/canvas.js index 5a9237d9a..d8b0dd338 100644 --- a/worker/canvas.js +++ b/worker/canvas.js @@ -39,7 +39,7 @@ function GradientProxy(cmdQueue, x0, y0, x1, y1) { cmdQueue.push(['$createLinearGradient', [x0, y0, x1, y1]]); this.addColorStop = function(i, rgba) { cmdQueue.push(['$addColorStop', [i, rgba]]); - } + }; } // Really simple PatternProxy. @@ -72,7 +72,7 @@ function CanvasProxy(width, height) { throw 'CanvasProxy can only provide a 2d context.'; } return ctx; - } + }; // Expose only the minimum of the canvas object - there is no dom to do // more here. @@ -127,7 +127,7 @@ function CanvasProxy(width, height) { return function() { // console.log("funcCall", name) cmdQueue.push([name, Array.prototype.slice.call(arguments)]); - } + }; } var name; for (var i = 0; i < ctxFunc.length; i++) { @@ -139,11 +139,11 @@ function CanvasProxy(width, height) { ctx.createPattern = function(object, kind) { return new PatternProxy(cmdQueue, object, kind); - } + }; ctx.createLinearGradient = function(x0, y0, x1, y1) { return new GradientProxy(cmdQueue, x0, y0, x1, y1); - } + }; ctx.getImageData = function(x, y, w, h) { return { @@ -151,11 +151,11 @@ function CanvasProxy(width, height) { height: h, data: Uint8ClampedArray(w * h * 4) }; - } + }; ctx.putImageData = function(data, x, y, width, height) { cmdQueue.push(['$putImageData', [data, x, y, width, height]]); - } + }; ctx.drawImage = function(image, x, y, width, height, sx, sy, swidth, sheight) { @@ -168,7 +168,7 @@ function CanvasProxy(width, height) { } else { throw 'unkown type to drawImage'; } - } + }; // Setup property access to `ctx`. var ctxProp = { @@ -195,14 +195,14 @@ function CanvasProxy(width, height) { function buildGetter(name) { return function() { return ctx['$' + name]; - } + }; } function buildSetter(name) { return function(value) { cmdQueue.push(['$', name, value]); - return ctx['$' + name] = value; - } + return (ctx['$' + name] = value); + }; } // Setting the value to `stroke|fillStyle` needs special handling, as it @@ -215,9 +215,9 @@ function CanvasProxy(width, height) { cmdQueue.push(['$' + name + 'Pattern', [value.id]]); } else { cmdQueue.push(['$', name, value]); - return ctx['$' + name] = value; + return (ctx['$' + name] = value); } - } + }; } for (var name in ctxProp) { diff --git a/worker/client.js b/worker/client.js index a20a4179f..36a0ae03a 100644 --- a/worker/client.js +++ b/worker/client.js @@ -58,7 +58,7 @@ FontWorker.prototype = { 'fonts': function(data) { // console.log("got processed fonts from worker", Object.keys(data)); - for (name in data) { + for (var name in data) { // Update the encoding property. var font = Fonts.lookup(name); font.properties = { @@ -176,7 +176,7 @@ function WorkerPDFDoc(canvas) { // Copy over the imageData. var len = imageRealData.length; while (len--) - imgRealData[len] = imageRealData[len]; + imgRealData[len] = imageRealData[len]; this.putImageData(imgData, x, y); }, @@ -273,7 +273,7 @@ function WorkerPDFDoc(canvas) { }, 'pdf_num_pages': function(data) { - this.numPages = parseInt(data); + this.numPages = parseInt(data, 10); if (this.loadCallback) { this.loadCallback(); } @@ -302,8 +302,8 @@ function WorkerPDFDoc(canvas) { 'setup_page': function(data) { var size = data.split(','); var canvas = this.canvas, ctx = this.ctx; - canvas.width = parseInt(size[0]); - canvas.height = parseInt(size[1]); + canvas.width = parseInt(size[0], 10); + canvas.height = parseInt(size[1], 10); }, 'fonts': function(data) { @@ -397,7 +397,7 @@ WorkerPDFDoc.prototype.open = function(url, callback) { }; WorkerPDFDoc.prototype.showPage = function(numPage) { - this.numPage = parseInt(numPage); + this.numPage = parseInt(numPage, 10); console.log('=== start rendering page ' + numPage + ' ==='); console.time('>>> total page display time:'); this.worker.postMessage(numPage); @@ -407,11 +407,12 @@ WorkerPDFDoc.prototype.showPage = function(numPage) { }; WorkerPDFDoc.prototype.nextPage = function() { - if (this.numPage == this.numPages) return; - this.showPage(++this.numPage); + if (this.numPage != this.numPages) + this.showPage(++this.numPage); }; WorkerPDFDoc.prototype.prevPage = function() { - if (this.numPage == 1) return; - this.showPage(--this.numPage); + if (this.numPage != 1) + this.showPage(--this.numPage); }; + diff --git a/worker/console.js b/worker/console.js index fc49583a6..43ec1af88 100644 --- a/worker/console.js +++ b/worker/console.js @@ -25,3 +25,4 @@ var console = { this.log('Timer:', name, Date.now() - time); } }; + diff --git a/worker/font.js b/worker/font.js index 549b73101..44a4782f0 100644 --- a/worker/font.js +++ b/worker/font.js @@ -64,3 +64,4 @@ this.onmessage = function(event) { throw 'Unkown action from worker: ' + data.action; } }; + diff --git a/worker/pdf.js b/worker/pdf.js index 8cb6342db..7a72ac824 100644 --- a/worker/pdf.js +++ b/worker/pdf.js @@ -58,7 +58,7 @@ onmessage = function(event) { console.time('compile'); // Let's try to render the first page... - var page = pdfDocument.getPage(parseInt(data)); + var page = pdfDocument.getPage(parseInt(data, 10)); var pdfToCssUnitsCoef = 96.0 / 72.0; var pageWidth = (page.mediaBox[2] - page.mediaBox[0]) * pdfToCssUnitsCoef; From 6e290349120160b39cfaf565fc061d00958cdd13 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 14 Sep 2011 17:34:29 -0700 Subject: [PATCH 02/10] Hello World example Perhaps the simplest use of pdf.js. See README.md in the new folder --- helloworld/README.md | 13 ++++++++ helloworld/hello.js | 49 ++++++++++++++++++++++++++++ helloworld/helloworld.pdf | 68 +++++++++++++++++++++++++++++++++++++++ helloworld/index.html | 18 +++++++++++ 4 files changed, 148 insertions(+) create mode 100644 helloworld/README.md create mode 100644 helloworld/hello.js create mode 100644 helloworld/helloworld.pdf create mode 100644 helloworld/index.html diff --git a/helloworld/README.md b/helloworld/README.md new file mode 100644 index 000000000..70d5e760b --- /dev/null +++ b/helloworld/README.md @@ -0,0 +1,13 @@ +## "Hello World" overview + +This example is a minimalistic application of the pdf.js project. The file `helloworld.pdf` is from the GNUpdf project (see [Introduction to PDF at GNUpdf](http://gnupdf.org/Introduction_to_PDF), and contains a simple and human-readable PDF. + + +## Getting started + +Point your browser to `index.html`. Voila. Take a peek at `hello.js` to see how to make basic calls to `pdf.js`. + + +## Additional resources + ++ [GNUpdf - Introduction to PDF](http://gnupdf.org/Introduction_to_PDF) diff --git a/helloworld/hello.js b/helloworld/hello.js new file mode 100644 index 000000000..0bf46311a --- /dev/null +++ b/helloworld/hello.js @@ -0,0 +1,49 @@ +// +// See README for overview +// + + +// +// Ajax GET request for binary files +// (like jQuery's $.get(), but supports the binary type ArrayBuffer) +// +var binaryGet = function(url, callback){ + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.mozResponseType = xhr.responseType = 'arraybuffer'; + xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200; + xhr.onreadystatechange = function() { + if (xhr.readyState === 4 && xhr.status === xhr.expected) { + var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || + xhr.responseArrayBuffer || xhr.response); + + callback(data); + } + }; + xhr.send(null); +} + +// +// This is where the fun happens +// +binaryGet('helloworld.pdf', function(data){ + // + // Instantiate PDFDoc with PDF data + // + var pdf = new PDFDoc(new Stream(data)); + var page = pdf.getPage(1); + var scale = 1.5; + + // + // Prepare canvas using PDF page dimensions + // + var canvas = document.getElementById('the-canvas'); + var context = canvas.getContext('2d'); + canvas.height = page.height * scale; + canvas.width = page.width * scale; + + // + // Render PDF page into canvas context + // + page.startRendering(context); +}); diff --git a/helloworld/helloworld.pdf b/helloworld/helloworld.pdf new file mode 100644 index 000000000..d98b4e1db --- /dev/null +++ b/helloworld/helloworld.pdf @@ -0,0 +1,68 @@ +%PDF-1.7 + +1 0 obj % entry point +<< + /Type /Catalog + /Pages 2 0 R +>> +endobj + +2 0 obj +<< + /Type /Pages + /MediaBox [ 0 0 200 200 ] + /Count 1 + /Kids [ 3 0 R ] +>> +endobj + +3 0 obj +<< + /Type /Page + /Parent 2 0 R + /Resources << + /Font << + /F1 4 0 R + >> + >> + /Contents 5 0 R +>> +endobj + +4 0 obj +<< + /Type /Font + /Subtype /Type1 + /BaseFont /Times-Roman +>> +endobj + +5 0 obj % page content +<< + /Length 44 +>> +stream +BT +70 50 TD +/F1 12 Tf +(Hello, world!) Tj +ET +endstream +endobj + +xref +0 6 +0000000000 65535 f +0000000010 00000 n +0000000079 00000 n +0000000173 00000 n +0000000301 00000 n +0000000380 00000 n +trailer +<< + /Size 6 + /Root 1 0 R +>> +startxref +492 +%%EOF \ No newline at end of file diff --git a/helloworld/index.html b/helloworld/index.html new file mode 100644 index 000000000..68a465f52 --- /dev/null +++ b/helloworld/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + From 3476364886a922180006b1b2ac5abf7d4afa3d00 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 09:28:58 -0700 Subject: [PATCH 03/10] Moved helloworld/ to examples/helloworld/ --- {helloworld => examples/helloworld}/README.md | 0 {helloworld => examples/helloworld}/hello.js | 0 .../helloworld}/helloworld.pdf | 0 examples/helloworld/index.html | 18 ++++++++++++++++++ helloworld/index.html | 18 ------------------ 5 files changed, 18 insertions(+), 18 deletions(-) rename {helloworld => examples/helloworld}/README.md (100%) rename {helloworld => examples/helloworld}/hello.js (100%) rename {helloworld => examples/helloworld}/helloworld.pdf (100%) create mode 100644 examples/helloworld/index.html delete mode 100644 helloworld/index.html diff --git a/helloworld/README.md b/examples/helloworld/README.md similarity index 100% rename from helloworld/README.md rename to examples/helloworld/README.md diff --git a/helloworld/hello.js b/examples/helloworld/hello.js similarity index 100% rename from helloworld/hello.js rename to examples/helloworld/hello.js diff --git a/helloworld/helloworld.pdf b/examples/helloworld/helloworld.pdf similarity index 100% rename from helloworld/helloworld.pdf rename to examples/helloworld/helloworld.pdf diff --git a/examples/helloworld/index.html b/examples/helloworld/index.html new file mode 100644 index 000000000..c353b6a89 --- /dev/null +++ b/examples/helloworld/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/helloworld/index.html b/helloworld/index.html deleted file mode 100644 index 68a465f52..000000000 --- a/helloworld/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - From bc91b98fc8f4d17a5d7c2224d2e93b91fee50385 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 11:14:55 -0700 Subject: [PATCH 04/10] Addressing comments by Vivien --- examples/helloworld/README.md | 7 +++++-- examples/helloworld/hello.js | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/helloworld/README.md b/examples/helloworld/README.md index 70d5e760b..8395733f3 100644 --- a/examples/helloworld/README.md +++ b/examples/helloworld/README.md @@ -1,11 +1,14 @@ ## "Hello World" overview -This example is a minimalistic application of the pdf.js project. The file `helloworld.pdf` is from the GNUpdf project (see [Introduction to PDF at GNUpdf](http://gnupdf.org/Introduction_to_PDF), and contains a simple and human-readable PDF. +This example is a minimalistic application of the pdf.js project. The file +`helloworld.pdf` is from the GNUpdf project (see [Introduction to PDF at GNUpdf](http://gnupdf.org/Introduction_to_PDF), and contains a simple and +human-readable PDF. ## Getting started -Point your browser to `index.html`. Voila. Take a peek at `hello.js` to see how to make basic calls to `pdf.js`. +Point your browser to `index.html`. Voila. Take a peek at `hello.js` to see +how to make basic calls to `pdf.js`. ## Additional resources diff --git a/examples/helloworld/hello.js b/examples/helloworld/hello.js index 0bf46311a..21799c33a 100644 --- a/examples/helloworld/hello.js +++ b/examples/helloworld/hello.js @@ -4,10 +4,10 @@ // -// Ajax GET request for binary files +// Ajax GET request, for binary files // (like jQuery's $.get(), but supports the binary type ArrayBuffer) // -var binaryGet = function(url, callback){ +var ajaxGet = function(url, callback){ var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.mozResponseType = xhr.responseType = 'arraybuffer'; @@ -26,7 +26,7 @@ var binaryGet = function(url, callback){ // // This is where the fun happens // -binaryGet('helloworld.pdf', function(data){ +ajaxGet('helloworld.pdf', function(data){ // // Instantiate PDFDoc with PDF data // From f3745f7c139d4d97deccbd9c5262a1b686d4c947 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 16:28:38 -0700 Subject: [PATCH 05/10] New README, .gitignores; Makefile Makefile: commented out unnecessary closure download and shell testing (at least until someone decides to work on this). --- Makefile | 72 ++++++++--------- README.md | 85 ++++++++++++++++++--- test/pdfs/.gitignore | 12 ++- test/resources/browser_manifests/.gitignore | 2 + 4 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 test/resources/browser_manifests/.gitignore diff --git a/Makefile b/Makefile index 0ca269642..fb4ffe9cb 100644 --- a/Makefile +++ b/Makefile @@ -55,30 +55,30 @@ browser-test: --browserManifestFile=$(PDF_BROWSERS) \ --manifestFile=$(PDF_TESTS) -# make shell-test -# -# This target runs all of the tests that can be run in a JS shell. -# The shell used is taken from the JS_SHELL environment variable. If -# that variable is not defined, the script will attempt to use the copy -# of Rhino that comes with the Closure compiler used for producing the -# website. -SHELL_TARGET = $(NULL) -ifeq ($(JS_SHELL),) -JS_SHELL := "java -cp $(BUILD_DIR)/compiler.jar" -JS_SHELL += "com.google.javascript.jscomp.mozilla.rhino.tools.shell.Main" -SHELL_TARGET = compiler -endif - -shell-test: shell-msg $(SHELL_TARGET) font-test -shell-msg: -ifeq ($(SHELL_TARGET), compiler) - @echo "No JS_SHELL env variable present." - @echo "The default is to find a copy of Rhino and try that." -endif - @echo "JS shell command is: $(JS_SHELL)" - -font-test: - @echo "font test stub." +# # make shell-test +# # +# # This target runs all of the tests that can be run in a JS shell. +# # The shell used is taken from the JS_SHELL environment variable. If +# # that variable is not defined, the script will attempt to use the copy +# # of Rhino that comes with the Closure compiler used for producing the +# # website. +# SHELL_TARGET = $(NULL) +# ifeq ($(JS_SHELL),) +# JS_SHELL := "java -cp $(BUILD_DIR)/compiler.jar" +# JS_SHELL += "com.google.javascript.jscomp.mozilla.rhino.tools.shell.Main" +# SHELL_TARGET = compiler +# endif +# +# shell-test: shell-msg $(SHELL_TARGET) font-test +# shell-msg: +# ifeq ($(SHELL_TARGET), compiler) +# @echo "No JS_SHELL env variable present." +# @echo "The default is to find a copy of Rhino and try that." +# endif +# @echo "JS shell command is: $(JS_SHELL)" +# +# font-test: +# @echo "font test stub." # make lint # @@ -133,18 +133,18 @@ $(GH_PAGES)/web/%: web/% $(GH_PAGES)/web/images/%: web/images/% @cp $< $@ -# make compiler -# -# This target downloads the Closure compiler, and places it in the -# build directory. This target is also useful when the user doesn't -# have a JS shell available--we can have them use the Rhino shell that -# comes with Closure. -COMPILER_URL = http://closure-compiler.googlecode.com/files/compiler-latest.zip - -compiler: $(BUILD_DIR)/compiler.zip -$(BUILD_DIR)/compiler.zip: | $(BUILD_DIR) - curl $(COMPILER_URL) > $(BUILD_DIR)/compiler.zip; - cd $(BUILD_DIR); unzip compiler.zip compiler.jar; +# # make compiler +# # +# # This target downloads the Closure compiler, and places it in the +# # build directory. This target is also useful when the user doesn't +# # have a JS shell available--we can have them use the Rhino shell that +# # comes with Closure. +# COMPILER_URL = http://closure-compiler.googlecode.com/files/compiler-latest.zip +# +# compiler: $(BUILD_DIR)/compiler.zip +# $(BUILD_DIR)/compiler.zip: | $(BUILD_DIR) +# curl $(COMPILER_URL) > $(BUILD_DIR)/compiler.zip; +# cd $(BUILD_DIR); unzip compiler.zip compiler.jar; # make firefox-extension # diff --git a/README.md b/README.md index 0d439b139..fe6d296a9 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,80 @@ # pdf.js -pdf.js is a technology demonstrator prototype to explore whether the HTML5 -platform is complete enough to faithfully and efficiently render the ISO -32000-1:2008 Portable Document Format (PDF) without native code assistance. -pdf.js is not currently part of the Mozilla project, and there is no plan -yet to integrate it into Firefox. We will explore that possibility once -pdf.js is production ready. Until then we aim to publish a Firefox -PDF reader extension powered by pdf.js. + +## Overview + +pdf.js is an HTML5 technology experiment that explores building a faithful +and efficient Portable Document Format (PDF) renderer without native code +assistance. + +pdf.js is community-driven and supported by Mozilla Labs. Our goal is to +create a general-purpose, web standards-based platform for parsing and +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 + +For an online demo, visit: + + http://andreasgal.github.com/pdf.js/web/viewer.html + +This demo provides an interactive interface for displaying and browsing PDFs +using the pdf.js API. + +For a "hello world" example, take a look at: + + examples/helloworld/ + +This example illustrates the bare minimum ingredients for integrating pdf.js +in a custom project. + + + +## Running the Tests + +pdf.js comes with browser-level regression tests that allow one to probe +whether it's able to successfully parse PDFs, as well as compare its output +against reference images, pixel-by-pixel. + +To run the tests, first configure the browser manifest file at: + + test/resources/browser_manifests/browser_manifest.json + +Sample manifests for different platforms are provided in that directory. + +To run all the bundled tests, type: + + $ make test + +and cross your fingers. Different types of tests are available, see the test +manifest file at: + + test/test_manifest.json + +The test type `eq` tests whether the output images are identical to reference +images. The test type `load` simply tests whether the file loads without +raising any errors. + + +## Contributing + +pdf.js is a community-driver project, so contributors are always welcome. +Simply fork our repo and contribute away. A great place to start is our +open issues. + +For better consistency and long-term stability, please do look around the +code and try to follow our conventions. + + +## Additional resources Our demo site is here: - http://andreasgal.github.com/pdf.js/ + http://andreasgal.github.com/pdf.js/web/viewer.html You can read more about pdf.js here: @@ -19,14 +82,14 @@ You can read more about pdf.js here: http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/ -follow us on twitter: @pdfjs +Follow us on twitter: @pdfjs http://twitter.com/#!/pdfjs -join our mailing list: +Join our mailing list: dev-pdf-js@lists.mozilla.org -and talk to us on IRC: +Talk to us on IRC: #pdfjs on irc.mozilla.org diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 95de9fb8e..77c89ece6 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -1,3 +1,13 @@ -pdf.pdf +DiwanProfile.pdf +artofwar.pdf +cable.pdf +ecma262.pdf +hmm.pdf +i9.pdf intelisa.pdf openweb_tm-PRINT.pdf +pdf.pdf +pdkids.pdf +shavian.pdf +jai.pdf + diff --git a/test/resources/browser_manifests/.gitignore b/test/resources/browser_manifests/.gitignore new file mode 100644 index 000000000..ca57ac505 --- /dev/null +++ b/test/resources/browser_manifests/.gitignore @@ -0,0 +1,2 @@ +browser_manifest.json + From e2b1cf1baf5c17a957b4591787036889b48ec824 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 16:39:18 -0700 Subject: [PATCH 06/10] Minor --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index fe6d296a9..252934cb7 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ successful. ## Getting started +**Online demo** + For an online demo, visit: http://andreasgal.github.com/pdf.js/web/viewer.html @@ -25,6 +27,8 @@ For an online demo, visit: This demo provides an interactive interface for displaying and browsing PDFs using the pdf.js API. +**Hello world** + For a "hello world" example, take a look at: examples/helloworld/ From cac74d7cb3d1c04bd43bd4e60a24e85e46357db9 Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Thu, 15 Sep 2011 23:23:22 +0100 Subject: [PATCH 07/10] Fix OS/2 table to match font yMax and yMin from head table --- fonts.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fonts.js b/fonts.js index 7d51e2c4b..064702de2 100644 --- a/fonts.js +++ b/fonts.js @@ -833,9 +833,11 @@ var Font = (function Font() { var data = file.getBytes(length); file.pos = previousPosition; - if (tag == 'head') + if (tag == 'head') { // clearing checksum adjustment data[8] = data[9] = data[10] = data[11] = 0; + data[17] |= 0x20; //Set font optimized for cleartype flag + } return { tag: tag, @@ -1008,7 +1010,7 @@ var Font = (function Font() { var header = readOpenTypeHeader(font); var numTables = header.numTables; - var cmap, maxp, hhea, hmtx, vhea, vmtx; + var cmap, maxp, hhea, hmtx, vhea, vmtx, head; var tables = []; for (var i = 0; i < numTables; i++) { var table = readTableEntry(font); @@ -1022,6 +1024,8 @@ var Font = (function Font() { hhea = table; else if (table.tag == 'hmtx') hmtx = table; + else if (table.tag == 'head') + head = table; requiredTables.splice(index, 1); } else { @@ -1048,6 +1052,13 @@ var Font = (function Font() { createOpenTypeHeader(header.version, ttf, numTables); if (requiredTables.indexOf('OS/2') != -1) { + if (typeof(head) != 'undefined') { + var ymax = int16([head.data[42],head.data[43]]); + var ymin = int16([head.data[38],head.data[39]]) - 0x10000; //always negative + properties.ascent = ymax; + properties.descent = ymin; + } + tables.push({ tag: 'OS/2', data: stringToArray(createOS2Table(properties)) From 870de2f7f3ed286603bbf1f19213d996839d2280 Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Fri, 16 Sep 2011 13:50:11 +0100 Subject: [PATCH 08/10] Add opentype ascender, descender and units-per-em to font properties Prefer font ascender, descender when creating OS/2 table scale PDF ascender and descender in OS/2 table by font units per em if it exists Fix the truetype font header to prevent Windows rejecting a Mac truetype font --- fonts.js | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/fonts.js b/fonts.js index 064702de2..fd7a23bd3 100644 --- a/fonts.js +++ b/fonts.js @@ -12,6 +12,9 @@ var kMaxWaitForFontFace = 1000; // Unicode Private Use Area var kCmapGlyphOffset = 0xE000; +// PDF Glyph Space Units are one Thousandth of a TextSpace Unit except for Type 3 fonts +var kPDFGlyphSpaceUnits = 1000; + // Until hinting is fully supported this constant can be used var kHintingEnabled = false; @@ -534,6 +537,10 @@ var Font = (function Font() { }; function createOpenTypeHeader(sfnt, file, numTables) { + // Windows hates the Mac TrueType sfnt version number + if (sfnt == 'true') + sfnt = string32(0x00010000); + // sfnt version (4 bytes) var header = sfnt; @@ -694,6 +701,24 @@ var Font = (function Font() { } } + var openTypeUnitsPerEm = (typeof (properties.openTypeUnitsPerEm) == 'undefined') ? kPDFGlyphSpaceUnits : properties.openTypeUnitsPerEm; + var typoAscent = properties.ascent; + var typoDescent = properties.descent; + var winAscent = typoAscent; + var winDescent = -typoDescent; + + // if the font already has ascent and descent information then use these values + if (typeof (properties.openTypeAscent) != 'undefined') { + typoAscent = properties.openTypeAscent; + typoDescent = properties.openTypeDescent; + winAscent = properties.openTypeYMax; + winDescent = -properties.openTypeYMin; + } else if (openTypeUnitsPerEm != kPDFGlyphSpaceUnits) { + // if the font units differ to the PDF glyph space units then scale up the values + typoAscent = Math.round(typoAscent * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); + typoDescent = Math.round(typoDescent * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); + } + return '\x00\x03' + // version '\x02\x24' + // xAvgCharWidth '\x01\xF4' + // usWeightClass @@ -722,11 +747,11 @@ var Font = (function Font() { string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex string16(lastCharIndex || properties.lastChar) + // usLastCharIndex - string16(properties.ascent) + // sTypoAscender - string16(properties.descent) + // sTypoDescender + string16(typoAscent) + // sTypoAscender + string16(typoDescent) + // sTypoDescender '\x00\x64' + // sTypoLineGap (7%-10% of the unitsPerEM value) - string16(properties.ascent) + // usWinAscent - string16(-properties.descent) + // usWinDescent + string16(winAscent) + // usWinAscent + string16(winDescent) + // usWinDescent '\x00\x00\x00\x00' + // ulCodePageRange1 (Bits 0-31) '\x00\x00\x00\x00' + // ulCodePageRange2 (Bits 32-63) string16(properties.xHeight) + // sxHeight @@ -1047,18 +1072,19 @@ var Font = (function Font() { virtualOffset: numTables * (4 * 4) }; + //extract some more font properties from the OpenType head and hhea tables + properties.openTypeUnitsPerEm = int16([head.data[18], head.data[19]]); + properties.openTypeYMax = int16([head.data[42], head.data[43]]); + properties.openTypeYMin = int16([head.data[38], head.data[39]]) - 0x10000; //always negative + properties.openTypeAscent = int16([hhea.data[4], hhea.data[5]]); + properties.openTypeDescent = int16([hhea.data[6], hhea.data[7]]) - 0x10000; //always negative + + // The new numbers of tables will be the last one plus the num // of missing tables createOpenTypeHeader(header.version, ttf, numTables); if (requiredTables.indexOf('OS/2') != -1) { - if (typeof(head) != 'undefined') { - var ymax = int16([head.data[42],head.data[43]]); - var ymin = int16([head.data[38],head.data[39]]) - 0x10000; //always negative - properties.ascent = ymax; - properties.descent = ymin; - } - tables.push({ tag: 'OS/2', data: stringToArray(createOS2Table(properties)) From 530d78e0f854505b814d3dcfe10fca82c3ba659b Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Fri, 16 Sep 2011 23:55:06 +0100 Subject: [PATCH 09/10] Added myself to the license (yay!) Tweaked according to comments in pull request #482 --- LICENSE | 1 + fonts.js | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/LICENSE b/LICENSE index d96b927a3..f8a848205 100644 --- a/LICENSE +++ b/LICENSE @@ -8,6 +8,7 @@ Justin D'Arcangelo Yury Delendik Kalervo Kujala + Adil Allawi <@ironymark> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/fonts.js b/fonts.js index fd7a23bd3..4f95d16f0 100644 --- a/fonts.js +++ b/fonts.js @@ -670,7 +670,9 @@ var Font = (function Font() { format314); }; - function createOS2Table(properties) { + function createOS2Table(properties, override) { + var override = override || {}; + var ulUnicodeRange1 = 0; var ulUnicodeRange2 = 0; var ulUnicodeRange3 = 0; @@ -701,22 +703,19 @@ var Font = (function Font() { } } - var openTypeUnitsPerEm = (typeof (properties.openTypeUnitsPerEm) == 'undefined') ? kPDFGlyphSpaceUnits : properties.openTypeUnitsPerEm; - var typoAscent = properties.ascent; - var typoDescent = properties.descent; - var winAscent = typoAscent; - var winDescent = -typoDescent; + var unitsPerEm = override.unitsPerEm || kPDFGlyphSpaceUnits; + var typoAscent = override.ascent || properties.ascent; + var typoDescent = override.descent || properties.descent; + var winAscent = override.yMax || typoAscent; + var winDescent = -override.yMin || -typoDescent; - // if the font already has ascent and descent information then use these values - if (typeof (properties.openTypeAscent) != 'undefined') { - typoAscent = properties.openTypeAscent; - typoDescent = properties.openTypeDescent; - winAscent = properties.openTypeYMax; - winDescent = -properties.openTypeYMin; - } else if (openTypeUnitsPerEm != kPDFGlyphSpaceUnits) { + // if there is a units per em value but no other override then scale the calculated ascent + if (unitsPerEm != kPDFGlyphSpaceUnits && 'undefined' == typeof(override.ascent)) { // if the font units differ to the PDF glyph space units then scale up the values - typoAscent = Math.round(typoAscent * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); - typoDescent = Math.round(typoDescent * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); + typoAscent = Math.round(typoAscent * unitsPerEm / kPDFGlyphSpaceUnits); + typoDescent = Math.round(typoDescent * unitsPerEm / kPDFGlyphSpaceUnits); + winAscent = typoAscent; + winDescent = -typoDescent; } return '\x00\x03' + // version @@ -1072,22 +1071,23 @@ var Font = (function Font() { virtualOffset: numTables * (4 * 4) }; - //extract some more font properties from the OpenType head and hhea tables - properties.openTypeUnitsPerEm = int16([head.data[18], head.data[19]]); - properties.openTypeYMax = int16([head.data[42], head.data[43]]); - properties.openTypeYMin = int16([head.data[38], head.data[39]]) - 0x10000; //always negative - properties.openTypeAscent = int16([hhea.data[4], hhea.data[5]]); - properties.openTypeDescent = int16([hhea.data[6], hhea.data[7]]) - 0x10000; //always negative - - // The new numbers of tables will be the last one plus the num // of missing tables createOpenTypeHeader(header.version, ttf, numTables); if (requiredTables.indexOf('OS/2') != -1) { + //extract some more font properties from the OpenType head and hhea tables + var override = { + unitsPerEm: int16([head.data[18], head.data[19]]), + yMax: int16([head.data[42], head.data[43]]), + yMin: int16([head.data[38], head.data[39]]) - 0x10000, //always negative + ascent: int16([hhea.data[4], hhea.data[5]]), + descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 //always negative + } + tables.push({ tag: 'OS/2', - data: stringToArray(createOS2Table(properties)) + data: stringToArray(createOS2Table(properties, override)) }); } From 1d00b89285e44d55c1bedcf0fcc5df941e463b81 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 17 Sep 2011 10:32:42 -0500 Subject: [PATCH 10/10] Modifying how the artofwar encryption handled; add wdsg_fitc as reftest --- pdf.js | 11 +++++++++-- test/pdfs/wdsg_fitc.pdf.link | 1 + test/test_manifest.json | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/pdfs/wdsg_fitc.pdf.link diff --git a/pdf.js b/pdf.js index 599e50af8..6a79cdf21 100644 --- a/pdf.js +++ b/pdf.js @@ -3224,7 +3224,14 @@ var XRef = (function() { error('bad XRef entry'); } if (this.encrypt && !suppressEncryption) { - e = parser.getObj(this.encrypt.createCipherTransform(num, gen)); + try { + e = parser.getObj(this.encrypt.createCipherTransform(num, gen)); + } catch(ex) { + // almost all streams must to encrypted, but sometimes + // they are not probably due to some broken generators + // re-trying without encryption + return this.fetch(ref, true); + } } else { e = parser.getObj(); } @@ -4381,7 +4388,7 @@ var PartialEvaluator = (function() { if (type == 'TrueType' && dict.has('ToUnicode') && differences) { var cmapObj = dict.get('ToUnicode'); if (IsRef(cmapObj)) { - cmapObj = xref.fetch(cmapObj, true); + cmapObj = xref.fetch(cmapObj); } if (IsName(cmapObj)) { error('ToUnicode file cmap translation not implemented'); diff --git a/test/pdfs/wdsg_fitc.pdf.link b/test/pdfs/wdsg_fitc.pdf.link new file mode 100644 index 000000000..77d3b590d --- /dev/null +++ b/test/pdfs/wdsg_fitc.pdf.link @@ -0,0 +1 @@ +http://www.airgid.com/book/wdsg_fitc.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index 3734ee9e4..926e37c18 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -115,5 +115,11 @@ "link": true, "rounds": 1, "type": "eq" + }, + { "id": "wdsg_fitc", + "file": "pdfs/wdsg_fitc.pdf", + "link": true, + "rounds": 1, + "type": "eq" } ]