diff --git a/Makefile b/Makefile
index 34a7930a3..c1062d49d 100644
--- a/Makefile
+++ b/Makefile
@@ -209,11 +209,11 @@ 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.html \
web/viewer-production.html \
$(NULL)
@@ -251,8 +251,19 @@ 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/
- @mv -f $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html $(FIREFOX_BUILD_CONTENT)/web/viewer.html
+ @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/
+ # 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
@@ -274,7 +285,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/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
diff --git a/extensions/firefox/bootstrap.js b/extensions/firefox/bootstrap.js
index e51df28f8..f1a712c0c 100644
--- a/extensions/firefox/bootstrap.js
+++ b/extensions/firefox/bootstrap.js
@@ -3,6 +3,8 @@
'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;
@@ -14,6 +16,7 @@ function log(str) {
dump(str + '\n');
}
+
function startup(aData, aReason) {
let manifestPath = 'chrome.manifest';
let manifest = Cc['@mozilla.org/file/local;1']
@@ -34,13 +37,11 @@ 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');
+ application.prefs.setValue(EXT_PREFIX + '.database', '{}');
}
diff --git a/extensions/firefox/chrome.manifest b/extensions/firefox/chrome.manifest
index d7db20b38..5351257e7 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}
+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/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js
new file mode 100644
index 000000000..984915d23
--- /dev/null
+++ b/extensions/firefox/components/PdfStreamConverter.js
@@ -0,0 +1,159 @@
+/* -*- 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 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');
+
+function log(aMsg) {
+ let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
+ Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService)
+ .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(data) {
+ 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;
+}
+// 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');
+ 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);
+};
+
+
+function PdfStreamConverter() {
+}
+
+PdfStreamConverter.prototype = {
+
+ // properties required for XPCOM registration:
+ classID: Components.ID('{6457a96b-2d68-439a-bcfa-44465fbcdbb1}'),
+ classDescription: 'pdf.js Component',
+ contractID: '@mozilla.org/streamconv;1?from=application/pdf&to=*/*',
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsISupports,
+ Ci.nsIStreamConverter,
+ Ci.nsIStreamListener,
+ Ci.nsIRequestObserver
+ ]),
+
+ /*
+ * 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.
+ */
+
+ // nsIStreamConverter::convert
+ convert: function(aFromStream, aFromType, aToType, aCtxt) {
+ 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;
+ },
+
+ // 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);
+ // Cancel the request so the viewer can handle it.
+ 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 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.receive(event);
+ }, false, true);
+ }
+ };
+ gb.addEventListener('DOMContentLoaded', domListener, false);
+ },
+
+ // nsIRequestObserver::onStopRequest
+ onStopRequest: function(aRequest, aContext, aStatusCode) {
+ // Do nothing.
+ }
+};
+
+var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfStreamConverter]);
diff --git a/extensions/firefox/components/pdfContentHandler.js b/extensions/firefox/components/pdfContentHandler.js
deleted file mode 100644
index 67459b759..000000000
--- a/extensions/firefox/components/pdfContentHandler.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/* -*- 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 Ci = Components.interfaces;
-const Cr = Components.results;
-const Cu = Components.utils;
-
-const PDF_CONTENT_TYPE = 'application/pdf';
-
-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);
- Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService)
- .logStringMessage(msg);
- dump(msg + '\n');
-}
-
-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;
-
- if (!Services.prefs.getBoolPref('extensions.pdf.js.active'))
- throw NS_ERROR_WONT_HANDLE_CONTENT;
-
- 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));
- },
-
- classID: Components.ID('{2278dfd0-b75c-11e0-8257-1ba3d93c9f1a}'),
- QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler])
-};
-
-var NSGetFactory = XPCOMUtils.generateNSGetFactory([pdfContentHandler]);
-
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/colorspace.js b/src/colorspace.js
index 827fd2e19..d67d928b1 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
+ var r = (1 - c);
+ var g = (1 - m);
+ var b = (1 - y);
return [r, g, b];
},
diff --git a/src/core.js b/src/core.js
index 7a9f3ee03..cb601398e 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,13 +620,23 @@ 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 {
- // 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.
@@ -716,7 +728,7 @@ var PDFDoc = (function PDFDocClosure() {
});
break;
default:
- throw 'Got unkown object type ' + type;
+ error('Got unkown object type ' + type);
}
}, this);
@@ -737,7 +749,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/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 f96c15458..3f618b82a 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', ['', '', '', '', '', '', '', '', '',
@@ -160,19 +172,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() {
@@ -405,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,
@@ -761,8 +787,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;
@@ -2186,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;
@@ -2194,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;
@@ -2204,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] ||
@@ -2235,9 +2261,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/src/function.js b/src/function.js
index 26b8fe679..4f81158f0 100644
--- a/src/function.js
+++ b/src/function.js
@@ -125,109 +125,99 @@ 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;
- // for each output, do m-linear interpolation
- for (i = 0; i < outputSize; ++i) {
+ // 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;
- // 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];
+ 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[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[i][0], encode[i][1]);
+
+ // e_i' = min(max(e_i, 0), Size_i - 1)
+ var size_i = size[i];
+ e = Math.min(Math.max(e, 0), size_i - 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 {
- sBuf[j] = 0; // TODO Investigate if this is what Adobe does
+ cubeN[j] *= n0;
+ cubeVertex[j] += offset0;
}
}
- // 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];
- }
- }
-
- // 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];
- }
+ k *= size_i;
+ pos <<= 1;
}
- return out;
+
+ 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;
}
},
diff --git a/src/jpx.js b/src/jpx.js
index 61a8f4487..a15c3db54 100644
--- a/src/jpx.js
+++ b/src/jpx.js
@@ -1052,7 +1052,7 @@ var JpxImage = (function JpxImageClosure() {
}
r = 0;
}
- throw 'Out of packets';
+ error('JPX error: Out of packets');
};
}
function ResolutionLayerComponentPositionIterator(context) {
@@ -1091,7 +1091,7 @@ var JpxImage = (function JpxImageClosure() {
}
l = 0;
}
- throw '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:
- throw 'Unsupported progression order';
+ 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)
- throw '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:
- throw 'Invalid SQcd value';
+ 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:
- throw 'Invalid SQcd value';
+ 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)
- throw '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:
- throw 'Unknown codestream code: ' + code.toString(16);
+ error('JPX error: Unknown codestream code: ' + code.toString(16));
}
position += length;
}
diff --git a/src/obj.js b/src/obj.js
index ef7932546..3cdee8778 100644
--- a/src/obj.js
+++ b/src/obj.js
@@ -287,74 +287,69 @@ var XRef = (function XRefClosure() {
XRef.prototype = {
readXRefTable: function readXRefTable(parser) {
+ // 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 obj;
- while (true) {
- if (isCmd(obj = parser.getObj(), 'trailer'))
- break;
- if (!isInt(obj))
- 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) {
+ 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');
+
+ // Inner loop is over objects themselves
+ for (var i = 0; i < count; 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')) {
+ entry.offset = parser.getObj();
+ entry.gen = parser.getObj();
+ var type = parser.getObj();
+
+ if (isCmd(type, 'f'))
entry.free = true;
- } else {
- error('Invalid XRef table: ' + first + ', ' + n);
- }
- 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;
- }
- this.entries[i] = entry;
+ else if (isCmd(type, 'n'))
+ entry.uncompressed = true;
+
+ // Validate entry obj
+ 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;
}
}
- // 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;
- 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;
},
@@ -407,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() {
@@ -529,22 +521,47 @@ 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);
}
warn('Indexing all PDF objects');
@@ -574,7 +591,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 +720,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..fc163171f 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('JPEG 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..b81ff0540 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];
},
@@ -208,6 +208,7 @@ var workerConsole = {
action: 'console_error',
data: args
});
+ throw 'pdf.js execution error';
},
time: function time(name) {
@@ -217,7 +218,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);
}
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/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 5bc344abf..c6fed0a35 100644
--- a/test/test_manifest.json
+++ b/test/test_manifest.json
@@ -402,6 +402,21 @@
"link": true,
"type": "eq"
},
+ { "id": "issue1096",
+ "file": "pdfs/issue1096.pdf",
+ "md5": "7f75d2b4b93c78d401ff39e8c1b00612",
+ "rounds": 1,
+ "pageLimit": 10,
+ "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",
diff --git a/web/compatibility.js b/web/compatibility.js
index 26405ad8f..b22153516 100644
--- a/web/compatibility.js
+++ b/web/compatibility.js
@@ -224,3 +224,10 @@
}
});
})();
+
+// Check console compatability
+(function checkConsoleCompatibility() {
+ if (typeof console == 'undefined') {
+ console = {log: function() {}};
+ }
+})();
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 @@
+
+