Browse Source

fix merge conflicts

= 14 years ago
parent
commit
ff2dea38ec
  1. 7
      Makefile
  2. 73
      README.md
  3. 6
      crypto.js
  4. 4
      examples/helloworld/README.md
  5. 32
      examples/helloworld/hello.js
  6. 25
      extensions/firefox/bootstrap.js
  7. 87
      extensions/firefox/components/pdfContentHandler.js
  8. 2
      extensions/firefox/install.rdf
  9. 8
      fonts.js
  10. 459
      pdf.js
  11. 14
      test/driver.js
  12. 1
      test/pdfs/unix01.pdf.link
  13. 6
      test/test_manifest.json
  14. 2
      utils/fonts_utils.js
  15. 2
      web/viewer.html
  16. 146
      web/viewer.js

7
Makefile

@ -88,7 +88,8 @@ browser-test:
# To install gjslint, see: # To install gjslint, see:
# #
# <http://code.google.com/closure/utilities/docs/linter_howto.html> # <http://code.google.com/closure/utilities/docs/linter_howto.html>
SRC_DIRS := . utils worker web test SRC_DIRS := . utils worker web test examples/helloworld extensions/firefox \
extensions/firefox/components
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js)) GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
lint: lint:
gjslint $(GJSLINT_FILES) gjslint $(GJSLINT_FILES)
@ -165,9 +166,9 @@ PDF_WEB_FILES = \
extension: extension:
# Copy a standalone version of pdf.js inside the content directory # Copy a standalone version of pdf.js inside the content directory
@rm -Rf $(EXTENSION_SRC)/$(CONTENT_DIR)/ @rm -Rf $(EXTENSION_SRC)/$(CONTENT_DIR)/
@mkdir $(EXTENSION_SRC)/$(CONTENT_DIR)/ @mkdir -p $(EXTENSION_SRC)/$(CONTENT_DIR)/web
@cp $(PDF_JS_FILES) $(EXTENSION_SRC)/$(CONTENT_DIR)/ @cp $(PDF_JS_FILES) $(EXTENSION_SRC)/$(CONTENT_DIR)/
@cp -r $(PDF_WEB_FILES) $(EXTENSION_SRC)/$(CONTENT_DIR)/ @cp -r $(PDF_WEB_FILES) $(EXTENSION_SRC)/$(CONTENT_DIR)/web/
# Create the xpi # Create the xpi
@cd $(EXTENSION_SRC); zip -r $(EXTENSION_NAME) * @cd $(EXTENSION_SRC); zip -r $(EXTENSION_NAME) *

73
README.md

@ -18,46 +18,42 @@ successful.
## Getting started ## Getting started
**Online demo** ### Online demo
For an online demo, visit: For an online demo, visit:
http://andreasgal.github.com/pdf.js/web/viewer.html + http://andreasgal.github.com/pdf.js/web/viewer.html
This demo provides an interactive interface for displaying and browsing PDFs This demo provides an interactive interface for displaying and browsing PDFs
using the pdf.js API. using the pdf.js API.
**Getting the code** ### Getting the code
To get a local copy of the current code, clone it using git: To get a local copy of the current code, clone it using git:
```bash $ git clone git://github.com/andreasgal/pdf.js.git pdfjs
git clone git://github.com/andreasgal/pdf.js.git pdfjs $ cd pdfjs
cd pdfjs
```
Next, you need to start a local web server as some browsers don't allow opening Next, you need to start a local web server as some browsers don't allow opening
PDF files for a file:// url: PDF files for a file:// url:
```bash $ make server
make server
```
If everything worked out, you can now serve If everything worked out, you can now serve
http://localhost:8888/web/viewer.html + http://localhost:8888/web/viewer.html
You can also view all the test pdf files on the right side serving You can also view all the test pdf files on the right side serving
http://localhost:8888/test/pdfs/?frame + http://localhost:8888/test/pdfs/?frame
**Hello world** ### Hello world
For a "hello world" example, take a look at: For a "hello world" example, take a look at:
examples/helloworld/ + [examples/helloworld/hello.js](https://github.com/andreasgal/pdf.js/blob/master/examples/helloworld/hello.js)
This example illustrates the bare minimum ingredients for integrating pdf.js This example illustrates the bare minimum ingredients for integrating pdf.js
in a custom project. in a custom project.
@ -66,16 +62,22 @@ in a custom project.
## Contributing ## Contributing
pdf.js is a community-driver project, so contributors are always welcome. pdf.js is a community-driven project, so contributors are always welcome.
Simply fork our repo and contribute away. A great place to start is our 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 [open issues](https://github.com/andreasgal/pdf.js/issues). For better consistency and
code and try to follow our conventions. long-term stability, please do look around the code and try to follow our conventions.
If you __don't want to hack__ on the project or have short spare times, you still If you don't want to hack on the project or have short spare times, __you still
can help! Just open PDFs in the can help!__ Just open PDFs in the
[online demo](http://andreasgal.github.com/pdf.js/web/viewer.html) and report [online demo](http://andreasgal.github.com/pdf.js/web/viewer.html) and report
any breakage in rendering. any breakage in rendering.
Our Github contributors so far:
+ https://github.com/andreasgal/pdf.js/contributors
You can add your name to it! :)
## Running the Tests ## Running the Tests
@ -108,47 +110,46 @@ raising any errors.
Our demo site is here: Our demo site is here:
http://andreasgal.github.com/pdf.js/web/viewer.html + http://andreasgal.github.com/pdf.js/web/viewer.html
You can read more about pdf.js here: You can read more about pdf.js here:
http://andreasgal.com/2011/06/15/pdf-js/ + http://andreasgal.com/2011/06/15/pdf-js/
+ http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/
http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/ Talk to us on IRC:
Follow us on twitter: @pdfjs
http://twitter.com/#!/pdfjs + #pdfjs on irc.mozilla.org
Join our mailing list: Join our mailing list:
dev-pdf-js@lists.mozilla.org + dev-pdf-js@lists.mozilla.org
Subscribe either using lists.mozilla.org or Google Groups: Subscribe either using lists.mozilla.org or Google Groups:
https://lists.mozilla.org/listinfo/dev-pdf-js + https://lists.mozilla.org/listinfo/dev-pdf-js
+ https://groups.google.com/group/mozilla.dev.pdf-js/topics
https://groups.google.com/group/mozilla.dev.pdf-js/topics Follow us on twitter: @pdfjs
+ http://twitter.com/#!/pdfjs
Talk to us on IRC:
#pdfjs on irc.mozilla.org
## Additional resources to understand the structure of PDF ## Additional resources to understand the structure of PDF
A really basic overview of PDF is described here: A really basic overview of PDF is described here:
http://partners.adobe.com/public/developer/en/livecycle/lc_pdf_overview_format.pdf + http://partners.adobe.com/public/developer/en/livecycle/lc_pdf_overview_format.pdf
A more detailed file example: A more detailed file example:
http://gnupdf.org/Introduction_to_PDF + http://gnupdf.org/Introduction_to_PDF
The PDF specification itself is an ISO and not free available. However, there is The PDF specification itself is an ISO and not freely available. However, there is
a "PDF Reference" from Adobe: a "PDF Reference" from Adobe:
http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/pdf_reference_1-7.pdf + http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/pdf_reference_1-7.pdf
Recommanded chapters to read: "2. Overview", "3.4 File Structure", Recommended chapters to read: "2. Overview", "3.4 File Structure",
"4.1 Graphics Objects" that lists the PDF commands. "4.1 Graphics Objects" that lists the PDF commands.

6
crypto.js

@ -493,16 +493,16 @@ var CipherTransformFactory = (function cipherTransformFactory() {
function constructor(dict, fileId, password) { function constructor(dict, fileId, password) {
var filter = dict.get('Filter'); var filter = dict.get('Filter');
if (!IsName(filter) || filter.name != 'Standard') if (!isName(filter) || filter.name != 'Standard')
error('unknown encryption method'); error('unknown encryption method');
this.dict = dict; this.dict = dict;
var algorithm = dict.get('V'); var algorithm = dict.get('V');
if (!IsInt(algorithm) || if (!isInt(algorithm) ||
(algorithm != 1 && algorithm != 2 && algorithm != 4)) (algorithm != 1 && algorithm != 2 && algorithm != 4))
error('unsupported encryption algorithm'); error('unsupported encryption algorithm');
this.algorithm = algorithm; this.algorithm = algorithm;
var keyLength = dict.get('Length') || 40; var keyLength = dict.get('Length') || 40;
if (!IsInt(keyLength) || if (!isInt(keyLength) ||
keyLength < 40 || (keyLength % 8) != 0) keyLength < 40 || (keyLength % 8) != 0)
error('invalid key length'); error('invalid key length');
// prepare keys // prepare keys

4
examples/helloworld/README.md

@ -1,7 +1,8 @@
## "Hello World" overview ## "Hello World" overview
This example is a minimalistic application of the pdf.js project. The file 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 `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. human-readable PDF.
@ -14,3 +15,4 @@ how to make basic calls to `pdf.js`.
## Additional resources ## Additional resources
+ [GNUpdf - Introduction to PDF](http://gnupdf.org/Introduction_to_PDF) + [GNUpdf - Introduction to PDF](http://gnupdf.org/Introduction_to_PDF)

32
examples/helloworld/hello.js

@ -1,36 +1,17 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
// //
// See README for overview // See README for overview
// //
'use strict';
// getPdf('helloworld.pdf', function getPdfHelloWorld(data) {
// Ajax GET request, for binary files
// (like jQuery's $.get(), but supports the binary type ArrayBuffer)
//
var ajaxGet = 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
//
ajaxGet('helloworld.pdf', function(data){
// //
// Instantiate PDFDoc with PDF data // Instantiate PDFDoc with PDF data
// //
var pdf = new PDFDoc(new Stream(data)); var pdf = new PDFDoc(data);
var page = pdf.getPage(1); var page = pdf.getPage(1);
var scale = 1.5; var scale = 1.5;
@ -47,3 +28,4 @@ ajaxGet('helloworld.pdf', function(data){
// //
page.startRendering(context); page.startRendering(context);
}); });

25
extensions/firefox/bootstrap.js vendored

@ -1,17 +1,22 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
let Cc = Components.classes; let Cc = Components.classes;
let Ci = Components.interfaces; let Ci = Components.interfaces;
let Cm = Components.manager; let Cm = Components.manager;
let Cu = Components.utils; let Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm"); Cu.import('resource://gre/modules/Services.jsm');
function log(str) { function log(str) {
dump(str + "\n"); dump(str + '\n');
}; }
function startup(aData, aReason) { function startup(aData, aReason) {
let manifestPath = "chrome.manifest"; let manifestPath = 'chrome.manifest';
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
try { try {
file.initWithPath(aData.installPath.path); file.initWithPath(aData.installPath.path);
file.append(manifestPath); file.append(manifestPath);
@ -19,13 +24,13 @@ function startup(aData, aReason) {
} catch (e) { } catch (e) {
log(e); log(e);
} }
}; }
function shutdown(aData, aReason) { function shutdown(aData, aReason) {
}; }
function install(aData, aReason) { function install(aData, aReason) {
let url = "chrome://pdf.js/content/web/viewer.html?file=%s"; let url = 'chrome://pdf.js/content/web/viewer.html?file=%s';
Services.prefs.setCharPref("extensions.pdf.js.url", url); Services.prefs.setCharPref('extensions.pdf.js.url', url);
}; }

87
extensions/firefox/components/pdfContentHandler.js

@ -1,52 +1,65 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
const Cc = Components.classes; const Cc = Components.classes;
const Ci = Components.interfaces; const Ci = Components.interfaces;
const Cr = Components.results; const Cr = Components.results;
const Cu = Components.utils; const Cu = Components.utils;
const PDF_CONTENT_TYPE = "application/pdf"; const PDF_CONTENT_TYPE = 'application/pdf';
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// TODO Cu.import('resource://gre/modules/XPCOMUtils.jsm');
// Add some download progress event Cu.import('resource://gre/modules/Services.jsm');
function log(aMsg) { function log(aMsg) {
let msg = "pdfContentHandler.js: " + (aMsg.join ? aMsg.join("") : aMsg); let msg = 'pdfContentHandler.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService) Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService)
.logStringMessage(msg); .logStringMessage(msg);
dump(msg + "\n"); dump(msg + '\n');
}; }
function fireEventTo(aName, aData, aWindow) {
let window = aWindow.wrappedJSObject;
let evt = window.document.createEvent('CustomEvent');
evt.initCustomEvent('pdf' + aName, false, false, aData);
window.document.dispatchEvent(evt);
}
function loadDocument(aWindow, aDocumentUrl) { function loadDocument(aWindow, aDocumentUrl) {
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] let xhr = Cc['@mozilla.org/xmlextras/xmlhttprequest;1']
.createInstance(Ci.nsIXMLHttpRequest); .createInstance(Ci.nsIXMLHttpRequest);
xhr.open("GET", aDocumentUrl); xhr.onprogress = function updateProgress(evt) {
xhr.mozResponseType = xhr.responseType = "arraybuffer"; if (evt.lengthComputable)
xhr.onreadystatechange = function() { fireEventTo(evt.type, evt.loaded / evt.total, aWindow);
if (xhr.readyState == 4 && xhr.status == 200) { };
xhr.onerror = function error(evt) {
fireEventTo(evt.type, false, aWindow);
};
xhr.onload = function load(evt) {
let data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || let data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
xhr.responseArrayBuffer || xhr.response); xhr.responseArrayBuffer || xhr.response);
try { try {
var view = new Uint8Array(data); let view = new Uint8Array(data);
// I think accessing aWindow.wrappedJSObject returns a
// XPCSafeJSObjectWrapper and so it is safe but mrbkap can confirm that
let window = aWindow.wrappedJSObject; let window = aWindow.wrappedJSObject;
var arrayBuffer = new window.ArrayBuffer(data.byteLength); let arrayBuffer = new window.ArrayBuffer(data.byteLength);
var view2 = new window.Uint8Array(arrayBuffer); let view2 = new window.Uint8Array(arrayBuffer);
view2.set(view); view2.set(view);
let evt = window.document.createEvent("CustomEvent"); fireEventTo(evt.type, arrayBuffer, aWindow);
evt.initCustomEvent("pdfloaded", false, false, arrayBuffer);
window.document.dispatchEvent(evt);
} catch (e) { } catch (e) {
log("Error - " + e); log('Error - ' + e);
}
} }
}; };
xhr.open('GET', aDocumentUrl);
xhr.responseType = 'arraybuffer';
xhr.send(null); xhr.send(null);
}; }
let WebProgressListener = { let WebProgressListener = {
init: function(aWindow, aUrl) { init: function(aWindow, aUrl) {
@ -68,7 +81,8 @@ let WebProgressListener = {
webProgress.addProgressListener(this, flags); webProgress.addProgressListener(this, flags);
}, },
onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags,
aStatus) {
const complete = Ci.nsIWebProgressListener.STATE_IS_WINDOW + const complete = Ci.nsIWebProgressListener.STATE_IS_WINDOW +
Ci.nsIWebProgressListener.STATE_STOP; Ci.nsIWebProgressListener.STATE_STOP;
if ((aStateFlags & complete) == complete && this._locationHasChanged) { if ((aStateFlags & complete) == complete && this._locationHasChanged) {
@ -77,14 +91,17 @@ let WebProgressListener = {
} }
}, },
onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) { onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf,
aMaxSelf, aCurTotal, aMaxTotal) {
}, },
onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI) { onLocationChange: function onLocationChange(aWebProgress, aRequest,
aLocationURI) {
this._locationHasChanged = true; this._locationHasChanged = true;
}, },
onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) { onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus,
aMessage) {
}, },
onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) { onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
@ -127,16 +144,16 @@ pdfContentHandler.prototype = {
WebProgressListener.init(window, uri.spec); WebProgressListener.init(window, uri.spec);
try { try {
let url = Services.prefs.getCharPref("extensions.pdf.js.url"); let url = Services.prefs.getCharPref('extensions.pdf.js.url');
url = url.replace("%s", uri.spec); url = url.replace('%s', uri.spec);
window.location = url; window.location = url;
} catch (e) { } catch (e) {
log("Error - " + e); log('Error retrieving the pdf.js base url - ' + e);
} }
}, },
classID: Components.ID("{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}"), classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]), QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler])
}; };
var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]); var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]);

2
extensions/firefox/install.rdf

@ -12,7 +12,7 @@
<Description> <Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>6.0</em:minVersion> <em:minVersion>6.0</em:minVersion>
<em:maxVersion>9.0.*</em:maxVersion> <em:maxVersion>10.0.*</em:maxVersion>
</Description> </Description>
</em:targetApplication> </em:targetApplication>
<em:bootstrap>true</em:bootstrap> <em:bootstrap>true</em:bootstrap>

8
fonts.js

@ -1222,7 +1222,7 @@ var Font = (function Font() {
encoding[i] = { encoding[i] = {
unicode: i <= 0x1f || (i >= 127 && i <= 255) ? unicode: i <= 0x1f || (i >= 127 && i <= 255) ?
i + kCmapGlyphOffset : i, i + kCmapGlyphOffset : i,
width: IsNum(width) ? width : properties.defaultWidth width: isNum(width) ? width : properties.defaultWidth
}; };
} }
} else { } else {
@ -2212,7 +2212,7 @@ CFF.prototype = {
var cmd = map[command]; var cmd = map[command];
assert(cmd, 'Unknow command: ' + command); assert(cmd, 'Unknow command: ' + command);
if (IsArray(cmd)) if (isArray(cmd))
charstring.splice(i++, 1, cmd[0], cmd[1]); charstring.splice(i++, 1, cmd[0], cmd[1]);
else else
charstring[i] = cmd; charstring[i] = cmd;
@ -2338,7 +2338,7 @@ CFF.prototype = {
continue; continue;
var value = properties.private[field]; var value = properties.private[field];
if (IsArray(value)) { if (isArray(value)) {
data += self.encodeNumber(value[0]); data += self.encodeNumber(value[0]);
for (var i = 1; i < value.length; i++) for (var i = 1; i < value.length; i++)
data += self.encodeNumber(value[i] - value[i - 1]); data += self.encodeNumber(value[i] - value[i - 1]);
@ -2497,7 +2497,7 @@ var Type2CFF = (function type2CFF() {
var width = mapping.width; var width = mapping.width;
properties.glyphs[glyph] = properties.encoding[index] = { properties.glyphs[glyph] = properties.encoding[index] = {
unicode: code, unicode: code,
width: IsNum(width) ? width : defaultWidth width: isNum(width) ? width : defaultWidth
}; };
charstrings.push({ charstrings.push({

459
pdf.js

File diff suppressed because it is too large Load Diff

14
test/driver.js

@ -73,26 +73,16 @@ function nextTask() {
log('Loading file "' + task.file + '"\n'); log('Loading file "' + task.file + '"\n');
var r = new XMLHttpRequest(); getPdf(task.file, function nextTaskGetPdf(data) {
r.open('GET', task.file);
r.mozResponseType = r.responseType = 'arraybuffer';
r.onreadystatechange = function nextTaskOnreadystatechange() {
var failure; var failure;
if (r.readyState == 4) {
var data = r.mozResponseArrayBuffer || r.mozResponse ||
r.responseArrayBuffer || r.response;
try { try {
task.pdfDoc = new PDFDoc(data); task.pdfDoc = new PDFDoc(data);
} catch (e) { } catch (e) {
failure = 'load PDF doc : ' + e.toString(); failure = 'load PDF doc : ' + e.toString();
} }
task.pageNum = 1; task.pageNum = 1;
nextPage(task, failure); nextPage(task, failure);
} });
};
r.send(null);
} }
function isLastPage(task) { function isLastPage(task) {

1
test/pdfs/unix01.pdf.link

@ -0,0 +1 @@
https://docs.rice.edu/confluence/download/attachments/4588376/unix01.pdf?version=1

6
test/test_manifest.json

@ -128,6 +128,12 @@
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
}, },
{ "id": "unix01",
"file": "pdfs/unix01.pdf",
"link": true,
"rounds": 1,
"type": "eq"
},
{ "id": "fips197", { "id": "fips197",
"file": "pdfs/fips197.pdf", "file": "pdfs/fips197.pdf",
"link": true, "link": true,

2
utils/fonts_utils.js

@ -258,7 +258,7 @@ var Type2Parser = function(aFilePath) {
var count = decoded.length; var count = decoded.length;
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
var token = decoded[i]; var token = decoded[i];
if (IsNum(token)) { if (isNum(token)) {
stack.push(token); stack.push(token);
} else { } else {
switch (token.operand) { switch (token.operand) {

2
web/viewer.html

@ -28,7 +28,7 @@
<div class="separator"></div> <div class="separator"></div>
<input type="text" id="pageNumber" onchange="PDFView.page = this.value;" value="1" size="4"/> <input type="number" id="pageNumber" onchange="PDFView.page = this.value;" value="1" size="4" min="1" />
<span>/</span> <span>/</span>
<span id="numPages">--</span> <span id="numPages">--</span>

146
web/viewer.js

@ -9,6 +9,8 @@ var kDefaultScaleDelta = 1.1;
var kCacheSize = 20; var kCacheSize = 20;
var kCssUnits = 96.0 / 72.0; var kCssUnits = 96.0 / 72.0;
var kScrollbarPadding = 40; var kScrollbarPadding = 40;
var kMinScale = 0.25;
var kMaxScale = 4.0;
var Cache = function(size) { var Cache = function(size) {
@ -21,11 +23,13 @@ var Cache = function(size) {
}; };
var cache = new Cache(kCacheSize); var cache = new Cache(kCacheSize);
var currentPageNumber = 1;
var PDFView = { var PDFView = {
pages: [], pages: [],
thumbnails: [], thumbnails: [],
currentScale: kDefaultScale, currentScale: kDefaultScale,
initialBookmark: document.location.hash.substring(1),
setScale: function(val, resetAutoSettings) { setScale: function(val, resetAutoSettings) {
var pages = this.pages; var pages = this.pages;
@ -33,11 +37,8 @@ var PDFView = {
pages[i].update(val * kCssUnits); pages[i].update(val * kCssUnits);
this.currentScale = val; this.currentScale = val;
if (document.location.hash == '#' + this.page) this.pages[this.page - 1].scrollIntoView();
this.pages[this.page - 1].draw(); this.pages[this.page - 1].draw();
else
// Jump the scroll position to the correct page.
document.location.hash = this.page;
var event = document.createEvent('UIEvents'); var event = document.createEvent('UIEvents');
event.initUIEvent('scalechange', false, false, window, 0); event.initUIEvent('scalechange', false, false, window, 0);
@ -72,11 +73,13 @@ var PDFView = {
}, },
zoomIn: function() { zoomIn: function() {
this.setScale(this.currentScale * kDefaultScaleDelta, true); var newScale = Math.min(kMaxScale, this.currentScale * kDefaultScaleDelta);
this.setScale(newScale, true);
}, },
zoomOut: function() { zoomOut: function() {
this.setScale(this.currentScale / kDefaultScaleDelta, true); var newScale = Math.max(kMinScale, this.currentScale / kDefaultScaleDelta);
this.setScale(newScale, true);
}, },
set page(val) { set page(val) {
@ -87,18 +90,18 @@ var PDFView = {
return; return;
} }
document.location.hash = val; currentPageNumber = val;
document.getElementById('previous').disabled = (val == 1); document.getElementById('previous').disabled = (val == 1);
document.getElementById('next').disabled = (val == pages.length); document.getElementById('next').disabled = (val == pages.length);
if (input.value == val) if (input.value != val) {
return;
input.value = val; input.value = val;
pages[val - 1].draw(); }
pages[val - 1].scrollIntoView();
}, },
get page() { get page() {
return parseInt(document.location.hash.substring(1), 10) || 1; return currentPageNumber;
}, },
open: function(url, scale) { open: function(url, scale) {
@ -107,28 +110,18 @@ var PDFView = {
document.title = url; document.title = url;
var xhr = new XMLHttpRequest(); getPdf(
xhr.open('GET', url); {
xhr.mozResponseType = xhr.responseType = 'arraybuffer'; url: url,
xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200; progress: function getPdfProgress(evt) {
xhr.onprogress = PDFView.progressLevel; if (evt.lengthComputable)
PDFView.progress(evt.loaded / evt.total);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === xhr.expected) {
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
xhr.responseArrayBuffer || xhr.response);
document.getElementById('loading').style.display = 'none';
PDFView.load(data, scale);
}
};
xhr.send(null);
}, },
error: PDFView.error
progressLevel: function(evt) { },
var p = Math.round((evt.loaded / evt.total) * 100); function getPdfLoad(data) {
document.getElementById('loading').innerHTML = 'Loading... ' + p + '%'; PDFView.load(data, scale);
});
}, },
navigateTo: function(dest) { navigateTo: function(dest) {
@ -147,7 +140,36 @@ var PDFView = {
} }
}, },
getDestinationHash: function(dest) {
if (typeof dest === 'string')
return '#' + escape(dest);
if (dest instanceof Array) {
var destRef = dest[0]; // see nevigateTo method for dest format
var pageNumber = destRef instanceof Object ?
this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
(destRef + 1);
if (pageNumber) {
return '#page=' + pageNumber + '&dest=' + dest.slice(1).join(',');
}
}
return '';
},
error: function() {
var loadingIndicator = document.getElementById('loading');
loadingIndicator.innerHTML = 'Error';
},
progress: function(level) {
var percent = Math.round(level * 100);
var loadingIndicator = document.getElementById('loading');
loadingIndicator.innerHTML = 'Loading... ' + percent + '%';
},
load: function(data, scale) { load: function(data, scale) {
var loadingIndicator = document.getElementById('loading');
loadingIndicator.style.display = 'none';
var sidebar = document.getElementById('sidebarView'); var sidebar = document.getElementById('sidebarView');
sidebar.parentNode.scrollTop = 0; sidebar.parentNode.scrollTop = 0;
@ -162,6 +184,7 @@ var PDFView = {
var pdf = new PDFDoc(data); var pdf = new PDFDoc(data);
var pagesCount = pdf.numPages; var pagesCount = pdf.numPages;
document.getElementById('numPages').innerHTML = pagesCount; document.getElementById('numPages').innerHTML = pagesCount;
document.getElementById('pageNumber').max = pagesCount;
var pages = this.pages = []; var pages = this.pages = [];
var pagesRefMap = {}; var pagesRefMap = {};
@ -177,7 +200,7 @@ var PDFView = {
} }
this.setScale(scale || kDefaultScale, true); this.setScale(scale || kDefaultScale, true);
this.page = parseInt(document.location.hash.substring(1), 10) || 1;
this.pagesRefMap = pagesRefMap; this.pagesRefMap = pagesRefMap;
this.destinations = pdf.catalog.destinations; this.destinations = pdf.catalog.destinations;
if (pdf.catalog.documentOutline) { if (pdf.catalog.documentOutline) {
@ -186,6 +209,28 @@ var PDFView = {
outlineSwitchButton.removeAttribute('disabled'); outlineSwitchButton.removeAttribute('disabled');
this.switchSidebarView('outline'); this.switchSidebarView('outline');
} }
if (this.initialBookmark) {
this.setHash(this.initialBookmark);
this.initialBookmark = null;
}
else
this.page = 1;
},
setHash: function(hash) {
if (!hash)
return;
if (hash.indexOf('=') >= 0) {
// TODO more complex hashes, for now catching page=XX only
var m = /\bpage=(\d+)/.exec(hash);
if (m && m[1] > 0)
this.page = m[1];
} else if (/^\d+$/.test(hash)) // page number
this.page = hash;
else // named destination
PDFView.navigateTo(unescape(hash));
}, },
switchSidebarView: function(view) { switchSidebarView: function(view) {
@ -270,6 +315,7 @@ var PageView = function(container, content, id, pageWidth, pageHeight,
function setupLinks(content, scale) { function setupLinks(content, scale) {
function bindLink(link, dest) { function bindLink(link, dest) {
link.href = PDFView.getDestinationHash(dest);
link.onclick = function() { link.onclick = function() {
if (dest) if (dest)
PDFView.navigateTo(dest); PDFView.navigateTo(dest);
@ -292,6 +338,11 @@ var PageView = function(container, content, id, pageWidth, pageHeight,
} }
this.scrollIntoView = function(dest) { this.scrollIntoView = function(dest) {
if (!dest) {
div.scrollIntoView(true);
return;
}
var x = 0, y = 0; var x = 0, y = 0;
var width = 0, height = 0, widthScale, heightScale; var width = 0, height = 0, widthScale, heightScale;
var scale = 0; var scale = 0;
@ -401,6 +452,10 @@ var PageView = function(container, content, id, pageWidth, pageHeight,
var ThumbnailView = function(container, page, id, pageRatio) { var ThumbnailView = function(container, page, id, pageRatio) {
var anchor = document.createElement('a'); var anchor = document.createElement('a');
anchor.href = '#' + id; anchor.href = '#' + id;
anchor.onclick = function stopNivigation() {
PDFView.page = id;
return false;
};
var div = document.createElement('div'); var div = document.createElement('div');
div.id = 'thumbnailContainer' + id; div.id = 'thumbnailContainer' + id;
@ -448,7 +503,7 @@ var DocumentOutlineView = function(outline) {
var outlineView = document.getElementById('outlineView'); var outlineView = document.getElementById('outlineView');
function bindItemLink(domObj, item) { function bindItemLink(domObj, item) {
domObj.href = ''; domObj.href = PDFView.getDestinationHash(item.dest);
domObj.onclick = function(e) { domObj.onclick = function(e) {
PDFView.navigateTo(item.dest); PDFView.navigateTo(item.dest);
return false; return false;
@ -495,10 +550,18 @@ window.addEventListener('load', function(evt) {
document.getElementById('fileInput').value = null; document.getElementById('fileInput').value = null;
}, true); }, true);
window.addEventListener('pdfloaded', function(evt) { window.addEventListener('pdfload', function(evt) {
PDFView.load(evt.detail); PDFView.load(evt.detail);
}, true); }, true);
window.addEventListener('pdfprogress', function(evt) {
PDFView.progress(evt.detail);
}, true);
window.addEventListener('pdferror', function(evt) {
PDFView.error();
}, true);
function updateViewarea() { function updateViewarea() {
var visiblePages = PDFView.getVisiblePages(); var visiblePages = PDFView.getVisiblePages();
for (var i = 0; i < visiblePages.length; i++) { for (var i = 0; i < visiblePages.length; i++) {
@ -531,7 +594,7 @@ window.addEventListener('resize', function onscroll(evt) {
}); });
window.addEventListener('hashchange', function(evt) { window.addEventListener('hashchange', function(evt) {
PDFView.page = PDFView.page; PDFView.setHash(document.location.hash.substring(1));
}); });
window.addEventListener('change', function(evt) { window.addEventListener('change', function(evt) {
@ -557,7 +620,6 @@ window.addEventListener('change', function(evt) {
fileReader.readAsBinaryString(file); fileReader.readAsBinaryString(file);
document.title = file.name; document.title = file.name;
document.location.hash = 1;
}, true); }, true);
window.addEventListener('transitionend', function(evt) { window.addEventListener('transitionend', function(evt) {
@ -609,13 +671,19 @@ window.addEventListener('scalechange', function scalechange(evt) {
window.addEventListener('pagechange', function pagechange(evt) { window.addEventListener('pagechange', function pagechange(evt) {
var page = evt.detail; var page = evt.detail;
document.location.hash = page;
document.getElementById('pageNumber').value = page; document.getElementById('pageNumber').value = page;
document.getElementById('previous').disabled = (page == 1); document.getElementById('previous').disabled = (page == 1);
document.getElementById('next').disabled = (page == PDFView.pages.length); document.getElementById('next').disabled = (page == PDFView.pages.length);
}, true); }, true);
window.addEventListener('keydown', function keydown(evt) { window.addEventListener('keydown', function keydown(evt) {
var curElement = document.activeElement;
var controlsElement = document.getElementById('controls');
while (curElement) {
if (curElement === controlsElement)
return; // ignoring if the 'controls' element is focused
curElement = curElement.parentNode;
}
switch (evt.keyCode) { switch (evt.keyCode) {
case 61: // FF/Mac '=' case 61: // FF/Mac '='
case 107: // FF '+' and '=' case 107: // FF '+' and '='

Loading…
Cancel
Save