Merge text search with current master
3
LICENSE
@ -10,6 +10,9 @@
|
||||
Kalervo Kujala
|
||||
Adil Allawi <@ironymark>
|
||||
Jakob Miland <saebekassebil@gmail.com>
|
||||
Artur Adib <aadib@mozilla.com>
|
||||
Brendan Dahl <bdahl@mozilla.com>
|
||||
David Quintana <gigaherz@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
|
60
Makefile
@ -38,6 +38,8 @@ PDF_JS_FILES = \
|
||||
worker.js \
|
||||
../external/jpgjs/jpg.js \
|
||||
jpx.js \
|
||||
bidi.js \
|
||||
metadata.js \
|
||||
$(NULL)
|
||||
|
||||
# make server
|
||||
@ -59,7 +61,7 @@ test: shell-test browser-test
|
||||
production: | bundle
|
||||
@echo "Preparing web/viewer-production.html"; \
|
||||
cd web; \
|
||||
sed '/PDFJSSCRIPT_REMOVE/d' viewer.html > viewer-1.tmp; \
|
||||
sed '/PDFJSSCRIPT_REMOVE_CORE/d' viewer.html > viewer-1.tmp; \
|
||||
sed '/PDFJSSCRIPT_INCLUDE_BUILD/ r viewer-snippet.html' viewer-1.tmp > viewer-production.html; \
|
||||
rm -f *.tmp; \
|
||||
cd ..
|
||||
@ -143,9 +145,9 @@ browser-test:
|
||||
# To install gjslint, see:
|
||||
#
|
||||
# <http://code.google.com/closure/utilities/docs/linter_howto.html>
|
||||
SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \
|
||||
SRC_DIRS := src utils web test examples/helloworld extensions/firefox \
|
||||
extensions/firefox/components extensions/chrome test/unit
|
||||
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
|
||||
GJSLINT_FILES = $(foreach DIR, $(SRC_DIRS), $(wildcard $(DIR)/*.js))
|
||||
lint:
|
||||
gjslint --nojsdoc $(GJSLINT_FILES)
|
||||
|
||||
@ -209,12 +211,14 @@ 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 = \
|
||||
PDFJSSCRIPT_VERSION := 0.2.$(BUILD_NUMBER)
|
||||
EXTENSION_WEB_FILES = \
|
||||
web/images \
|
||||
web/compatibility.js \
|
||||
web/viewer.css \
|
||||
web/viewer.js \
|
||||
web/viewer.html \
|
||||
web/viewer-production.html \
|
||||
web/debugger.js \
|
||||
$(NULL)
|
||||
|
||||
FIREFOX_BUILD_DIR := $(BUILD_DIR)/firefox
|
||||
@ -223,16 +227,28 @@ FIREFOX_CONTENT_DIR := $(EXTENSION_SRC)/firefox/$(CONTENT_DIR)/
|
||||
FIREFOX_EXTENSION_FILES_TO_COPY = \
|
||||
*.js \
|
||||
*.rdf \
|
||||
chrome.manifest \
|
||||
*.png \
|
||||
install.rdf.in \
|
||||
README.mozilla \
|
||||
components \
|
||||
../../LICENSE \
|
||||
$(NULL)
|
||||
FIREFOX_EXTENSION_FILES = \
|
||||
content \
|
||||
*.js \
|
||||
bootstrap.js \
|
||||
install.rdf \
|
||||
chrome.manifest \
|
||||
icon.png \
|
||||
icon64.png \
|
||||
components \
|
||||
content \
|
||||
LICENSE \
|
||||
$(NULL)
|
||||
FIREFOX_MC_EXTENSION_FILES = \
|
||||
bootstrap.js \
|
||||
icon.png \
|
||||
icon64.png \
|
||||
components \
|
||||
content \
|
||||
LICENSE \
|
||||
$(NULL)
|
||||
|
||||
CHROME_BUILD_DIR := $(BUILD_DIR)/chrome
|
||||
@ -251,12 +267,26 @@ 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_CORE/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
|
||||
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf
|
||||
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf.in
|
||||
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/update.rdf
|
||||
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/README.mozilla
|
||||
@rm -f $(FIREFOX_BUILD_DIR)/*.bak
|
||||
@find $(FIREFOX_BUILD_DIR) -name ".*" -delete
|
||||
# Create the xpi
|
||||
@cd $(FIREFOX_BUILD_DIR); zip -r $(FIREFOX_EXTENSION_NAME) $(FIREFOX_EXTENSION_FILES)
|
||||
@echo "extension created: " $(FIREFOX_EXTENSION_NAME)
|
||||
@ -265,6 +295,8 @@ extension: | production
|
||||
@rm -f $(FIREFOX_BUILD_DIR)/*.bak
|
||||
@cd $(FIREFOX_BUILD_DIR); zip -r $(FIREFOX_AMO_EXTENSION_NAME) $(FIREFOX_EXTENSION_FILES)
|
||||
@echo "AMO extension created: " $(FIREFOX_AMO_EXTENSION_NAME)
|
||||
# List all files for mozilla-central
|
||||
@cd $(FIREFOX_BUILD_DIR); find $(FIREFOX_MC_EXTENSION_FILES) -type f > extension-files
|
||||
|
||||
# Clear out everything in the chrome extension build directory
|
||||
@rm -Rf $(CHROME_BUILD_DIR)
|
||||
@ -274,7 +306,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
|
||||
|
66
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
|
||||
|
||||
@ -26,16 +26,19 @@ using the pdf.js API.
|
||||
|
||||
### Extension
|
||||
|
||||
A Firefox extension is also available:
|
||||
A Firefox extension is availble in two places:
|
||||
|
||||
+ http://mozilla.github.com/pdf.js/extensions/firefox/pdf.js.xpi
|
||||
+ Stable Version: https://addons.mozilla.org/en-US/firefox/addon/pdfjs
|
||||
+ Development Version: http://mozilla.github.com/pdf.js/extensions/firefox/pdf.js.xpi
|
||||
|
||||
Note that this extension is self-updating, and by default Firefox will auto-update extensions on a
|
||||
daily basis (you can change this through the `extensions.update.interval` option in `about:config`).
|
||||
The development extension should be quite stable but still might break from time to time.
|
||||
Also, note that the development extension is updated on every merge and by default Firefox will
|
||||
auto-update extensions on a daily basis (you can change this through the
|
||||
`extensions.update.interval` option in `about:config`).
|
||||
|
||||
For an experimental Chrome extension, get the code as explained below and issue `make extension`.
|
||||
Then open Chrome with the flag `--enable-experimental-extension-apis`, go to `Tools > Extension`
|
||||
and load the (unpackaged) extension from the directory `extensions/chrome`.
|
||||
Then open Chrome, go to `Tools > Extension` and load the (unpackaged) extension
|
||||
from the directory `build/chrome`.
|
||||
|
||||
### Getting the code
|
||||
|
||||
@ -68,22 +71,16 @@ This will generate the file `build/pdf.js` that can be included in your final pr
|
||||
|
||||
# Learning
|
||||
|
||||
Here are some initial pointers to help contributors get off the ground.
|
||||
Additional resources are available in a separate section below.
|
||||
You can play with the PDF.js API directly from your browser through the live demos below:
|
||||
|
||||
### Hello world
|
||||
+ Hello world: http://jsbin.com/pdfjs-helloworld/edit#html,live
|
||||
+ Simple reader with prev/next page controls: http://jsbin.com/pdfjs-prevnext/edit#html,live
|
||||
|
||||
For a "hello world" example, take a look at:
|
||||
The repo contains a hello world example that you can run locally:
|
||||
|
||||
+ [examples/helloworld/hello.js](https://github.com/mozilla/pdf.js/blob/master/examples/helloworld/hello.js)
|
||||
|
||||
This example illustrates the bare minimum ingredients for integrating pdf.js
|
||||
in a custom project.
|
||||
|
||||
### Introductory video
|
||||
|
||||
Check out the presentation by our contributor Julian Viereck on the inner
|
||||
workings of PDF and pdf.js:
|
||||
For an introduction to the PDF.js code, check out the presentation by our contributor Julian Viereck:
|
||||
|
||||
+ http://www.youtube.com/watch?v=Iv15UY-4Fg8
|
||||
|
||||
@ -119,41 +116,14 @@ Our Github contributors so far:
|
||||
You can add your name to it! :)
|
||||
|
||||
|
||||
|
||||
# Running the tests
|
||||
|
||||
pdf.js comes with browser-level regression tests that allow one to probe
|
||||
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.
|
||||
|
||||
|
||||
### Running tests through our bot
|
||||
|
||||
If you are a reviewer, you can use our remote bot to issue comprehensive tests
|
||||
against reference images before merging pull requests.
|
||||
|
||||
See the bot repo for details:
|
||||
|
||||
+ https://github.com/mozilla/pdf.js-bot
|
||||
More information about running the tests can be found on the
|
||||
[contributor wiki page](https://github.com/mozilla/pdf.js/wiki/Contributing).
|
||||
|
||||
|
||||
# Additional resources
|
||||
|
@ -3,7 +3,7 @@
|
||||
"version": "0.1",
|
||||
"description": "Read PDF Document",
|
||||
"permissions": [
|
||||
"experimental",
|
||||
"webRequest", "webRequestBlocking",
|
||||
"http://*/*.pdf",
|
||||
"file:///*/*.pdf"
|
||||
],
|
||||
|
@ -1,18 +1,28 @@
|
||||
<!doctype html>
|
||||
<script>
|
||||
chrome.experimental.webRequest.onBeforeRequest.addListener(
|
||||
|
||||
function isPdfDownloadable(details) {
|
||||
return details.url.indexOf('pdfjs.action=download') >= 0;
|
||||
}
|
||||
|
||||
chrome.webRequest.onBeforeRequest.addListener(
|
||||
function(details) {
|
||||
if (isPdfDownloadable(details))
|
||||
return;
|
||||
|
||||
var viewerPage = 'content/web/viewer.html';
|
||||
var url = chrome.extension.getURL(viewerPage) + '?file=' + details.url;
|
||||
var url = chrome.extension.getURL(viewerPage) +
|
||||
'?file=' + encodeURIComponent(details.url);
|
||||
return { redirectUrl: url };
|
||||
},
|
||||
{
|
||||
urls: [
|
||||
"http://*/*.pdf",
|
||||
"file://*/*.pdf",
|
||||
"file://*/*.pdf"
|
||||
],
|
||||
types: [ "main_frame" ]
|
||||
},
|
||||
["blocking"]);
|
||||
|
||||
</script>
|
||||
|
||||
|
4
extensions/firefox/README.mozilla
Normal file
@ -0,0 +1,4 @@
|
||||
This is the pdf.js project output, https://github.com/mozilla/pdf.js
|
||||
|
||||
Current extension version is: PDFJSSCRIPT_VERSION
|
||||
|
88
extensions/firefox/bootstrap.js
vendored
@ -3,44 +3,96 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const RESOURCE_NAME = 'pdf.js';
|
||||
const EXT_PREFIX = 'extensions.uriloader@pdf.js';
|
||||
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let Cm = Components.manager;
|
||||
let Cu = Components.utils;
|
||||
let application = Cc['@mozilla.org/fuel/application;1']
|
||||
.getService(Ci.fuelIApplication);
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
function log(str) {
|
||||
if (!application.prefs.getValue(EXT_PREFIX + '.pdfBugEnabled', false))
|
||||
return;
|
||||
dump(str + '\n');
|
||||
}
|
||||
|
||||
function startup(aData, aReason) {
|
||||
let manifestPath = 'chrome.manifest';
|
||||
let manifest = Cc['@mozilla.org/file/local;1']
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
try {
|
||||
manifest.initWithPath(aData.installPath.path);
|
||||
manifest.append(manifestPath);
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(manifest);
|
||||
Services.prefs.setBoolPref('extensions.pdf.js.active', true);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
// Register/unregister a class as a component.
|
||||
let Factory = {
|
||||
registrar: null,
|
||||
aClass: null,
|
||||
register: function(aClass) {
|
||||
if (this.aClass) {
|
||||
log('Cannot register more than one class');
|
||||
return;
|
||||
}
|
||||
this.registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
this.aClass = aClass;
|
||||
var proto = aClass.prototype;
|
||||
this.registrar.registerFactory(proto.classID, proto.classDescription,
|
||||
proto.contractID, this);
|
||||
},
|
||||
unregister: function() {
|
||||
if (!this.aClass) {
|
||||
log('Class was never registered.');
|
||||
return;
|
||||
}
|
||||
var proto = this.aClass.prototype;
|
||||
this.registrar.unregisterFactory(proto.classID, this);
|
||||
this.aClass = null;
|
||||
},
|
||||
// nsIFactory::createInstance
|
||||
createInstance: function(outer, iid) {
|
||||
if (outer !== null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return (new (this.aClass)).QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
let pdfStreamConverterUrl = null;
|
||||
|
||||
// As of Firefox 13 bootstrapped add-ons don't support automatic registering and
|
||||
// unregistering of resource urls and components/contracts. Until then we do
|
||||
// it programatically. See ManifestDirective ManifestParser.cpp for support.
|
||||
|
||||
function startup(aData, aReason) {
|
||||
// Setup the resource url.
|
||||
var ioService = Services.io;
|
||||
var resProt = ioService.getProtocolHandler('resource')
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
var aliasURI = ioService.newURI('content/', 'UTF-8', aData.resourceURI);
|
||||
resProt.setSubstitution(RESOURCE_NAME, aliasURI);
|
||||
|
||||
// Load the component and register it.
|
||||
pdfStreamConverterUrl = aData.resourceURI.spec +
|
||||
'components/PdfStreamConverter.js';
|
||||
Cu.import(pdfStreamConverterUrl);
|
||||
Factory.register(PdfStreamConverter);
|
||||
}
|
||||
|
||||
function shutdown(aData, aReason) {
|
||||
if (Services.prefs.getBoolPref('extensions.pdf.js.active'))
|
||||
Services.prefs.setBoolPref('extensions.pdf.js.active', false);
|
||||
if (aReason == APP_SHUTDOWN)
|
||||
return;
|
||||
var ioService = Services.io;
|
||||
var resProt = ioService.getProtocolHandler('resource')
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
// Remove the resource url.
|
||||
resProt.setSubstitution(RESOURCE_NAME, null);
|
||||
// Remove the contract/component.
|
||||
Factory.unregister();
|
||||
// Unload the converter
|
||||
Cu.unload(pdfStreamConverterUrl);
|
||||
pdfStreamConverterUrl = null;
|
||||
}
|
||||
|
||||
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', '{}');
|
||||
}
|
||||
|
||||
|
@ -1,5 +0,0 @@
|
||||
content 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}
|
||||
|
189
extensions/firefox/components/PdfStreamConverter.js
Normal file
@ -0,0 +1,189 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
var EXPORTED_SYMBOLS = ['PdfStreamConverter'];
|
||||
|
||||
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 EXT_PREFIX = 'extensions.uriloader@pdf.js';
|
||||
const MAX_DATABASE_LENGTH = 4096;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
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;
|
||||
|
||||
function log(aMsg) {
|
||||
if (!application.prefs.getValue(EXT_PREFIX + '.pdfBugEnabled', false))
|
||||
return;
|
||||
let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
|
||||
Services.console.logStringMessage(msg);
|
||||
dump(msg + '\n');
|
||||
}
|
||||
|
||||
function getDOMWindow(aChannel) {
|
||||
var requestor = aChannel.notificationCallbacks;
|
||||
var win = requestor.getInterface(Components.interfaces.nsIDOMWindow);
|
||||
return win;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// Protect against something sending tons of data to setDatabase.
|
||||
if (data.length > MAX_DATABASE_LENGTH)
|
||||
return;
|
||||
application.prefs.setValue(EXT_PREFIX + '.database', data);
|
||||
},
|
||||
getDatabase: function() {
|
||||
if (this.inPrivateBrowswing)
|
||||
return '{}';
|
||||
return application.prefs.getValue(EXT_PREFIX + '.database', '{}');
|
||||
},
|
||||
pdfBugEnabled: function() {
|
||||
return application.prefs.getValue(EXT_PREFIX + '.pdfBugEnabled', false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 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) {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
// nsIStreamConverter::asyncConvertData
|
||||
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
|
||||
// Ignoring HTTP POST requests -- pdf.js has to repeat the request.
|
||||
var skipConversion = false;
|
||||
try {
|
||||
var request = aCtxt;
|
||||
request.QueryInterface(Ci.nsIHttpChannel);
|
||||
skipConversion = (request.requestMethod !== 'GET');
|
||||
} catch (e) {
|
||||
// Non-HTTP request... continue normally.
|
||||
}
|
||||
if (skipConversion)
|
||||
throw Cr.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 = Services.io;
|
||||
var channel = ioService.newChannel(
|
||||
'resource://pdf.js/web/viewer.html', null, null);
|
||||
|
||||
var listener = this.listener;
|
||||
// Proxy all the requst observer calls, when it gets to onStopRequst
|
||||
// we can get the dom window.
|
||||
var proxy = {
|
||||
onStartRequest: function() {
|
||||
listener.onStartRequest.apply(listener, arguments);
|
||||
},
|
||||
onDataAvailable: function() {
|
||||
listener.onDataAvailable.apply(listener, arguments);
|
||||
},
|
||||
onStopRequest: function() {
|
||||
var domWindow = getDOMWindow(channel);
|
||||
// Double check the url is still the correct one.
|
||||
if (domWindow.document.documentURIObject.equals(aRequest.URI)) {
|
||||
let requestListener = new RequestListener(new ChromeActions);
|
||||
domWindow.addEventListener(PDFJS_EVENT_ID, function(event) {
|
||||
requestListener.receive(event);
|
||||
}, false, true);
|
||||
}
|
||||
listener.onStopRequest.apply(listener, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
// Keep the URL the same so the browser sees it as the same.
|
||||
channel.originalURI = aRequest.URI;
|
||||
channel.asyncOpen(proxy, aContext);
|
||||
},
|
||||
|
||||
// nsIRequestObserver::onStopRequest
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
// Do nothing.
|
||||
}
|
||||
};
|
||||
|
||||
var NSGetFactory = XPCOMUtils.generateNSGetFactory([PdfStreamConverter]);
|
@ -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]);
|
||||
|
BIN
extensions/firefox/icon.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
extensions/firefox/icon64.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
@ -5,20 +5,18 @@
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>uriloader@pdf.js</em:id>
|
||||
<em:name>pdf.js</em:name>
|
||||
<em:version>0.2.PDFJSSCRIPT_BUILD</em:version>
|
||||
<em:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL>
|
||||
<em:name>PDF Viewer</em:name>
|
||||
<em:version>PDFJSSCRIPT_VERSION</em:version>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>6.0</em:minVersion>
|
||||
<em:maxVersion>11.0a1</em:maxVersion>
|
||||
<em:maxVersion>14.0a1</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>true</em:unpack>
|
||||
<em:creator>Mozilla Labs</em:creator>
|
||||
<em:description>pdf.js uri loader</em:description>
|
||||
<em:creator>Mozilla</em:creator>
|
||||
<em:description>Uses HTML5 to display PDF files directly in Firefox.</em:description>
|
||||
<em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL>
|
||||
<em:type>2</em:type>
|
||||
<!-- Use the raw link for updates so we we can use SSL. -->
|
||||
|
26
extensions/firefox/install.rdf.in
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
#filter substitution
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>uriloader@pdf.js</em:id>
|
||||
<em:name>PDF Viewer</em:name>
|
||||
<em:version>PDFJSSCRIPT_VERSION</em:version>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>@FIREFOX_VERSION@</em:minVersion>
|
||||
<em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
<em:strictCompatibility>true</em:strictCompatibility>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:creator>Mozilla</em:creator>
|
||||
<em:description>Uses HTML5 to display PDF files directly in Firefox.</em:description>
|
||||
<em:homepageURL>https://support.mozilla.org/kb/Opening%20PDF%20files%20within%20Firefox</em:homepageURL>
|
||||
<em:type>2</em:type>
|
||||
</Description>
|
||||
</RDF>
|
@ -8,7 +8,7 @@
|
||||
<RDF:Seq>
|
||||
<RDF:li>
|
||||
<RDF:Description>
|
||||
<em:version>0.2.PDFJSSCRIPT_BUILD</em:version>
|
||||
<em:version>PDFJSSCRIPT_VERSION</em:version>
|
||||
<em:targetApplication>
|
||||
<RDF:Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
|
22
external/shelljs/ChangeLog
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
2012.03.22, Version 0.0.4
|
||||
|
||||
* ls() and find() return arrays instead of hashes (Artur Adib)
|
||||
* exec({silent:...}) overrides global silent() state (Artur Adib)
|
||||
|
||||
|
||||
2012.03.21, Version 0.0.3
|
||||
|
||||
* Wildcard bug fix (Artur Adib)
|
||||
* execSync() now uses dummy file I/O op to reduce CPU usage (Artur Adib)
|
||||
* Minor fixes
|
||||
|
||||
|
||||
2012.03.15, Version 0.0.2
|
||||
|
||||
* New methods: find(), test() (Artur Adib)
|
||||
* Deprecated non-Unix methods: exists(), verbose()
|
||||
|
||||
|
||||
2012.03.03, Version 0.0.2pre1
|
||||
|
||||
* First public release
|
26
external/shelljs/LICENSE
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Copyright (c) 2012, Artur Adib <aadib@mozilla.com>
|
||||
All rights reserved.
|
||||
|
||||
You may use this project under the terms of the New BSD license as follows:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Artur Adib nor the
|
||||
names of the contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
370
external/shelljs/README.md
vendored
Normal file
@ -0,0 +1,370 @@
|
||||
# ShellJS - Unix shell commands for Node.js [](http://travis-ci.org/arturadib/shelljs)
|
||||
|
||||
+ _This project is young and experimental. Use at your own risk._
|
||||
+ _Major API change as of v0.0.4: `ls()` and `find()` now return arrays._
|
||||
|
||||
ShellJS is a **portable** (Windows included) implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands.
|
||||
|
||||
The project is [unit-tested](http://travis-ci.org/arturadib/shelljs) and is being used at Mozilla's [pdf.js](http://github.com/mozilla/pdf.js).
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
require('shelljs/global');
|
||||
|
||||
// Copy files to release dir
|
||||
mkdir('-p', 'out/Release');
|
||||
cp('-R', 'stuff/*', 'out/Release');
|
||||
|
||||
// Replace macros in each .js file
|
||||
cd('lib');
|
||||
ls('*.js').forEach(function(file) {
|
||||
sed('-i', 'BUILD_VERSION', 'v0.1.2', file);
|
||||
sed('-i', /.*REMOVE_THIS_LINE.*\n/, '', file);
|
||||
sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file);
|
||||
});
|
||||
cd('..');
|
||||
|
||||
// Run external tool synchronously
|
||||
if (exec('git commit -am "Auto-commit"').code !== 0) {
|
||||
echo('Error: Git commit failed');
|
||||
exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
### Global vs. Local
|
||||
|
||||
The example above uses the convenience script `shelljs/global` to reduce verbosity. If polluting your global namespace is not desirable, simply require `shelljs`.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
var shell = require('shelljs');
|
||||
shell.echo('hello world');
|
||||
```
|
||||
|
||||
### Make tool
|
||||
|
||||
A convenience script `shelljs/make` is also provided to mimic the behavior of a Unix Makefile. In this case all shell objects are global, and command line arguments will cause the script to execute only the corresponding function in the global `target` object. To avoid redundant calls, target functions are executed only once per script.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
//
|
||||
// Example file: make.js
|
||||
//
|
||||
require('shelljs/make');
|
||||
|
||||
target.all = function() {
|
||||
target.bundle();
|
||||
target.docs();
|
||||
}
|
||||
|
||||
// Bundle JS files
|
||||
target.bundle = function() {
|
||||
cd(__dirname);
|
||||
mkdir('build');
|
||||
cd('lib');
|
||||
cat('*.js').to('../build/output.js');
|
||||
}
|
||||
|
||||
// Generate docs
|
||||
target.docs = function() {
|
||||
cd(__dirname);
|
||||
mkdir('docs');
|
||||
cd('lib');
|
||||
ls('*.js').forEach(function(file){
|
||||
var text = grep('//@', file); // extract special comments
|
||||
text.replace('//@', ''); // remove comment tags
|
||||
text.to('docs/my_docs.md');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
To run the target `all`, call the above script without arguments: `$ node make`. To run the target `docs`: `$ node make docs`, and so on.
|
||||
|
||||
### Installing
|
||||
|
||||
Via npm:
|
||||
|
||||
```bash
|
||||
$ npm install shelljs
|
||||
```
|
||||
|
||||
Or simply copy `shell.js` into your project's directory, and `require()` accordingly.
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
DO NOT MODIFY BEYOND THIS POINT - IT'S AUTOMATICALLY GENERATED
|
||||
|
||||
-->
|
||||
|
||||
|
||||
# Command reference
|
||||
|
||||
|
||||
All commands run synchronously, unless otherwise stated.
|
||||
|
||||
|
||||
#### cd('dir')
|
||||
Changes to directory `dir` for the duration of the script
|
||||
|
||||
#### pwd()
|
||||
Returns the current directory.
|
||||
|
||||
#### ls([options ,] path [,path ...])
|
||||
#### ls([options ,] path_array)
|
||||
Available options:
|
||||
|
||||
+ `-R`: recursive
|
||||
+ `-a`: all files (include files beginning with `.`)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
ls('projs/*.js');
|
||||
ls('-R', '/users/me', '/tmp');
|
||||
ls('-R', ['/users/me', '/tmp']); // same as above
|
||||
```
|
||||
|
||||
Returns array of files in the given path, or in current directory if no path provided.
|
||||
|
||||
#### find(path [,path ...])
|
||||
#### find(path_array)
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
find('src', 'lib');
|
||||
find(['src', 'lib']); // same as above
|
||||
find('.').filter(function(file) { return file.match(/\.js$/); });
|
||||
```
|
||||
|
||||
Returns array of all files (however deep) in the given paths.
|
||||
|
||||
The main difference from `ls('-R', path)` is that the resulting file names
|
||||
include the base directories, e.g. `lib/resources/file1` instead of just `file1`.
|
||||
|
||||
#### cp('[options ,] source [,source ...], dest')
|
||||
#### cp('[options ,] source_array, dest')
|
||||
Available options:
|
||||
|
||||
+ `-f`: force
|
||||
+ `-r, -R`: recursive
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
cp('file1', 'dir1');
|
||||
cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp');
|
||||
cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above
|
||||
```
|
||||
|
||||
Copies files. The wildcard `*` is accepted.
|
||||
|
||||
#### rm([options ,] file [, file ...])
|
||||
#### rm([options ,] file_array)
|
||||
Available options:
|
||||
|
||||
+ `-f`: force
|
||||
+ `-r, -R`: recursive
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
rm('-rf', '/tmp/*');
|
||||
rm('some_file.txt', 'another_file.txt');
|
||||
rm(['some_file.txt', 'another_file.txt']); // same as above
|
||||
```
|
||||
|
||||
Removes files. The wildcard `*` is accepted.
|
||||
|
||||
#### mv(source [, source ...], dest')
|
||||
#### mv(source_array, dest')
|
||||
Available options:
|
||||
|
||||
+ `f`: force
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
mv('-f', 'file', 'dir/');
|
||||
mv('file1', 'file2', 'dir/');
|
||||
mv(['file1', 'file2'], 'dir/'); // same as above
|
||||
```
|
||||
|
||||
Moves files. The wildcard `*` is accepted.
|
||||
|
||||
#### mkdir([options ,] dir [, dir ...])
|
||||
#### mkdir([options ,] dir_array)
|
||||
Available options:
|
||||
|
||||
+ `p`: full path (will create intermediate dirs if necessary)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g');
|
||||
mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above
|
||||
```
|
||||
|
||||
Creates directories.
|
||||
|
||||
#### test(expression)
|
||||
Available expression primaries:
|
||||
|
||||
+ `'-d', 'path'`: true if path is a directory
|
||||
+ `'-f', 'path'`: true if path is a regular file
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
if (test('-d', path)) { /* do something with dir */ };
|
||||
if (!test('-f', path)) continue; // skip if it's a regular file
|
||||
```
|
||||
|
||||
Evaluates expression using the available primaries and returns corresponding value.
|
||||
|
||||
#### cat(file [, file ...])
|
||||
#### cat(file_array)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var str = cat('file*.txt');
|
||||
var str = cat('file1', 'file2');
|
||||
var str = cat(['file1', 'file2']); // same as above
|
||||
```
|
||||
|
||||
Returns a string containing the given file, or a concatenated string
|
||||
containing the files if more than one file is given (a new line character is
|
||||
introduced between each file). Wildcard `*` accepted.
|
||||
|
||||
#### 'string'.to(file)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
cat('input.txt').to('output.txt');
|
||||
```
|
||||
|
||||
Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as
|
||||
those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_
|
||||
|
||||
#### sed([options ,] search_regex, replace_str, file)
|
||||
Available options:
|
||||
|
||||
+ `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js');
|
||||
sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js');
|
||||
```
|
||||
|
||||
Reads an input string from `file` and performs a JavaScript `replace()` on the input
|
||||
using the given search regex and replacement string. Returns the new string after replacement.
|
||||
|
||||
#### grep(regex_filter, file [, file ...])
|
||||
#### grep(regex_filter, file_array)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
grep('GLOBAL_VARIABLE', '*.js');
|
||||
```
|
||||
|
||||
Reads input string from given files and returns a string containing all lines of the
|
||||
file that match the given `regex_filter`. Wildcard `*` accepted.
|
||||
|
||||
#### which(command)
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var nodeExec = which('node');
|
||||
```
|
||||
|
||||
Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions.
|
||||
Returns string containing the absolute path to the command.
|
||||
|
||||
#### echo(string [,string ...])
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
echo('hello world');
|
||||
var str = echo('hello world');
|
||||
```
|
||||
|
||||
Prints string to stdout, and returns string with additional utility methods
|
||||
like `.to()`.
|
||||
|
||||
#### exit(code)
|
||||
Exits the current process with the given exit code.
|
||||
|
||||
#### env['VAR_NAME']
|
||||
Object containing environment variables (both getter and setter). Shortcut to process.env.
|
||||
|
||||
#### exec(command [, options] [, callback])
|
||||
Available options (all `false` by default):
|
||||
|
||||
+ `async`: Asynchronous execution. Needs callback.
|
||||
+ `silent`: Do not echo program output to console.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var version = exec('node --version', {silent:true}).output;
|
||||
```
|
||||
|
||||
Executes the given `command` _synchronously_, unless otherwise specified.
|
||||
When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's
|
||||
`output` (stdout + stderr) and its exit `code`. Otherwise the `callback` gets the
|
||||
arguments `(code, output)`.
|
||||
|
||||
**Note:** For long-lived processes, it's best to run `exec()` asynchronously as
|
||||
the current synchronous implementation uses a lot of CPU. This should be getting
|
||||
fixed soon.
|
||||
|
||||
## Non-Unix commands
|
||||
|
||||
|
||||
#### tempdir()
|
||||
Searches and returns string containing a writeable, platform-dependent temporary directory.
|
||||
Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir).
|
||||
|
||||
#### error()
|
||||
Tests if error occurred in the last command. Returns `null` if no error occurred,
|
||||
otherwise returns string explaining the error
|
||||
|
||||
#### silent([state])
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
var silentState = silent();
|
||||
silent(true);
|
||||
/* ... */
|
||||
silent(silentState); // restore old silent state
|
||||
```
|
||||
|
||||
Suppresses all command output if `state = true`, except for `echo()` calls.
|
||||
Returns state if no arguments given.
|
||||
|
||||
## Deprecated
|
||||
|
||||
|
||||
#### exists(path [, path ...])
|
||||
#### exists(path_array)
|
||||
|
||||
_This function is being deprecated. Use `test()` instead._
|
||||
|
||||
Returns true if all the given paths exist.
|
||||
|
||||
#### verbose()
|
||||
|
||||
_This function is being deprecated. Use `silent(false) instead.`_
|
||||
|
||||
Enables all output (default)
|
3
external/shelljs/global.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
var shell = require('./shell.js');
|
||||
for (cmd in shell)
|
||||
global[cmd] = shell[cmd];
|
46
external/shelljs/make.js
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
require('./global');
|
||||
|
||||
global.target = {};
|
||||
|
||||
// This ensures we only execute the script targets after the entire script has
|
||||
// been evaluated
|
||||
var args = process.argv.slice(2);
|
||||
setTimeout(function() {
|
||||
|
||||
if (args.length === 1 && args[0] === '--help') {
|
||||
console.log('Available targets:');
|
||||
for (t in target)
|
||||
console.log(' ' + t);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap targets to prevent duplicate execution
|
||||
for (t in target) {
|
||||
(function(t, oldTarget){
|
||||
|
||||
// Wrap it
|
||||
target[t] = function(force) {
|
||||
if (oldTarget.done && !force)
|
||||
return;
|
||||
oldTarget.done = true;
|
||||
return oldTarget.apply(oldTarget, arguments);
|
||||
}
|
||||
|
||||
})(t, target[t]);
|
||||
}
|
||||
|
||||
// Execute desired targets
|
||||
if (args.length > 0) {
|
||||
args.forEach(function(arg) {
|
||||
if (arg in target)
|
||||
target[arg]();
|
||||
else {
|
||||
console.log('no such target: ' + arg);
|
||||
exit(1);
|
||||
}
|
||||
});
|
||||
} else if ('all' in target) {
|
||||
target.all();
|
||||
}
|
||||
|
||||
}, 0);
|
29
external/shelljs/package.json
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "shelljs",
|
||||
"version": "0.0.5pre4",
|
||||
"author": "Artur Adib <aadib@mozilla.com>",
|
||||
"description": "Portable Unix shell commands for Node.js",
|
||||
"keywords": [
|
||||
"unix",
|
||||
"shell",
|
||||
"makefile",
|
||||
"make",
|
||||
"jake",
|
||||
"synchronous"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/arturadib/shelljs.git"
|
||||
},
|
||||
"homepage": "http://github.com/arturadib/shelljs",
|
||||
"main": "./shell.js",
|
||||
"scripts": {
|
||||
"test": "node scripts/run-tests"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"optionalDependencies": {},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
}
|
1421
external/shelljs/shell.js
vendored
Normal file
482
make.js
Executable file
@ -0,0 +1,482 @@
|
||||
#!/usr/bin/env node
|
||||
require('./external/shelljs/make');
|
||||
|
||||
var ROOT_DIR = __dirname + '/', // absolute path to project's root
|
||||
BUILD_DIR = 'build/',
|
||||
BUILD_TARGET = BUILD_DIR + 'pdf.js',
|
||||
FIREFOX_BUILD_DIR = BUILD_DIR + '/firefox/',
|
||||
EXTENSION_SRC_DIR = 'extensions/',
|
||||
GH_PAGES_DIR = BUILD_DIR + 'gh-pages/',
|
||||
REPO = 'git@github.com:mozilla/pdf.js.git',
|
||||
PYTHON_BIN = 'python2.7';
|
||||
|
||||
//
|
||||
// make all
|
||||
//
|
||||
target.all = function() {
|
||||
// Don't do anything by default
|
||||
echo('Please specify a target. Available targets:');
|
||||
for (t in target)
|
||||
if (t !== 'all') echo(' ' + t);
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Production stuff
|
||||
//
|
||||
|
||||
//
|
||||
// make web
|
||||
// Generates the website for the project, by checking out the gh-pages branch underneath
|
||||
// the build directory, and then moving the various viewer files into place.
|
||||
//
|
||||
target.web = function() {
|
||||
target.production();
|
||||
target.extension();
|
||||
target.pagesrepo();
|
||||
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Creating web site');
|
||||
|
||||
cp(BUILD_TARGET, GH_PAGES_DIR + BUILD_TARGET);
|
||||
cp('-R', 'web/*', GH_PAGES_DIR + '/web');
|
||||
cp(FIREFOX_BUILD_DIR + '/*.xpi', FIREFOX_BUILD_DIR + '/*.rdf',
|
||||
GH_PAGES_DIR + EXTENSION_SRC_DIR + 'firefox/');
|
||||
cp(GH_PAGES_DIR + '/web/index.html.template', GH_PAGES_DIR + '/index.html');
|
||||
mv('-f', GH_PAGES_DIR + '/web/viewer-production.html',
|
||||
GH_PAGES_DIR + '/web/viewer.html');
|
||||
cd(GH_PAGES_DIR);
|
||||
exec('git add -A');
|
||||
|
||||
echo();
|
||||
echo("Website built in " + GH_PAGES_DIR);
|
||||
echo("Don't forget to cd into " + GH_PAGES_DIR +
|
||||
" and issue 'git commit' to push changes.");
|
||||
};
|
||||
|
||||
//
|
||||
// make production
|
||||
// Creates production output (pdf.js, and corresponding changes to web/ files)
|
||||
//
|
||||
target.production = function() {
|
||||
target.bundle();
|
||||
target.viewer();
|
||||
};
|
||||
|
||||
//
|
||||
// make bundle
|
||||
// Bundles all source files into one wrapper 'pdf.js' file, in the given order.
|
||||
//
|
||||
|
||||
target.bundle = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Bundling files into ' + BUILD_TARGET);
|
||||
|
||||
// File order matters
|
||||
var SRC_FILES =
|
||||
['core.js',
|
||||
'util.js',
|
||||
'canvas.js',
|
||||
'obj.js',
|
||||
'function.js',
|
||||
'charsets.js',
|
||||
'cidmaps.js',
|
||||
'colorspace.js',
|
||||
'crypto.js',
|
||||
'evaluator.js',
|
||||
'fonts.js',
|
||||
'glyphlist.js',
|
||||
'image.js',
|
||||
'metrics.js',
|
||||
'parser.js',
|
||||
'pattern.js',
|
||||
'stream.js',
|
||||
'worker.js',
|
||||
'../external/jpgjs/jpg.js',
|
||||
'jpx.js',
|
||||
'bidi.js',
|
||||
'metadata.js'];
|
||||
|
||||
if (!test('-d', BUILD_DIR))
|
||||
mkdir(BUILD_DIR);
|
||||
|
||||
cd('src');
|
||||
var bundle = cat(SRC_FILES),
|
||||
bundleVersion = exec('git log --format="%h" -n 1',
|
||||
{silent: true}).output.replace('\n', '');
|
||||
|
||||
sed(/.*PDFJSSCRIPT_INCLUDE_ALL.*\n/, bundle, 'pdf.js')
|
||||
.to(ROOT_DIR + BUILD_TARGET);
|
||||
sed('-i', 'PDFJSSCRIPT_BUNDLE_VER', bundleVersion, ROOT_DIR + BUILD_TARGET);
|
||||
};
|
||||
|
||||
//
|
||||
// make viewer
|
||||
// Changes development <script> tags in our web viewer to use only 'pdf.js'.
|
||||
// Produces 'viewer-production.html'
|
||||
//
|
||||
target.viewer = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Generating production-level viewer');
|
||||
|
||||
cd('web');
|
||||
// Remove development lines
|
||||
sed(/.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html')
|
||||
.to('viewer-production.html');
|
||||
// Introduce snippet
|
||||
sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUILD.*\n/g, cat('viewer-snippet.html'),
|
||||
'viewer-production.html');
|
||||
};
|
||||
|
||||
//
|
||||
// make pagesrepo
|
||||
//
|
||||
// This target clones the gh-pages repo into the build directory. It deletes the current contents
|
||||
// of the repo, since we overwrite everything with data from the master repo. The 'make web' target
|
||||
// then uses 'git add -A' to track additions, modifications, moves, and deletions.
|
||||
target.pagesrepo = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Creating fresh clone of gh-pages');
|
||||
|
||||
if (!test('-d', BUILD_DIR))
|
||||
mkdir(BUILD_DIR);
|
||||
|
||||
if (!test('-d', GH_PAGES_DIR)) {
|
||||
echo();
|
||||
echo('Cloning project repo...');
|
||||
echo('(This operation can take a while, depending on network conditions)');
|
||||
exec('git clone -b gh-pages --depth=1 ' + REPO + ' ' + GH_PAGES_DIR,
|
||||
{silent: true});
|
||||
echo('Done.');
|
||||
}
|
||||
|
||||
rm('-rf', GH_PAGES_DIR + '/*');
|
||||
mkdir('-p', GH_PAGES_DIR + '/web');
|
||||
mkdir('-p', GH_PAGES_DIR + '/web/images');
|
||||
mkdir('-p', GH_PAGES_DIR + BUILD_DIR);
|
||||
mkdir('-p', GH_PAGES_DIR + EXTENSION_SRC_DIR + '/firefox');
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Extension stuff
|
||||
//
|
||||
|
||||
var EXTENSION_WEB_FILES =
|
||||
['web/debugger.js',
|
||||
'web/images',
|
||||
'web/viewer.css',
|
||||
'web/viewer.js',
|
||||
'web/viewer.html',
|
||||
'web/viewer-production.html'],
|
||||
EXTENSION_BASE_VERSION = '4bb289ec499013de66eb421737a4dbb4a9273eda',
|
||||
EXTENSION_VERSION_PREFIX = '0.2.',
|
||||
EXTENSION_BUILD_NUMBER,
|
||||
EXTENSION_VERSION;
|
||||
|
||||
//
|
||||
// make extension
|
||||
//
|
||||
target.extension = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Building extensions');
|
||||
|
||||
target.production();
|
||||
target.firefox();
|
||||
target.chrome();
|
||||
};
|
||||
|
||||
target.buildnumber = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Getting extension build number');
|
||||
|
||||
// Build number is the number of commits since base version
|
||||
EXTENSION_BUILD_NUMBER = exec('git log --format=oneline ' +
|
||||
EXTENSION_BASE_VERSION + '..', {silent: true})
|
||||
.output.match(/\n/g).length; // get # of lines in git output
|
||||
|
||||
echo('Extension build number: ' + EXTENSION_BUILD_NUMBER);
|
||||
|
||||
EXTENSION_VERSION = EXTENSION_VERSION_PREFIX + EXTENSION_BUILD_NUMBER;
|
||||
};
|
||||
|
||||
//
|
||||
// make firefox
|
||||
//
|
||||
target.firefox = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Building Firefox extension');
|
||||
|
||||
var FIREFOX_BUILD_CONTENT_DIR = FIREFOX_BUILD_DIR + '/content/',
|
||||
FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/',
|
||||
FIREFOX_EXTENSION_FILES_TO_COPY =
|
||||
['*.js',
|
||||
'*.rdf',
|
||||
'*.png',
|
||||
'install.rdf.in',
|
||||
'README.mozilla',
|
||||
'components',
|
||||
'../../LICENSE'];
|
||||
FIREFOX_EXTENSION_FILES =
|
||||
['bootstrap.js',
|
||||
'install.rdf',
|
||||
'icon.png',
|
||||
'icon64.png',
|
||||
'components',
|
||||
'content',
|
||||
'LICENSE'];
|
||||
FIREFOX_MC_EXTENSION_FILES =
|
||||
['bootstrap.js',
|
||||
'icon.png',
|
||||
'icon64.png',
|
||||
'components',
|
||||
'content',
|
||||
'LICENSE'];
|
||||
FIREFOX_EXTENSION_NAME = 'pdf.js.xpi',
|
||||
FIREFOX_AMO_EXTENSION_NAME = 'pdf.js.amo.xpi';
|
||||
|
||||
target.production();
|
||||
target.buildnumber();
|
||||
cd(ROOT_DIR);
|
||||
|
||||
// Clear out everything in the firefox extension build directory
|
||||
rm('-rf', FIREFOX_BUILD_DIR);
|
||||
mkdir('-p', FIREFOX_BUILD_CONTENT_DIR);
|
||||
mkdir('-p', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR);
|
||||
mkdir('-p', FIREFOX_BUILD_CONTENT_DIR + '/web');
|
||||
|
||||
// Copy extension files
|
||||
cd('extensions/firefox');
|
||||
cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + FIREFOX_BUILD_DIR);
|
||||
cd(ROOT_DIR);
|
||||
|
||||
// Copy a standalone version of pdf.js inside the content directory
|
||||
cp(BUILD_TARGET, FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR);
|
||||
cp('-R', EXTENSION_WEB_FILES, FIREFOX_BUILD_CONTENT_DIR + '/web');
|
||||
rm(FIREFOX_BUILD_CONTENT_DIR + '/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_DIR + '/web');
|
||||
|
||||
// Modify the viewer so it does all the extension-only stuff.
|
||||
cd(FIREFOX_BUILD_CONTENT_DIR + '/web');
|
||||
sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUNDLE.*\n/, cat(ROOT_DIR + BUILD_TARGET), 'viewer-snippet-firefox-extension.html');
|
||||
sed('-i', /.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html');
|
||||
sed('-i', /.*PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION.*\n/g, '', 'viewer.html');
|
||||
sed('-i', /.*PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION.*\n/, cat('viewer-snippet-firefox-extension.html'), 'viewer.html');
|
||||
cd(ROOT_DIR);
|
||||
|
||||
// We don't need pdf.js anymore since its inlined
|
||||
rm('-Rf', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR);
|
||||
// Remove '.DS_Store' and other hidden files
|
||||
find(FIREFOX_BUILD_DIR).forEach(function(file) {
|
||||
if (file.match(/^\./))
|
||||
rm('-f', file);
|
||||
});
|
||||
|
||||
// Update the build version number
|
||||
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/install.rdf');
|
||||
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/update.rdf');
|
||||
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/install.rdf.in');
|
||||
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/README.mozilla');
|
||||
|
||||
// Create the xpi
|
||||
cd(FIREFOX_BUILD_DIR);
|
||||
exec('zip -r ' + FIREFOX_EXTENSION_NAME + ' ' + FIREFOX_EXTENSION_FILES.join(' '));
|
||||
echo('extension created: ' + FIREFOX_EXTENSION_NAME);
|
||||
cd(ROOT_DIR);
|
||||
|
||||
// Build the amo extension too (remove the updateUrl)
|
||||
cd(FIREFOX_BUILD_DIR);
|
||||
sed('-i', /.*updateURL.*\n/, '', 'install.rdf');
|
||||
exec('zip -r ' + FIREFOX_AMO_EXTENSION_NAME + ' ' + FIREFOX_EXTENSION_FILES.join(' '));
|
||||
echo('AMO extension created: ' + FIREFOX_AMO_EXTENSION_NAME);
|
||||
cd(ROOT_DIR);
|
||||
|
||||
// List all files for mozilla-central
|
||||
cd(FIREFOX_BUILD_DIR);
|
||||
var extensionFiles = '';
|
||||
find(FIREFOX_MC_EXTENSION_FILES).forEach(function(file){
|
||||
if (test('-f', file))
|
||||
extensionFiles += file+'\n';
|
||||
});
|
||||
extensionFiles.to('extension-files');
|
||||
};
|
||||
|
||||
//
|
||||
// make chrome
|
||||
//
|
||||
target.chrome = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Building Chrome extension');
|
||||
|
||||
var CHROME_BUILD_DIR = BUILD_DIR + '/chrome/',
|
||||
CHROME_CONTENT_DIR = EXTENSION_SRC_DIR + '/chrome/content/',
|
||||
CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + '/content/',
|
||||
CHROME_EXTENSION_FILES =
|
||||
['extensions/chrome/*.json',
|
||||
'extensions/chrome/*.html'];
|
||||
|
||||
target.production();
|
||||
target.buildnumber();
|
||||
cd(ROOT_DIR);
|
||||
|
||||
// Clear out everything in the chrome extension build directory
|
||||
rm('-Rf', CHROME_BUILD_DIR);
|
||||
mkdir('-p', CHROME_BUILD_CONTENT_DIR);
|
||||
mkdir('-p', CHROME_BUILD_CONTENT_DIR + BUILD_DIR);
|
||||
mkdir('-p', CHROME_BUILD_CONTENT_DIR + '/web');
|
||||
|
||||
// Copy extension files
|
||||
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_DIR + BUILD_DIR);
|
||||
cp('-R', EXTENSION_WEB_FILES, CHROME_BUILD_CONTENT_DIR + '/web');
|
||||
mv('-f', CHROME_BUILD_CONTENT_DIR + '/web/viewer-production.html',
|
||||
CHROME_BUILD_CONTENT_DIR + '/web/viewer.html');
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Test stuff
|
||||
//
|
||||
|
||||
//
|
||||
// make test
|
||||
//
|
||||
target.test = function() {
|
||||
target.browsertest();
|
||||
target.unittest();
|
||||
};
|
||||
|
||||
//
|
||||
// make bottest
|
||||
// (Special tests for the Github bot)
|
||||
//
|
||||
target.bottest = function() {
|
||||
target.browsertest({noreftest: true});
|
||||
// target.unittest();
|
||||
};
|
||||
|
||||
//
|
||||
// make browsertest
|
||||
//
|
||||
target.browsertest = function(options) {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Running browser tests');
|
||||
|
||||
var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json',
|
||||
PDF_BROWSERS = env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json';
|
||||
|
||||
if (!test('-f', 'test/' + PDF_BROWSERS)) {
|
||||
echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.');
|
||||
echo('Try copying one of the examples in test/resources/browser_manifests/');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
var reftest = (options && options.noreftest) ? '' : '--reftest';
|
||||
|
||||
cd('test');
|
||||
exec(PYTHON_BIN + ' -u test.py '+reftest+' --browserManifestFile=' + PDF_BROWSERS +
|
||||
' --manifestFile=' + PDF_TEST, {async: true});
|
||||
};
|
||||
|
||||
//
|
||||
// make unittest
|
||||
//
|
||||
target.unittest = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Running unit tests');
|
||||
|
||||
if (!which('make')) {
|
||||
echo('make not found. Skipping unit tests...');
|
||||
return;
|
||||
}
|
||||
|
||||
cd('test/unit');
|
||||
exec('make', {async: true});
|
||||
};
|
||||
|
||||
//
|
||||
// make botmakeref
|
||||
//
|
||||
target.botmakeref = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Creating reference images');
|
||||
|
||||
var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json',
|
||||
PDF_BROWSERS = env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json';
|
||||
|
||||
if (!test('-f', 'test/' + PDF_BROWSERS)) {
|
||||
echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.');
|
||||
echo('Try copying one of the examples in test/resources/browser_manifests/');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cd('test');
|
||||
exec(PYTHON_BIN + ' -u test.py --masterMode --noPrompts --browserManifestFile=' + PDF_BROWSERS,
|
||||
{async: true});
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Other
|
||||
//
|
||||
|
||||
//
|
||||
// make server
|
||||
//
|
||||
target.server = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Starting local server');
|
||||
|
||||
cd('test');
|
||||
exec(PYTHON_BIN + ' -u test.py --port=8888', {async: true});
|
||||
};
|
||||
|
||||
//
|
||||
// make lint
|
||||
//
|
||||
target.lint = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Linting JS files (this can take a while!)');
|
||||
|
||||
var LINT_FILES = 'src/*.js \
|
||||
web/*.js \
|
||||
test/*.js \
|
||||
test/unit/*.js \
|
||||
extensions/firefox/*.js \
|
||||
extensions/firefox/components/*.js \
|
||||
extensions/chrome/*.js';
|
||||
|
||||
exec('gjslint --nojsdoc ' + LINT_FILES);
|
||||
};
|
||||
|
||||
//
|
||||
// make clean
|
||||
//
|
||||
target.clean = function() {
|
||||
cd(ROOT_DIR);
|
||||
echo();
|
||||
echo('### Cleaning up project builds');
|
||||
|
||||
rm('-rf', BUILD_DIR);
|
||||
};
|
432
src/bidi.js
Normal file
@ -0,0 +1,432 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
var bidi = PDFJS.bidi = (function bidiClosure() {
|
||||
// Character types for symbols from 0000 to 00FF.
|
||||
var baseTypes = [
|
||||
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
|
||||
'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
||||
'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
|
||||
'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
|
||||
'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
|
||||
'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
||||
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
|
||||
'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
||||
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
||||
'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
|
||||
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
||||
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
|
||||
'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
|
||||
'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
|
||||
'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
||||
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
||||
'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
||||
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
|
||||
'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
|
||||
];
|
||||
|
||||
// Character types for symbols from 0600 to 06FF
|
||||
var arabicTypes = [
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
|
||||
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
|
||||
'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
|
||||
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
|
||||
'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
|
||||
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
|
||||
];
|
||||
|
||||
function isOdd(i) {
|
||||
return (i & 1) != 0;
|
||||
}
|
||||
|
||||
function isEven(i) {
|
||||
return (i & 1) == 0;
|
||||
}
|
||||
|
||||
function findUnequal(arr, start, value) {
|
||||
var j;
|
||||
for (var j = start, jj = arr.length; j < jj; ++j) {
|
||||
if (arr[j] != value)
|
||||
return j;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
function setValues(arr, start, end, value) {
|
||||
for (var j = start; j < end; ++j) {
|
||||
arr[j] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function reverseValues(arr, start, end) {
|
||||
for (var i = start, j = end - 1; i < j; ++i, --j) {
|
||||
var temp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
function mirrorGlyphs(c) {
|
||||
/*
|
||||
# BidiMirroring-1.txt
|
||||
0028; 0029 # LEFT PARENTHESIS
|
||||
0029; 0028 # RIGHT PARENTHESIS
|
||||
003C; 003E # LESS-THAN SIGN
|
||||
003E; 003C # GREATER-THAN SIGN
|
||||
005B; 005D # LEFT SQUARE BRACKET
|
||||
005D; 005B # RIGHT SQUARE BRACKET
|
||||
007B; 007D # LEFT CURLY BRACKET
|
||||
007D; 007B # RIGHT CURLY BRACKET
|
||||
00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
|
||||
00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
|
||||
*/
|
||||
switch (c) {
|
||||
case '(':
|
||||
return ')';
|
||||
case ')':
|
||||
return '(';
|
||||
case '<':
|
||||
return '>';
|
||||
case '>':
|
||||
return '<';
|
||||
case ']':
|
||||
return '[';
|
||||
case '[':
|
||||
return ']';
|
||||
case '}':
|
||||
return '{';
|
||||
case '{':
|
||||
return '}';
|
||||
case '\u00AB':
|
||||
return '\u00BB';
|
||||
case '\u00BB':
|
||||
return '\u00AB';
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
function bidi(text, startLevel) {
|
||||
var str = text.str;
|
||||
var strLength = str.length;
|
||||
if (strLength == 0)
|
||||
return str;
|
||||
|
||||
// get types, fill arrays
|
||||
|
||||
var chars = [];
|
||||
var types = [];
|
||||
var oldtypes = [];
|
||||
var numBidi = 0;
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
chars[i] = str.charAt(i);
|
||||
|
||||
var charCode = str.charCodeAt(i);
|
||||
var charType = 'L';
|
||||
if (charCode <= 0x00ff)
|
||||
charType = baseTypes[charCode];
|
||||
else if (0x0590 <= charCode && charCode <= 0x05f4)
|
||||
charType = 'R';
|
||||
else if (0x0600 <= charCode && charCode <= 0x06ff)
|
||||
charType = arabicTypes[charCode & 0xff];
|
||||
else if (0x0700 <= charCode && charCode <= 0x08AC)
|
||||
charType = 'AL';
|
||||
|
||||
if (charType == 'R' || charType == 'AL' || charType == 'AN')
|
||||
numBidi++;
|
||||
|
||||
oldtypes[i] = types[i] = charType;
|
||||
}
|
||||
|
||||
// detect the bidi method
|
||||
// if there are no rtl characters then no bidi needed
|
||||
// if less than 30% chars are rtl then string is primarily ltr
|
||||
// if more than 30% chars are rtl then string is primarily rtl
|
||||
if (numBidi == 0) {
|
||||
text.direction = 'ltr';
|
||||
return str;
|
||||
}
|
||||
|
||||
if (startLevel == -1) {
|
||||
if ((strLength / numBidi) < 0.3) {
|
||||
text.direction = 'ltr';
|
||||
startLevel = 0;
|
||||
} else {
|
||||
text.direction = 'rtl';
|
||||
startLevel = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var levels = [];
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
levels[i] = startLevel;
|
||||
}
|
||||
|
||||
/*
|
||||
X1-X10: skip most of this, since we are NOT doing the embeddings.
|
||||
*/
|
||||
|
||||
var e = isOdd(startLevel) ? 'R' : 'L';
|
||||
var sor = e;
|
||||
var eor = sor;
|
||||
|
||||
/*
|
||||
W1. Examine each non-spacing mark (NSM) in the level run, and change the
|
||||
type of the NSM to the type of the previous character. If the NSM is at the
|
||||
start of the level run, it will get the type of sor.
|
||||
*/
|
||||
|
||||
var lastType = sor;
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
if (types[i] == 'NSM')
|
||||
types[i] = lastType;
|
||||
else
|
||||
lastType = types[i];
|
||||
}
|
||||
|
||||
/*
|
||||
W2. Search backwards from each instance of a European number until the
|
||||
first strong type (R, L, AL, or sor) is found. If an AL is found, change
|
||||
the type of the European number to Arabic number.
|
||||
*/
|
||||
|
||||
var lastType = sor;
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
var t = types[i];
|
||||
if (t == 'EN')
|
||||
types[i] = (lastType == 'AL') ? 'AN' : 'EN';
|
||||
else if (t == 'R' || t == 'L' || t == 'AL')
|
||||
lastType = t;
|
||||
}
|
||||
|
||||
/*
|
||||
W3. Change all ALs to R.
|
||||
*/
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
var t = types[i];
|
||||
if (t == 'AL')
|
||||
types[i] = 'R';
|
||||
}
|
||||
|
||||
/*
|
||||
W4. A single European separator between two European numbers changes to a
|
||||
European number. A single common separator between two numbers of the same
|
||||
type changes to that type:
|
||||
*/
|
||||
|
||||
for (var i = 1; i < strLength - 1; ++i) {
|
||||
if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN')
|
||||
types[i] = 'EN';
|
||||
if (types[i] == 'CS' && (types[i - 1] == 'EN' || types[i - 1] == 'AN') &&
|
||||
types[i + 1] == types[i - 1])
|
||||
types[i] = types[i - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
W5. A sequence of European terminators adjacent to European numbers changes
|
||||
to all European numbers:
|
||||
*/
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
if (types[i] == 'EN') {
|
||||
// do before
|
||||
for (var j = i - 1; j >= 0; --j) {
|
||||
if (types[j] != 'ET')
|
||||
break;
|
||||
types[j] = 'EN';
|
||||
}
|
||||
// do after
|
||||
for (var j = i + 1; j < strLength; --j) {
|
||||
if (types[j] != 'ET')
|
||||
break;
|
||||
types[j] = 'EN';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
W6. Otherwise, separators and terminators change to Other Neutral:
|
||||
*/
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
var t = types[i];
|
||||
if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS')
|
||||
types[i] = 'ON';
|
||||
}
|
||||
|
||||
/*
|
||||
W7. Search backwards from each instance of a European number until the
|
||||
first strong type (R, L, or sor) is found. If an L is found, then change
|
||||
the type of the European number to L.
|
||||
*/
|
||||
|
||||
var lastType = sor;
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
var t = types[i];
|
||||
if (t == 'EN')
|
||||
types[i] = (lastType == 'L') ? 'L' : 'EN';
|
||||
else if (t == 'R' || t == 'L')
|
||||
lastType = t;
|
||||
}
|
||||
|
||||
/*
|
||||
N1. A sequence of neutrals takes the direction of the surrounding strong
|
||||
text if the text on both sides has the same direction. European and Arabic
|
||||
numbers are treated as though they were R. Start-of-level-run (sor) and
|
||||
end-of-level-run (eor) are used at level run boundaries.
|
||||
*/
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
if (types[i] == 'ON') {
|
||||
var end = findUnequal(types, i + 1, 'ON');
|
||||
var before = sor;
|
||||
if (i > 0)
|
||||
before = types[i - 1];
|
||||
var after = eor;
|
||||
if (end + 1 < strLength)
|
||||
after = types[end + 1];
|
||||
if (before != 'L')
|
||||
before = 'R';
|
||||
if (after != 'L')
|
||||
after = 'R';
|
||||
if (before == after)
|
||||
setValues(types, i, end, before);
|
||||
i = end - 1; // reset to end (-1 so next iteration is ok)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
N2. Any remaining neutrals take the embedding direction.
|
||||
*/
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
if (types[i] == 'ON')
|
||||
types[i] = e;
|
||||
}
|
||||
|
||||
/*
|
||||
I1. For all characters with an even (left-to-right) embedding direction,
|
||||
those of type R go up one level and those of type AN or EN go up two
|
||||
levels.
|
||||
I2. For all characters with an odd (right-to-left) embedding direction,
|
||||
those of type L, EN or AN go up one level.
|
||||
*/
|
||||
|
||||
for (var i = 0; i < strLength; ++i) {
|
||||
var t = types[i];
|
||||
if (isEven(levels[i])) {
|
||||
if (t == 'R') {
|
||||
levels[i] += 1;
|
||||
} else if (t == 'AN' || t == 'EN') {
|
||||
levels[i] += 2;
|
||||
}
|
||||
} else { // isOdd, so
|
||||
if (t == 'L' || t == 'AN' || t == 'EN') {
|
||||
levels[i] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
L1. On each line, reset the embedding level of the following characters to
|
||||
the paragraph embedding level:
|
||||
|
||||
segment separators,
|
||||
paragraph separators,
|
||||
any sequence of whitespace characters preceding a segment separator or
|
||||
paragraph separator, and any sequence of white space characters at the end
|
||||
of the line.
|
||||
*/
|
||||
|
||||
// don't bother as text is only single line
|
||||
|
||||
/*
|
||||
L2. From the highest level found in the text to the lowest odd level on
|
||||
each line, reverse any contiguous sequence of characters that are at that
|
||||
level or higher.
|
||||
*/
|
||||
|
||||
// find highest level & lowest odd level
|
||||
|
||||
var highestLevel = -1;
|
||||
var lowestOddLevel = 99;
|
||||
for (var i = 0, ii = levels.length; i < ii; ++i) {
|
||||
var level = levels[i];
|
||||
if (highestLevel < level)
|
||||
highestLevel = level;
|
||||
if (lowestOddLevel > level && isOdd(level))
|
||||
lowestOddLevel = level;
|
||||
}
|
||||
|
||||
// now reverse between those limits
|
||||
|
||||
for (var level = highestLevel; level >= lowestOddLevel; --level) {
|
||||
// find segments to reverse
|
||||
var start = -1;
|
||||
for (var i = 0, ii = levels.length; i < ii; ++i) {
|
||||
if (levels[i] < level) {
|
||||
if (start >= 0) {
|
||||
reverseValues(chars, start, i);
|
||||
start = -1;
|
||||
}
|
||||
} else if (start < 0) {
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
if (start >= 0) {
|
||||
reverseValues(chars, start, levels.length);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
L3. Combining marks applied to a right-to-left base character will at this
|
||||
point precede their base character. If the rendering engine expects them to
|
||||
follow the base characters in the final display process, then the ordering
|
||||
of the marks and the base character must be reversed.
|
||||
*/
|
||||
|
||||
// don't bother for now
|
||||
|
||||
/*
|
||||
L4. A character that possesses the mirrored property as specified by
|
||||
Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
|
||||
directionality of that character is R.
|
||||
*/
|
||||
|
||||
// don't mirror as characters are already mirrored in the pdf
|
||||
|
||||
// Finally, return string
|
||||
|
||||
var result = '';
|
||||
for (var i = 0, ii = chars.length; i < ii; ++i) {
|
||||
var ch = chars[i];
|
||||
if (ch != '<' && ch != '>')
|
||||
result += ch;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return bidi;
|
||||
})();
|
||||
|
449
src/canvas.js
@ -17,55 +17,10 @@ var TextRenderingMode = {
|
||||
ADD_TO_PATH: 7
|
||||
};
|
||||
|
||||
var CanvasExtraState = (function CanvasExtraStateClosure() {
|
||||
function CanvasExtraState(old) {
|
||||
// Are soft masks and alpha values shapes or opacities?
|
||||
this.alphaIsShape = false;
|
||||
this.fontSize = 0;
|
||||
this.textMatrix = IDENTITY_MATRIX;
|
||||
this.leading = 0;
|
||||
// Current point (in user coordinates)
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
// Start of text line (in text coordinates)
|
||||
this.lineX = 0;
|
||||
this.lineY = 0;
|
||||
// Character and word spacing
|
||||
this.charSpacing = 0;
|
||||
this.wordSpacing = 0;
|
||||
this.textHScale = 1;
|
||||
this.textRenderingMode = TextRenderingMode.FILL;
|
||||
// Color spaces
|
||||
this.fillColorSpace = new DeviceGrayCS();
|
||||
this.fillColorSpaceObj = null;
|
||||
this.strokeColorSpace = new DeviceGrayCS();
|
||||
this.strokeColorSpaceObj = null;
|
||||
this.fillColorObj = null;
|
||||
this.strokeColorObj = null;
|
||||
// Default fore and background colors
|
||||
this.fillColor = '#000000';
|
||||
this.strokeColor = '#000000';
|
||||
// Note: fill alpha applies to all non-stroking operations
|
||||
this.fillAlpha = 1;
|
||||
this.strokeAlpha = 1;
|
||||
this.lineWidth = 1;
|
||||
// Minimal font size that would be used during canvas fillText operations.
|
||||
var MIN_FONT_SIZE = 1;
|
||||
|
||||
this.old = old;
|
||||
}
|
||||
|
||||
CanvasExtraState.prototype = {
|
||||
clone: function canvasextra_clone() {
|
||||
return Object.create(this);
|
||||
},
|
||||
setCurrentPoint: function canvasextra_setCurrentPoint(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
};
|
||||
return CanvasExtraState;
|
||||
})();
|
||||
|
||||
function ScratchCanvas(width, height) {
|
||||
function createScratchCanvas(width, height) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
@ -182,10 +137,60 @@ function addContextCurrentTransform(ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
var CanvasExtraState = (function CanvasExtraStateClosure() {
|
||||
function CanvasExtraState(old) {
|
||||
// Are soft masks and alpha values shapes or opacities?
|
||||
this.alphaIsShape = false;
|
||||
this.fontSize = 0;
|
||||
this.fontSizeScale = 1;
|
||||
this.textMatrix = IDENTITY_MATRIX;
|
||||
this.fontMatrix = IDENTITY_MATRIX;
|
||||
this.leading = 0;
|
||||
// Current point (in user coordinates)
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
// Start of text line (in text coordinates)
|
||||
this.lineX = 0;
|
||||
this.lineY = 0;
|
||||
// Character and word spacing
|
||||
this.charSpacing = 0;
|
||||
this.wordSpacing = 0;
|
||||
this.textHScale = 1;
|
||||
this.textRenderingMode = TextRenderingMode.FILL;
|
||||
// Color spaces
|
||||
this.fillColorSpace = new DeviceGrayCS();
|
||||
this.fillColorSpaceObj = null;
|
||||
this.strokeColorSpace = new DeviceGrayCS();
|
||||
this.strokeColorSpaceObj = null;
|
||||
this.fillColorObj = null;
|
||||
this.strokeColorObj = null;
|
||||
// Default fore and background colors
|
||||
this.fillColor = '#000000';
|
||||
this.strokeColor = '#000000';
|
||||
// Note: fill alpha applies to all non-stroking operations
|
||||
this.fillAlpha = 1;
|
||||
this.strokeAlpha = 1;
|
||||
this.lineWidth = 1;
|
||||
|
||||
this.old = old;
|
||||
}
|
||||
|
||||
CanvasExtraState.prototype = {
|
||||
clone: function CanvasExtraState_clone() {
|
||||
return Object.create(this);
|
||||
},
|
||||
setCurrentPoint: function CanvasExtraState_setCurrentPoint(x, y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
};
|
||||
return CanvasExtraState;
|
||||
})();
|
||||
|
||||
var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
// Defines the time the executeIRQueue is going to be executing
|
||||
// Defines the time the executeOperatorList is going to be executing
|
||||
// before it stops and shedules a continue of execution.
|
||||
var kExecutionTime = 50;
|
||||
var kExecutionTime = 15;
|
||||
|
||||
function CanvasGraphics(canvasCtx, objs, textLayer) {
|
||||
this.ctx = canvasCtx;
|
||||
@ -194,7 +199,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.pendingClip = null;
|
||||
this.res = null;
|
||||
this.xobjs = null;
|
||||
this.ScratchCanvas = ScratchCanvas;
|
||||
this.objs = objs;
|
||||
this.textLayer = textLayer;
|
||||
if (canvasCtx) {
|
||||
@ -224,7 +228,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
'setStrokeColor': true,
|
||||
'setStrokeColorN': true,
|
||||
'setFillColor': true,
|
||||
'setFillColorN_IR': true,
|
||||
'setFillColorN': true,
|
||||
'setStrokeGray': true,
|
||||
'setFillGray': true,
|
||||
'setStrokeRGBColor': true,
|
||||
@ -237,7 +241,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
'shadingFill': true
|
||||
},
|
||||
|
||||
beginDrawing: function canvasGraphicsBeginDrawing(mediaBox) {
|
||||
beginDrawing: function CanvasGraphics_beginDrawing(mediaBox) {
|
||||
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
|
||||
this.ctx.save();
|
||||
switch (mediaBox.rotate) {
|
||||
@ -263,14 +267,16 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.textLayer.beginLayout();
|
||||
},
|
||||
|
||||
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
|
||||
executionStartIdx, continueCallback) {
|
||||
var argsArray = codeIR.argsArray;
|
||||
var fnArray = codeIR.fnArray;
|
||||
executeOperatorList: function CanvasGraphics_executeOperatorList(
|
||||
operatorList,
|
||||
executionStartIdx, continueCallback,
|
||||
stepper) {
|
||||
var argsArray = operatorList.argsArray;
|
||||
var fnArray = operatorList.fnArray;
|
||||
var i = executionStartIdx || 0;
|
||||
var argsArrayLen = argsArray.length;
|
||||
|
||||
// Sometimes the IRQueue to execute is empty.
|
||||
// Sometimes the OperatorList to execute is empty.
|
||||
if (argsArrayLen == i) {
|
||||
return i;
|
||||
}
|
||||
@ -283,6 +289,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
var slowCommands = this.slowCommands;
|
||||
|
||||
while (true) {
|
||||
if (stepper && i === stepper.nextBreakPoint) {
|
||||
stepper.breakIt(i, continueCallback);
|
||||
return i;
|
||||
}
|
||||
|
||||
fnName = fnArray[i];
|
||||
|
||||
if (fnName !== 'dependency') {
|
||||
@ -303,7 +314,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
|
||||
i++;
|
||||
|
||||
// If the entire IRQueue was executed, stop as were done.
|
||||
// If the entire operatorList was executed, stop as were done.
|
||||
if (i == argsArrayLen) {
|
||||
return i;
|
||||
}
|
||||
@ -316,12 +327,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
return i;
|
||||
}
|
||||
|
||||
// If the IRQueue isn't executed completly yet OR the execution time
|
||||
// was short enough, do another execution round.
|
||||
// If the operatorList isn't executed completely yet OR the execution
|
||||
// time was short enough, do another execution round.
|
||||
}
|
||||
},
|
||||
|
||||
endDrawing: function canvasGraphicsEndDrawing() {
|
||||
endDrawing: function CanvasGraphics_endDrawing() {
|
||||
this.ctx.restore();
|
||||
|
||||
if (this.textLayer)
|
||||
@ -329,32 +340,32 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
},
|
||||
|
||||
// Graphics state
|
||||
setLineWidth: function canvasGraphicsSetLineWidth(width) {
|
||||
setLineWidth: function CanvasGraphics_setLineWidth(width) {
|
||||
this.current.lineWidth = width;
|
||||
this.ctx.lineWidth = width;
|
||||
},
|
||||
setLineCap: function canvasGraphicsSetLineCap(style) {
|
||||
setLineCap: function CanvasGraphics_setLineCap(style) {
|
||||
this.ctx.lineCap = LINE_CAP_STYLES[style];
|
||||
},
|
||||
setLineJoin: function canvasGraphicsSetLineJoin(style) {
|
||||
setLineJoin: function CanvasGraphics_setLineJoin(style) {
|
||||
this.ctx.lineJoin = LINE_JOIN_STYLES[style];
|
||||
},
|
||||
setMiterLimit: function canvasGraphicsSetMiterLimit(limit) {
|
||||
setMiterLimit: function CanvasGraphics_setMiterLimit(limit) {
|
||||
this.ctx.miterLimit = limit;
|
||||
},
|
||||
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
||||
setDash: function CanvasGraphics_setDash(dashArray, dashPhase) {
|
||||
this.ctx.mozDash = dashArray;
|
||||
this.ctx.mozDashOffset = dashPhase;
|
||||
this.ctx.webkitLineDash = dashArray;
|
||||
this.ctx.webkitLineDashOffset = dashPhase;
|
||||
},
|
||||
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
||||
setRenderingIntent: function CanvasGraphics_setRenderingIntent(intent) {
|
||||
TODO('set rendering intent: ' + intent);
|
||||
},
|
||||
setFlatness: function canvasGraphicsSetFlatness(flatness) {
|
||||
setFlatness: function CanvasGraphics_setFlatness(flatness) {
|
||||
TODO('set flatness: ' + flatness);
|
||||
},
|
||||
setGState: function canvasGraphicsSetGState(states) {
|
||||
setGState: function CanvasGraphics_setGState(states) {
|
||||
for (var i = 0, ii = states.length; i < ii; i++) {
|
||||
var state = states[i];
|
||||
var key = state[0];
|
||||
@ -395,52 +406,52 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
}
|
||||
},
|
||||
save: function canvasGraphicsSave() {
|
||||
save: function CanvasGraphics_save() {
|
||||
this.ctx.save();
|
||||
var old = this.current;
|
||||
this.stateStack.push(old);
|
||||
this.current = old.clone();
|
||||
},
|
||||
restore: function canvasGraphicsRestore() {
|
||||
restore: function CanvasGraphics_restore() {
|
||||
var prev = this.stateStack.pop();
|
||||
if (prev) {
|
||||
this.current = prev;
|
||||
this.ctx.restore();
|
||||
}
|
||||
},
|
||||
transform: function canvasGraphicsTransform(a, b, c, d, e, f) {
|
||||
transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
|
||||
this.ctx.transform(a, b, c, d, e, f);
|
||||
},
|
||||
|
||||
// Path
|
||||
moveTo: function canvasGraphicsMoveTo(x, y) {
|
||||
moveTo: function CanvasGraphics_moveTo(x, y) {
|
||||
this.ctx.moveTo(x, y);
|
||||
this.current.setCurrentPoint(x, y);
|
||||
},
|
||||
lineTo: function canvasGraphicsLineTo(x, y) {
|
||||
lineTo: function CanvasGraphics_lineTo(x, y) {
|
||||
this.ctx.lineTo(x, y);
|
||||
this.current.setCurrentPoint(x, y);
|
||||
},
|
||||
curveTo: function canvasGraphicsCurveTo(x1, y1, x2, y2, x3, y3) {
|
||||
curveTo: function CanvasGraphics_curveTo(x1, y1, x2, y2, x3, y3) {
|
||||
this.ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
|
||||
this.current.setCurrentPoint(x3, y3);
|
||||
},
|
||||
curveTo2: function canvasGraphicsCurveTo2(x2, y2, x3, y3) {
|
||||
curveTo2: function CanvasGraphics_curveTo2(x2, y2, x3, y3) {
|
||||
var current = this.current;
|
||||
this.ctx.bezierCurveTo(current.x, current.y, x2, y2, x3, y3);
|
||||
current.setCurrentPoint(x3, y3);
|
||||
},
|
||||
curveTo3: function canvasGraphicsCurveTo3(x1, y1, x3, y3) {
|
||||
curveTo3: function CanvasGraphics_curveTo3(x1, y1, x3, y3) {
|
||||
this.curveTo(x1, y1, x3, y3, x3, y3);
|
||||
this.current.setCurrentPoint(x3, y3);
|
||||
},
|
||||
closePath: function canvasGraphicsClosePath() {
|
||||
closePath: function CanvasGraphics_closePath() {
|
||||
this.ctx.closePath();
|
||||
},
|
||||
rectangle: function canvasGraphicsRectangle(x, y, width, height) {
|
||||
rectangle: function CanvasGraphics_rectangle(x, y, width, height) {
|
||||
this.ctx.rect(x, y, width, height);
|
||||
},
|
||||
stroke: function canvasGraphicsStroke(consumePath) {
|
||||
stroke: function CanvasGraphics_stroke(consumePath) {
|
||||
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
||||
var ctx = this.ctx;
|
||||
var strokeColor = this.current.strokeColor;
|
||||
@ -465,11 +476,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
// Restore the global alpha to the fill alpha
|
||||
ctx.globalAlpha = this.current.fillAlpha;
|
||||
},
|
||||
closeStroke: function canvasGraphicsCloseStroke() {
|
||||
closeStroke: function CanvasGraphics_closeStroke() {
|
||||
this.closePath();
|
||||
this.stroke();
|
||||
},
|
||||
fill: function canvasGraphicsFill(consumePath) {
|
||||
fill: function CanvasGraphics_fill(consumePath) {
|
||||
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
||||
var ctx = this.ctx;
|
||||
var fillColor = this.current.fillColor;
|
||||
@ -486,76 +497,99 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
if (consumePath)
|
||||
this.consumePath();
|
||||
},
|
||||
eoFill: function canvasGraphicsEoFill() {
|
||||
eoFill: function CanvasGraphics_eoFill() {
|
||||
var savedFillRule = this.setEOFillRule();
|
||||
this.fill();
|
||||
this.restoreFillRule(savedFillRule);
|
||||
},
|
||||
fillStroke: function canvasGraphicsFillStroke() {
|
||||
fillStroke: function CanvasGraphics_fillStroke() {
|
||||
this.fill(false);
|
||||
this.stroke(false);
|
||||
|
||||
this.consumePath();
|
||||
},
|
||||
eoFillStroke: function canvasGraphicsEoFillStroke() {
|
||||
eoFillStroke: function CanvasGraphics_eoFillStroke() {
|
||||
var savedFillRule = this.setEOFillRule();
|
||||
this.fillStroke();
|
||||
this.restoreFillRule(savedFillRule);
|
||||
},
|
||||
closeFillStroke: function canvasGraphicsCloseFillStroke() {
|
||||
closeFillStroke: function CanvasGraphics_closeFillStroke() {
|
||||
this.closePath();
|
||||
this.fillStroke();
|
||||
},
|
||||
closeEOFillStroke: function canvasGraphicsCloseEOFillStroke() {
|
||||
closeEOFillStroke: function CanvasGraphics_closeEOFillStroke() {
|
||||
var savedFillRule = this.setEOFillRule();
|
||||
this.closePath();
|
||||
this.fillStroke();
|
||||
this.restoreFillRule(savedFillRule);
|
||||
},
|
||||
endPath: function canvasGraphicsEndPath() {
|
||||
endPath: function CanvasGraphics_endPath() {
|
||||
this.consumePath();
|
||||
},
|
||||
|
||||
// Clipping
|
||||
clip: function canvasGraphicsClip() {
|
||||
clip: function CanvasGraphics_clip() {
|
||||
this.pendingClip = NORMAL_CLIP;
|
||||
},
|
||||
eoClip: function canvasGraphicsEoClip() {
|
||||
eoClip: function CanvasGraphics_eoClip() {
|
||||
this.pendingClip = EO_CLIP;
|
||||
},
|
||||
|
||||
// Text
|
||||
beginText: function canvasGraphicsBeginText() {
|
||||
beginText: function CanvasGraphics_beginText() {
|
||||
this.current.textMatrix = IDENTITY_MATRIX;
|
||||
this.current.x = this.current.lineX = 0;
|
||||
this.current.y = this.current.lineY = 0;
|
||||
},
|
||||
endText: function canvasGraphicsEndText() {
|
||||
endText: function CanvasGraphics_endText() {
|
||||
},
|
||||
setCharSpacing: function canvasGraphicsSetCharSpacing(spacing) {
|
||||
setCharSpacing: function CanvasGraphics_setCharSpacing(spacing) {
|
||||
this.current.charSpacing = spacing;
|
||||
},
|
||||
setWordSpacing: function canvasGraphicsSetWordSpacing(spacing) {
|
||||
setWordSpacing: function CanvasGraphics_setWordSpacing(spacing) {
|
||||
this.current.wordSpacing = spacing;
|
||||
},
|
||||
setHScale: function canvasGraphicsSetHScale(scale) {
|
||||
setHScale: function CanvasGraphics_setHScale(scale) {
|
||||
this.current.textHScale = scale / 100;
|
||||
},
|
||||
setLeading: function canvasGraphicsSetLeading(leading) {
|
||||
setLeading: function CanvasGraphics_setLeading(leading) {
|
||||
this.current.leading = -leading;
|
||||
},
|
||||
setFont: function canvasGraphicsSetFont(fontRefName, size) {
|
||||
var fontObj = this.objs.get(fontRefName).fontObj;
|
||||
setFont: function CanvasGraphics_setFont(fontRefName, size) {
|
||||
var fontObj = this.objs.get(fontRefName);
|
||||
var current = this.current;
|
||||
|
||||
if (!fontObj) {
|
||||
throw 'Can\'t find font for ' + fontRefName;
|
||||
if (!fontObj)
|
||||
error('Can\'t find font for ' + fontRefName);
|
||||
|
||||
// Slice-clone matrix so we can manipulate it without affecting original
|
||||
if (fontObj.fontMatrix)
|
||||
current.fontMatrix = fontObj.fontMatrix.slice(0);
|
||||
else
|
||||
current.fontMatrix = IDENTITY_MATRIX.slice(0);
|
||||
|
||||
// A valid matrix needs all main diagonal elements to be non-zero
|
||||
// This also ensures we bypass FF bugzilla bug #719844.
|
||||
if (current.fontMatrix[0] === 0 ||
|
||||
current.fontMatrix[3] === 0) {
|
||||
warn('Invalid font matrix for font ' + fontRefName);
|
||||
}
|
||||
|
||||
var name = fontObj.loadedName || 'sans-serif';
|
||||
// The spec for Tf (setFont) says that 'size' specifies the font 'scale',
|
||||
// and in some docs this can be negative (inverted x-y axes).
|
||||
// We implement this condition with fontMatrix.
|
||||
if (size < 0) {
|
||||
size = -size;
|
||||
current.fontMatrix[0] *= -1;
|
||||
current.fontMatrix[3] *= -1;
|
||||
}
|
||||
|
||||
this.current.font = fontObj;
|
||||
this.current.fontSize = size;
|
||||
|
||||
if (fontObj.coded)
|
||||
return; // we don't need ctx.font for Type3 fonts
|
||||
|
||||
var name = fontObj.loadedName || 'sans-serif';
|
||||
var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
|
||||
(fontObj.bold ? 'bold' : 'normal');
|
||||
@ -563,39 +597,48 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
var italic = fontObj.italic ? 'italic' : 'normal';
|
||||
var serif = fontObj.isSerifFont ? 'serif' : 'sans-serif';
|
||||
var typeface = '"' + name + '", ' + serif;
|
||||
var rule = italic + ' ' + bold + ' ' + size + 'px ' + typeface;
|
||||
|
||||
// Some font backends cannot handle fonts below certain size.
|
||||
// Keeping the font at minimal size and using the fontSizeScale to change
|
||||
// the current transformation matrix before the fillText/strokeText.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
|
||||
var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE;
|
||||
this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 :
|
||||
size / MIN_FONT_SIZE;
|
||||
|
||||
var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
|
||||
this.ctx.font = rule;
|
||||
},
|
||||
setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) {
|
||||
setTextRenderingMode: function CanvasGraphics_setTextRenderingMode(mode) {
|
||||
if (mode >= TextRenderingMode.FILL_ADD_TO_PATH)
|
||||
TODO('unsupported text rendering mode: ' + mode);
|
||||
this.current.textRenderingMode = mode;
|
||||
},
|
||||
setTextRise: function canvasGraphicsSetTextRise(rise) {
|
||||
setTextRise: function CanvasGraphics_setTextRise(rise) {
|
||||
TODO('text rise: ' + rise);
|
||||
},
|
||||
moveText: function canvasGraphicsMoveText(x, y) {
|
||||
moveText: function CanvasGraphics_moveText(x, y) {
|
||||
this.current.x = this.current.lineX += x;
|
||||
this.current.y = this.current.lineY += y;
|
||||
},
|
||||
setLeadingMoveText: function canvasGraphicsSetLeadingMoveText(x, y) {
|
||||
setLeadingMoveText: function CanvasGraphics_setLeadingMoveText(x, y) {
|
||||
this.setLeading(-y);
|
||||
this.moveText(x, y);
|
||||
},
|
||||
setTextMatrix: function canvasGraphicsSetTextMatrix(a, b, c, d, e, f) {
|
||||
setTextMatrix: function CanvasGraphics_setTextMatrix(a, b, c, d, e, f) {
|
||||
this.current.textMatrix = [a, b, c, d, e, f];
|
||||
|
||||
this.current.x = this.current.lineX = 0;
|
||||
this.current.y = this.current.lineY = 0;
|
||||
},
|
||||
nextLine: function canvasGraphicsNextLine() {
|
||||
nextLine: function CanvasGraphics_nextLine() {
|
||||
this.moveText(0, this.current.leading);
|
||||
},
|
||||
applyTextTransforms: function canvasApplyTransforms() {
|
||||
applyTextTransforms: function CanvasGraphics_applyTextTransforms() {
|
||||
var ctx = this.ctx;
|
||||
var current = this.current;
|
||||
var textHScale = current.textHScale;
|
||||
var fontMatrix = current.font.fontMatrix || IDENTITY_MATRIX;
|
||||
var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
|
||||
|
||||
ctx.transform.apply(ctx, current.textMatrix);
|
||||
ctx.scale(1, -1);
|
||||
@ -603,7 +646,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
ctx.transform.apply(ctx, fontMatrix);
|
||||
ctx.scale(textHScale, 1);
|
||||
},
|
||||
getTextGeometry: function canvasGetTextGeometry() {
|
||||
getTextGeometry: function CanvasGraphics_getTextGeometry() {
|
||||
var geometry = {};
|
||||
var ctx = this.ctx;
|
||||
var font = this.current.font;
|
||||
@ -620,16 +663,17 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
return geometry;
|
||||
},
|
||||
|
||||
showText: function canvasGraphicsShowText(str, skipTextSelection) {
|
||||
showText: function CanvasGraphics_showText(str, skipTextSelection) {
|
||||
var ctx = this.ctx;
|
||||
var current = this.current;
|
||||
var font = current.font;
|
||||
var glyphs = font.charsToGlyphs(str);
|
||||
var fontSize = current.fontSize;
|
||||
var fontSizeScale = current.fontSizeScale;
|
||||
var charSpacing = current.charSpacing;
|
||||
var wordSpacing = current.wordSpacing;
|
||||
var textHScale = current.textHScale;
|
||||
var fontMatrix = font.fontMatrix || IDENTITY_MATRIX;
|
||||
var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
|
||||
var textHScale2 = textHScale * fontMatrix[0];
|
||||
var glyphsLength = glyphs.length;
|
||||
var textLayer = this.textLayer;
|
||||
@ -663,11 +707,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.save();
|
||||
ctx.scale(fontSize, fontSize);
|
||||
ctx.transform.apply(ctx, fontMatrix);
|
||||
this.executeIRQueue(glyph.codeIRQueue);
|
||||
this.executeOperatorList(glyph.operatorList);
|
||||
this.restore();
|
||||
|
||||
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
|
||||
var width = transformed[0] * fontSize + charSpacing;
|
||||
var width = transformed[0] * fontSize +
|
||||
Util.sign(current.fontMatrix[0]) * charSpacing;
|
||||
|
||||
ctx.translate(width, 0);
|
||||
current.x += width * textHScale;
|
||||
@ -688,49 +733,66 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
else
|
||||
lineWidth /= scale;
|
||||
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
if (textSelection)
|
||||
text.geom = this.getTextGeometry();
|
||||
|
||||
var width = 0;
|
||||
if (fontSizeScale != 1.0) {
|
||||
ctx.scale(fontSizeScale, fontSizeScale);
|
||||
lineWidth /= fontSizeScale;
|
||||
}
|
||||
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
var x = 0;
|
||||
for (var i = 0; i < glyphsLength; ++i) {
|
||||
var glyph = glyphs[i];
|
||||
if (glyph === null) {
|
||||
// word break
|
||||
width += wordSpacing;
|
||||
x += Util.sign(current.fontMatrix[0]) * wordSpacing;
|
||||
continue;
|
||||
}
|
||||
|
||||
var char = glyph.fontChar;
|
||||
var charWidth = glyph.width * fontSize * 0.001 + charSpacing;
|
||||
var character = glyph.fontChar;
|
||||
var charWidth = glyph.width * fontSize * 0.001 +
|
||||
Util.sign(current.fontMatrix[0]) * charSpacing;
|
||||
|
||||
switch (textRenderingMode) {
|
||||
default: // other unsupported rendering modes
|
||||
case TextRenderingMode.FILL:
|
||||
case TextRenderingMode.FILL_ADD_TO_PATH:
|
||||
ctx.fillText(char, width, 0);
|
||||
break;
|
||||
case TextRenderingMode.STROKE:
|
||||
case TextRenderingMode.STROKE_ADD_TO_PATH:
|
||||
ctx.strokeText(char, width, 0);
|
||||
break;
|
||||
case TextRenderingMode.FILL_STROKE:
|
||||
case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
|
||||
ctx.fillText(char, width, 0);
|
||||
ctx.strokeText(char, width, 0);
|
||||
break;
|
||||
case TextRenderingMode.INVISIBLE:
|
||||
break;
|
||||
if (!glyph.disabled) {
|
||||
var scaledX = x / fontSizeScale;
|
||||
switch (textRenderingMode) {
|
||||
default: // other unsupported rendering modes
|
||||
case TextRenderingMode.FILL:
|
||||
case TextRenderingMode.FILL_ADD_TO_PATH:
|
||||
ctx.fillText(character, scaledX, 0);
|
||||
break;
|
||||
case TextRenderingMode.STROKE:
|
||||
case TextRenderingMode.STROKE_ADD_TO_PATH:
|
||||
ctx.strokeText(character, scaledX, 0);
|
||||
break;
|
||||
case TextRenderingMode.FILL_STROKE:
|
||||
case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
|
||||
ctx.fillText(character, scaledX, 0);
|
||||
ctx.strokeText(character, scaledX, 0);
|
||||
break;
|
||||
case TextRenderingMode.INVISIBLE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
width += charWidth;
|
||||
x += charWidth;
|
||||
|
||||
text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
|
||||
text.length++;
|
||||
var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
|
||||
var glyphUnicodeLength = glyphUnicode.length;
|
||||
//reverse an arabic ligature
|
||||
if (glyphUnicodeLength > 1 &&
|
||||
isRTLRangeFor(glyphUnicode.charCodeAt(0))) {
|
||||
for (var ii = glyphUnicodeLength - 1; ii >= 0; ii--)
|
||||
text.str += glyphUnicode[ii];
|
||||
} else
|
||||
text.str += glyphUnicode;
|
||||
text.length += glyphUnicodeLength;
|
||||
text.canvasWidth += charWidth;
|
||||
}
|
||||
current.x += width * textHScale2;
|
||||
current.x += x * textHScale2;
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
@ -739,14 +801,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
|
||||
return text;
|
||||
},
|
||||
showSpacedText: function canvasGraphicsShowSpacedText(arr) {
|
||||
showSpacedText: function CanvasGraphics_showSpacedText(arr) {
|
||||
var ctx = this.ctx;
|
||||
var current = this.current;
|
||||
var font = current.font;
|
||||
var fontSize = current.fontSize;
|
||||
var textHScale = current.textHScale;
|
||||
if (!font.coded)
|
||||
textHScale *= (font.fontMatrix || IDENTITY_MATRIX)[0];
|
||||
textHScale *= (current.fontMatrix || IDENTITY_MATRIX)[0];
|
||||
var arrLength = arr.length;
|
||||
var textLayer = this.textLayer;
|
||||
var text = {str: '', length: 0, canvasWidth: 0, geom: {}};
|
||||
@ -793,7 +855,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
text.str += shownText.str;
|
||||
}
|
||||
text.canvasWidth += shownText.canvasWidth;
|
||||
text.length += e.length;
|
||||
text.length += shownText.length;
|
||||
}
|
||||
} else {
|
||||
malformed('TJ array element ' + e + ' is not string or num');
|
||||
@ -803,25 +865,25 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
if (textSelection)
|
||||
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||
},
|
||||
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
||||
nextLineShowText: function CanvasGraphics_nextLineShowText(text) {
|
||||
this.nextLine();
|
||||
this.showText(text);
|
||||
},
|
||||
nextLineSetSpacingShowText:
|
||||
function canvasGraphicsNextLineSetSpacingShowText(wordSpacing,
|
||||
charSpacing,
|
||||
text) {
|
||||
function CanvasGraphics_nextLineSetSpacingShowText(wordSpacing,
|
||||
charSpacing,
|
||||
text) {
|
||||
this.setWordSpacing(wordSpacing);
|
||||
this.setCharSpacing(charSpacing);
|
||||
this.nextLineShowText(text);
|
||||
},
|
||||
|
||||
// Type3 fonts
|
||||
setCharWidth: function canvasGraphicsSetCharWidth(xWidth, yWidth) {
|
||||
setCharWidth: function CanvasGraphics_setCharWidth(xWidth, yWidth) {
|
||||
// We can safely ignore this since the width should be the same
|
||||
// as the width in the Widths array.
|
||||
},
|
||||
setCharWidthAndBounds: function canvasGraphicsSetCharWidthAndBounds(xWidth,
|
||||
setCharWidthAndBounds: function CanvasGraphics_setCharWidthAndBounds(xWidth,
|
||||
yWidth,
|
||||
llx,
|
||||
lly,
|
||||
@ -835,20 +897,20 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
},
|
||||
|
||||
// Color
|
||||
setStrokeColorSpace: function canvasGraphicsSetStrokeColorSpace(raw) {
|
||||
setStrokeColorSpace: function CanvasGraphics_setStrokeColorSpace(raw) {
|
||||
this.current.strokeColorSpace = ColorSpace.fromIR(raw);
|
||||
},
|
||||
setFillColorSpace: function canvasGraphicsSetFillColorSpace(raw) {
|
||||
setFillColorSpace: function CanvasGraphics_setFillColorSpace(raw) {
|
||||
this.current.fillColorSpace = ColorSpace.fromIR(raw);
|
||||
},
|
||||
setStrokeColor: function canvasGraphicsSetStrokeColor(/*...*/) {
|
||||
setStrokeColor: function CanvasGraphics_setStrokeColor(/*...*/) {
|
||||
var cs = this.current.strokeColorSpace;
|
||||
var rgbColor = cs.getRgb(arguments);
|
||||
var color = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
|
||||
this.ctx.strokeStyle = color;
|
||||
this.current.strokeColor = color;
|
||||
},
|
||||
getColorN_IR_Pattern: function canvasGraphicsGetColorN_IR_Pattern(IR, cs) {
|
||||
getColorN_Pattern: function CanvasGraphics_getColorN_Pattern(IR, cs) {
|
||||
if (IR[0] == 'TilingPattern') {
|
||||
var args = IR[1];
|
||||
var base = cs.base;
|
||||
@ -864,38 +926,38 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
var pattern = new TilingPattern(IR, color, this.ctx, this.objs);
|
||||
} else if (IR[0] == 'RadialAxial' || IR[0] == 'Dummy') {
|
||||
var pattern = Pattern.shadingFromIR(this.ctx, IR);
|
||||
var pattern = Pattern.shadingFromIR(IR);
|
||||
} else {
|
||||
throw 'Unkown IR type';
|
||||
error('Unkown IR type ' + IR[0]);
|
||||
}
|
||||
return pattern;
|
||||
},
|
||||
setStrokeColorN_IR: function canvasGraphicsSetStrokeColorN(/*...*/) {
|
||||
setStrokeColorN: function CanvasGraphics_setStrokeColorN(/*...*/) {
|
||||
var cs = this.current.strokeColorSpace;
|
||||
|
||||
if (cs.name == 'Pattern') {
|
||||
this.current.strokeColor = this.getColorN_IR_Pattern(arguments, cs);
|
||||
this.current.strokeColor = this.getColorN_Pattern(arguments, cs);
|
||||
} else {
|
||||
this.setStrokeColor.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
setFillColor: function canvasGraphicsSetFillColor(/*...*/) {
|
||||
setFillColor: function CanvasGraphics_setFillColor(/*...*/) {
|
||||
var cs = this.current.fillColorSpace;
|
||||
var rgbColor = cs.getRgb(arguments);
|
||||
var color = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
|
||||
this.ctx.fillStyle = color;
|
||||
this.current.fillColor = color;
|
||||
},
|
||||
setFillColorN_IR: function canvasGraphicsSetFillColorN(/*...*/) {
|
||||
setFillColorN: function CanvasGraphics_setFillColorN(/*...*/) {
|
||||
var cs = this.current.fillColorSpace;
|
||||
|
||||
if (cs.name == 'Pattern') {
|
||||
this.current.fillColor = this.getColorN_IR_Pattern(arguments, cs);
|
||||
this.current.fillColor = this.getColorN_Pattern(arguments, cs);
|
||||
} else {
|
||||
this.setFillColor.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
setStrokeGray: function canvasGraphicsSetStrokeGray(gray) {
|
||||
setStrokeGray: function CanvasGraphics_setStrokeGray(gray) {
|
||||
if (!(this.current.strokeColorSpace instanceof DeviceGrayCS))
|
||||
this.current.strokeColorSpace = new DeviceGrayCS();
|
||||
|
||||
@ -903,7 +965,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.ctx.strokeStyle = color;
|
||||
this.current.strokeColor = color;
|
||||
},
|
||||
setFillGray: function canvasGraphicsSetFillGray(gray) {
|
||||
setFillGray: function CanvasGraphics_setFillGray(gray) {
|
||||
if (!(this.current.fillColorSpace instanceof DeviceGrayCS))
|
||||
this.current.fillColorSpace = new DeviceGrayCS();
|
||||
|
||||
@ -911,7 +973,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.ctx.fillStyle = color;
|
||||
this.current.fillColor = color;
|
||||
},
|
||||
setStrokeRGBColor: function canvasGraphicsSetStrokeRGBColor(r, g, b) {
|
||||
setStrokeRGBColor: function CanvasGraphics_setStrokeRGBColor(r, g, b) {
|
||||
if (!(this.current.strokeColorSpace instanceof DeviceRgbCS))
|
||||
this.current.strokeColorSpace = new DeviceRgbCS();
|
||||
|
||||
@ -919,7 +981,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.ctx.strokeStyle = color;
|
||||
this.current.strokeColor = color;
|
||||
},
|
||||
setFillRGBColor: function canvasGraphicsSetFillRGBColor(r, g, b) {
|
||||
setFillRGBColor: function CanvasGraphics_setFillRGBColor(r, g, b) {
|
||||
if (!(this.current.fillColorSpace instanceof DeviceRgbCS))
|
||||
this.current.fillColorSpace = new DeviceRgbCS();
|
||||
|
||||
@ -927,7 +989,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.ctx.fillStyle = color;
|
||||
this.current.fillColor = color;
|
||||
},
|
||||
setStrokeCMYKColor: function canvasGraphicsSetStrokeCMYKColor(c, m, y, k) {
|
||||
setStrokeCMYKColor: function CanvasGraphics_setStrokeCMYKColor(c, m, y, k) {
|
||||
if (!(this.current.strokeColorSpace instanceof DeviceCmykCS))
|
||||
this.current.strokeColorSpace = new DeviceCmykCS();
|
||||
|
||||
@ -935,7 +997,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.ctx.strokeStyle = color;
|
||||
this.current.strokeColor = color;
|
||||
},
|
||||
setFillCMYKColor: function canvasGraphicsSetFillCMYKColor(c, m, y, k) {
|
||||
setFillCMYKColor: function CanvasGraphics_setFillCMYKColor(c, m, y, k) {
|
||||
if (!(this.current.fillColorSpace instanceof DeviceCmykCS))
|
||||
this.current.fillColorSpace = new DeviceCmykCS();
|
||||
|
||||
@ -944,11 +1006,12 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.current.fillColor = color;
|
||||
},
|
||||
|
||||
shadingFill: function canvasGraphicsShadingFill(patternIR) {
|
||||
shadingFill: function CanvasGraphics_shadingFill(patternIR) {
|
||||
var ctx = this.ctx;
|
||||
|
||||
this.save();
|
||||
ctx.fillStyle = Pattern.shadingFromIR(ctx, patternIR);
|
||||
var pattern = Pattern.shadingFromIR(patternIR);
|
||||
ctx.fillStyle = pattern.getPattern(ctx);
|
||||
|
||||
var inv = ctx.mozCurrentTransformInverse;
|
||||
if (inv) {
|
||||
@ -981,14 +1044,14 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
},
|
||||
|
||||
// Images
|
||||
beginInlineImage: function canvasGraphicsBeginInlineImage() {
|
||||
beginInlineImage: function CanvasGraphics_beginInlineImage() {
|
||||
error('Should not call beginInlineImage');
|
||||
},
|
||||
beginImageData: function canvasGraphicsBeginImageData() {
|
||||
beginImageData: function CanvasGraphics_beginImageData() {
|
||||
error('Should not call beginImageData');
|
||||
},
|
||||
|
||||
paintFormXObjectBegin: function canvasGraphicsPaintFormXObjectBegin(matrix,
|
||||
paintFormXObjectBegin: function CanvasGraphics_paintFormXObjectBegin(matrix,
|
||||
bbox) {
|
||||
this.save();
|
||||
|
||||
@ -1004,11 +1067,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
paintFormXObjectEnd: function canvasGraphicsPaintFormXObjectEnd() {
|
||||
paintFormXObjectEnd: function CanvasGraphics_paintFormXObjectEnd() {
|
||||
this.restore();
|
||||
},
|
||||
|
||||
paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) {
|
||||
paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
|
||||
var domImage = this.objs.get(objId);
|
||||
if (!domImage) {
|
||||
error('Dependent image isn\'t ready yet');
|
||||
@ -1026,7 +1089,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.restore();
|
||||
},
|
||||
|
||||
paintImageMaskXObject: function canvasGraphicsPaintImageMaskXObject(
|
||||
paintImageMaskXObject: function CanvasGraphics_paintImageMaskXObject(
|
||||
imgArray, inverseDecode, width, height) {
|
||||
function applyStencilMask(buffer, inverseDecode) {
|
||||
var imgArrayPos = 0;
|
||||
@ -1056,7 +1119,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
// scale the image to the unit square
|
||||
ctx.scale(1 / w, -1 / h);
|
||||
|
||||
var tmpCanvas = new this.ScratchCanvas(w, h);
|
||||
var tmpCanvas = createScratchCanvas(w, h);
|
||||
var tmpCtx = tmpCanvas.getContext('2d');
|
||||
|
||||
var fillColor = this.current.fillColor;
|
||||
@ -1075,7 +1138,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.restore();
|
||||
},
|
||||
|
||||
paintImageXObject: function canvasGraphicsPaintImageXObject(objId) {
|
||||
paintImageXObject: function CanvasGraphics_paintImageXObject(objId) {
|
||||
var imgData = this.objs.get(objId);
|
||||
if (!imgData)
|
||||
error('Dependent image isn\'t ready yet');
|
||||
@ -1087,7 +1150,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
// scale the image to the unit square
|
||||
ctx.scale(1 / w, -1 / h);
|
||||
|
||||
var tmpCanvas = new this.ScratchCanvas(w, h);
|
||||
var tmpCanvas = createScratchCanvas(w, h);
|
||||
var tmpCtx = tmpCanvas.getContext('2d');
|
||||
this.putBinaryImageData(tmpCtx, imgData, w, h);
|
||||
|
||||
@ -1095,41 +1158,41 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
this.restore();
|
||||
},
|
||||
|
||||
putBinaryImageData: function canvasPutBinaryImageData() {
|
||||
putBinaryImageData: function CanvasGraphics_putBinaryImageData() {
|
||||
//
|
||||
},
|
||||
|
||||
// Marked content
|
||||
|
||||
markPoint: function canvasGraphicsMarkPoint(tag) {
|
||||
markPoint: function CanvasGraphics_markPoint(tag) {
|
||||
TODO('Marked content');
|
||||
},
|
||||
markPointProps: function canvasGraphicsMarkPointProps(tag, properties) {
|
||||
markPointProps: function CanvasGraphics_markPointProps(tag, properties) {
|
||||
TODO('Marked content');
|
||||
},
|
||||
beginMarkedContent: function canvasGraphicsBeginMarkedContent(tag) {
|
||||
beginMarkedContent: function CanvasGraphics_beginMarkedContent(tag) {
|
||||
TODO('Marked content');
|
||||
},
|
||||
beginMarkedContentProps:
|
||||
function canvasGraphicsBeginMarkedContentProps(tag, properties) {
|
||||
beginMarkedContentProps: function CanvasGraphics_beginMarkedContentProps(
|
||||
tag, properties) {
|
||||
TODO('Marked content');
|
||||
},
|
||||
endMarkedContent: function canvasGraphicsEndMarkedContent() {
|
||||
endMarkedContent: function CanvasGraphics_endMarkedContent() {
|
||||
TODO('Marked content');
|
||||
},
|
||||
|
||||
// Compatibility
|
||||
|
||||
beginCompat: function canvasGraphicsBeginCompat() {
|
||||
beginCompat: function CanvasGraphics_beginCompat() {
|
||||
TODO('ignore undefined operators (should we do that anyway?)');
|
||||
},
|
||||
endCompat: function canvasGraphicsEndCompat() {
|
||||
endCompat: function CanvasGraphics_endCompat() {
|
||||
TODO('stop ignoring undefined operators');
|
||||
},
|
||||
|
||||
// Helper functions
|
||||
|
||||
consumePath: function canvasGraphicsConsumePath() {
|
||||
consumePath: function CanvasGraphics_consumePath() {
|
||||
if (this.pendingClip) {
|
||||
var savedFillRule = null;
|
||||
if (this.pendingClip == EO_CLIP)
|
||||
@ -1146,15 +1209,15 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
// We generally keep the canvas context set for
|
||||
// nonzero-winding, and just set evenodd for the operations
|
||||
// that need them.
|
||||
setEOFillRule: function canvasGraphicsSetEOFillRule() {
|
||||
setEOFillRule: function CanvasGraphics_setEOFillRule() {
|
||||
var savedFillRule = this.ctx.mozFillRule;
|
||||
this.ctx.mozFillRule = 'evenodd';
|
||||
return savedFillRule;
|
||||
},
|
||||
restoreFillRule: function canvasGraphicsRestoreFillRule(rule) {
|
||||
restoreFillRule: function CanvasGraphics_restoreFillRule(rule) {
|
||||
this.ctx.mozFillRule = rule;
|
||||
},
|
||||
getSinglePixelWidth: function getSinglePixelWidth(scale) {
|
||||
getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) {
|
||||
var inverse = this.ctx.mozCurrentTransformInverse;
|
||||
return Math.abs(inverse[0] + inverse[2]);
|
||||
}
|
||||
|
@ -12,17 +12,17 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
ColorSpace.prototype = {
|
||||
// Input: array of size numComps representing color component values
|
||||
// Output: array of rgb values, each value ranging from [0.1]
|
||||
getRgb: function colorSpaceGetRgb(color) {
|
||||
getRgb: function ColorSpace_getRgb(color) {
|
||||
error('Should not call ColorSpace.getRgb: ' + color);
|
||||
},
|
||||
// Input: Uint8Array of component values, each value scaled to [0,255]
|
||||
// Output: Uint8Array of rgb values, each value scaled to [0,255]
|
||||
getRgbBuffer: function colorSpaceGetRgbBuffer(input) {
|
||||
getRgbBuffer: function ColorSpace_getRgbBuffer(input) {
|
||||
error('Should not call ColorSpace.getRgbBuffer: ' + input);
|
||||
}
|
||||
};
|
||||
|
||||
ColorSpace.parse = function colorSpaceParse(cs, xref, res) {
|
||||
ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
|
||||
var IR = ColorSpace.parseToIR(cs, xref, res);
|
||||
if (IR instanceof AlternateCS)
|
||||
return IR;
|
||||
@ -30,7 +30,7 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
return ColorSpace.fromIR(IR);
|
||||
};
|
||||
|
||||
ColorSpace.fromIR = function colorSpaceFromIR(IR) {
|
||||
ColorSpace.fromIR = function ColorSpace_fromIR(IR) {
|
||||
var name = isArray(IR) ? IR[0] : IR;
|
||||
|
||||
switch (name) {
|
||||
@ -57,15 +57,20 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
|
||||
return new AlternateCS(numComps, ColorSpace.fromIR(alt),
|
||||
PDFFunction.fromIR(tintFnIR));
|
||||
case 'LabCS':
|
||||
var whitePoint = IR[1].WhitePoint;
|
||||
var blackPoint = IR[1].BlackPoint;
|
||||
var range = IR[1].Range;
|
||||
return new LabCS(whitePoint, blackPoint, range);
|
||||
default:
|
||||
error('Unkown name ' + name);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
ColorSpace.parseToIR = function colorSpaceParseToIR(cs, xref, res) {
|
||||
ColorSpace.parseToIR = function ColorSpace_parseToIR(cs, xref, res) {
|
||||
if (isName(cs)) {
|
||||
var colorSpaces = xref.fetchIfRef(res.get('ColorSpace'));
|
||||
var colorSpaces = res.get('ColorSpace');
|
||||
if (isDict(colorSpaces)) {
|
||||
var refcs = colorSpaces.get(cs.name);
|
||||
if (refcs)
|
||||
@ -130,6 +135,7 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
|
||||
return ['PatternCS', basePatternCS];
|
||||
case 'Indexed':
|
||||
case 'I':
|
||||
var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
|
||||
var hiVal = cs[2] + 1;
|
||||
var lookup = xref.fetchIfRef(cs[3]);
|
||||
@ -146,6 +152,8 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
|
||||
return ['AlternateCS', numComps, alt, tintFnIR];
|
||||
case 'Lab':
|
||||
var params = cs[1].getAll();
|
||||
return ['LabCS', params];
|
||||
default:
|
||||
error('unimplemented color space object "' + mode + '"');
|
||||
}
|
||||
@ -163,7 +171,7 @@ var ColorSpace = (function ColorSpaceClosure() {
|
||||
* @param {Array} decode Decode map (usually from an image).
|
||||
* @param {Number} n Number of components the color space has.
|
||||
*/
|
||||
ColorSpace.isDefaultDecode = function colorSpaceIsDefaultDecode(decode, n) {
|
||||
ColorSpace.isDefaultDecode = function ColorSpace_isDefaultDecode(decode, n) {
|
||||
if (!decode)
|
||||
return true;
|
||||
|
||||
@ -199,11 +207,11 @@ var AlternateCS = (function AlternateCSClosure() {
|
||||
}
|
||||
|
||||
AlternateCS.prototype = {
|
||||
getRgb: function altcs_getRgb(color) {
|
||||
getRgb: function AlternateCS_getRgb(color) {
|
||||
var tinted = this.tintFn(color);
|
||||
return this.base.getRgb(tinted);
|
||||
},
|
||||
getRgbBuffer: function altcs_getRgbBuffer(input, bits) {
|
||||
getRgbBuffer: function AlternateCS_getRgbBuffer(input, bits) {
|
||||
var tintFn = this.tintFn;
|
||||
var base = this.base;
|
||||
var scale = 1 / ((1 << bits) - 1);
|
||||
@ -212,7 +220,7 @@ var AlternateCS = (function AlternateCSClosure() {
|
||||
var baseNumComps = base.numComps;
|
||||
var baseBuf = new Uint8Array(baseNumComps * length);
|
||||
var numComps = this.numComps;
|
||||
var scaled = new Array(numComps);
|
||||
var scaled = [];
|
||||
|
||||
for (var i = 0; i < length; i += numComps) {
|
||||
for (var z = 0; z < numComps; ++z)
|
||||
@ -224,7 +232,7 @@ var AlternateCS = (function AlternateCSClosure() {
|
||||
}
|
||||
return base.getRgbBuffer(baseBuf, 8);
|
||||
},
|
||||
isDefaultDecode: function altcs_isDefaultDecode(decodeMap) {
|
||||
isDefaultDecode: function AlternateCS_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
@ -267,7 +275,7 @@ var IndexedCS = (function IndexedCSClosure() {
|
||||
}
|
||||
|
||||
IndexedCS.prototype = {
|
||||
getRgb: function indexcs_getRgb(color) {
|
||||
getRgb: function IndexedCS_getRgb(color) {
|
||||
var numComps = this.base.numComps;
|
||||
var start = color[0] * numComps;
|
||||
var c = [];
|
||||
@ -277,7 +285,7 @@ var IndexedCS = (function IndexedCSClosure() {
|
||||
|
||||
return this.base.getRgb(c);
|
||||
},
|
||||
getRgbBuffer: function indexcs_getRgbBuffer(input) {
|
||||
getRgbBuffer: function IndexedCS_getRgbBuffer(input) {
|
||||
var base = this.base;
|
||||
var numComps = base.numComps;
|
||||
var lookup = this.lookup;
|
||||
@ -294,7 +302,7 @@ var IndexedCS = (function IndexedCSClosure() {
|
||||
|
||||
return base.getRgbBuffer(baseBuf, 8);
|
||||
},
|
||||
isDefaultDecode: function indexcs_isDefaultDecode(decodeMap) {
|
||||
isDefaultDecode: function IndexedCS_isDefaultDecode(decodeMap) {
|
||||
// indexed color maps shouldn't be changed
|
||||
return true;
|
||||
}
|
||||
@ -310,11 +318,11 @@ var DeviceGrayCS = (function DeviceGrayCSClosure() {
|
||||
}
|
||||
|
||||
DeviceGrayCS.prototype = {
|
||||
getRgb: function graycs_getRgb(color) {
|
||||
getRgb: function DeviceGrayCS_getRgb(color) {
|
||||
var c = color[0];
|
||||
return [c, c, c];
|
||||
},
|
||||
getRgbBuffer: function graycs_getRgbBuffer(input, bits) {
|
||||
getRgbBuffer: function DeviceGrayCS_getRgbBuffer(input, bits) {
|
||||
var scale = 255 / ((1 << bits) - 1);
|
||||
var length = input.length;
|
||||
var rgbBuf = new Uint8Array(length * 3);
|
||||
@ -326,7 +334,7 @@ var DeviceGrayCS = (function DeviceGrayCSClosure() {
|
||||
}
|
||||
return rgbBuf;
|
||||
},
|
||||
isDefaultDecode: function graycs_isDefaultDecode(decodeMap) {
|
||||
isDefaultDecode: function DeviceGrayCS_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
@ -340,10 +348,10 @@ var DeviceRgbCS = (function DeviceRgbCSClosure() {
|
||||
this.defaultColor = [0, 0, 0];
|
||||
}
|
||||
DeviceRgbCS.prototype = {
|
||||
getRgb: function rgbcs_getRgb(color) {
|
||||
getRgb: function DeviceRgbCS_getRgb(color) {
|
||||
return color;
|
||||
},
|
||||
getRgbBuffer: function rgbcs_getRgbBuffer(input, bits) {
|
||||
getRgbBuffer: function DeviceRgbCS_getRgbBuffer(input, bits) {
|
||||
if (bits == 8)
|
||||
return input;
|
||||
var scale = 255 / ((1 << bits) - 1);
|
||||
@ -353,7 +361,7 @@ var DeviceRgbCS = (function DeviceRgbCSClosure() {
|
||||
rgbBuf[i] = (scale * input[i]) | 0;
|
||||
return rgbBuf;
|
||||
},
|
||||
isDefaultDecode: function rgbcs_isDefaultDecode(decodeMap) {
|
||||
isDefaultDecode: function DeviceRgbCS_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
@ -367,61 +375,22 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() {
|
||||
this.defaultColor = [0, 0, 0, 1];
|
||||
}
|
||||
DeviceCmykCS.prototype = {
|
||||
getRgb: function cmykcs_getRgb(color) {
|
||||
getRgb: function DeviceCmykCS_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];
|
||||
},
|
||||
getRgbBuffer: function cmykcs_getRgbBuffer(colorBuf, bits) {
|
||||
getRgbBuffer: function DeviceCmykCS_getRgbBuffer(colorBuf, bits) {
|
||||
var scale = 1 / ((1 << bits) - 1);
|
||||
var length = colorBuf.length / 4;
|
||||
var rgbBuf = new Uint8Array(length * 3);
|
||||
@ -440,7 +409,7 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() {
|
||||
|
||||
return rgbBuf;
|
||||
},
|
||||
isDefaultDecode: function cmykcs_isDefaultDecode(decodeMap) {
|
||||
isDefaultDecode: function DeviceCmykCS_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
@ -448,3 +417,116 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() {
|
||||
return DeviceCmykCS;
|
||||
})();
|
||||
|
||||
//
|
||||
// LabCS: Based on "PDF Reference, Sixth Ed", p.250
|
||||
//
|
||||
var LabCS = (function LabCSClosure() {
|
||||
function LabCS(whitePoint, blackPoint, range) {
|
||||
this.name = 'Lab';
|
||||
this.numComps = 3;
|
||||
this.defaultColor = [0, 0, 0];
|
||||
|
||||
if (!whitePoint)
|
||||
error('WhitePoint missing - required for color space Lab');
|
||||
blackPoint = blackPoint || [0, 0, 0];
|
||||
range = range || [-100, 100, -100, 100];
|
||||
|
||||
// Translate args to spec variables
|
||||
this.XW = whitePoint[0];
|
||||
this.YW = whitePoint[1];
|
||||
this.ZW = whitePoint[2];
|
||||
this.amin = range[0];
|
||||
this.amax = range[1];
|
||||
this.bmin = range[2];
|
||||
this.bmax = range[3];
|
||||
|
||||
// These are here just for completeness - the spec doesn't offer any
|
||||
// formulas that use BlackPoint in Lab
|
||||
this.XB = blackPoint[0];
|
||||
this.YB = blackPoint[1];
|
||||
this.ZB = blackPoint[2];
|
||||
|
||||
// Validate vars as per spec
|
||||
if (this.XW < 0 || this.ZW < 0 || this.YW !== 1)
|
||||
error('Invalid WhitePoint components, no fallback available');
|
||||
|
||||
if (this.XB < 0 || this.YB < 0 || this.ZB < 0) {
|
||||
warn('Invalid BlackPoint, falling back to default');
|
||||
this.XB = this.YB = this.ZB = 0;
|
||||
}
|
||||
|
||||
if (this.amin > this.amax || this.bmin > this.bmax) {
|
||||
warn('Invalid Range, falling back to defaults');
|
||||
this.amin = -100;
|
||||
this.amax = 100;
|
||||
this.bmin = -100;
|
||||
this.bmax = 100;
|
||||
}
|
||||
};
|
||||
|
||||
// Function g(x) from spec
|
||||
function g(x) {
|
||||
if (x >= 6 / 29)
|
||||
return x * x * x;
|
||||
else
|
||||
return (108 / 841) * (x - 4 / 29);
|
||||
}
|
||||
|
||||
LabCS.prototype = {
|
||||
getRgb: function LabCS_getRgb(color) {
|
||||
// Ls,as,bs <---> L*,a*,b* in the spec
|
||||
var Ls = color[0], as = color[1], bs = color[2];
|
||||
|
||||
// Adjust limits of 'as' and 'bs'
|
||||
as = as > this.amax ? this.amax : as;
|
||||
as = as < this.amin ? this.amin : as;
|
||||
bs = bs > this.bmax ? this.bmax : bs;
|
||||
bs = bs < this.bmin ? this.bmin : bs;
|
||||
|
||||
// Computes intermediate variables X,Y,Z as per spec
|
||||
var M = (Ls + 16) / 116;
|
||||
var L = M + (as / 500);
|
||||
var N = M - (bs / 200);
|
||||
var X = this.XW * g(L);
|
||||
var Y = this.YW * g(M);
|
||||
var Z = this.ZW * g(N);
|
||||
|
||||
// XYZ to RGB 3x3 matrix, from:
|
||||
// http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html#RTFToC18
|
||||
var XYZtoRGB = [3.240479, -1.537150, -0.498535,
|
||||
-0.969256, 1.875992, 0.041556,
|
||||
0.055648, -0.204043, 1.057311];
|
||||
|
||||
return Util.apply3dTransform(XYZtoRGB, [X, Y, Z]);
|
||||
},
|
||||
getRgbBuffer: function LabCS_getRgbBuffer(input, bits) {
|
||||
if (bits == 8)
|
||||
return input;
|
||||
var scale = 255 / ((1 << bits) - 1);
|
||||
var i, length = input.length / 3;
|
||||
var rgbBuf = new Uint8Array(length);
|
||||
|
||||
var j = 0;
|
||||
for (i = 0; i < length; ++i) {
|
||||
// Convert L*, a*, s* into RGB
|
||||
var rgb = this.getRgb([input[i], input[i + 1], input[i + 2]]);
|
||||
rgbBuf[j++] = rgb[0];
|
||||
rgbBuf[j++] = rgb[1];
|
||||
rgbBuf[j++] = rgb[2];
|
||||
}
|
||||
|
||||
return rgbBuf;
|
||||
},
|
||||
isDefaultDecode: function LabCS_isDefaultDecode(decodeMap) {
|
||||
// From Table 90 in Adobe's:
|
||||
// "Document management - Portable document format", 1st ed, 2008
|
||||
if (decodeMap[0] === 0 && decodeMap[1] === 100 &&
|
||||
decodeMap[2] === this.amin && decodeMap[3] === this.amax &&
|
||||
decodeMap[4] === this.bmin && decodeMap[5] === this.bmax)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
return LabCS;
|
||||
})();
|
||||
|
318
src/core.js
@ -33,7 +33,9 @@ function getPdf(arg, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', params.url);
|
||||
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
|
||||
xhr.expected = (params.url.indexOf('file:') === 0) ? 0 : 200;
|
||||
var protocol = params.url.indexOf(':') < 0 ? window.location.protocol :
|
||||
params.url.substring(0, params.url.indexOf(':') + 1);
|
||||
xhr.expected = (protocol === 'http:' || protocol === 'https:') ? 200 : 0;
|
||||
|
||||
if ('progress' in params)
|
||||
xhr.onprogress = params.progress || undefined;
|
||||
@ -55,18 +57,14 @@ function getPdf(arg, callback) {
|
||||
xhr.send(null);
|
||||
}
|
||||
globalScope.PDFJS.getPdf = getPdf;
|
||||
globalScope.PDFJS.pdfBug = false;
|
||||
|
||||
var Page = (function PageClosure() {
|
||||
function Page(xref, pageNumber, pageDict, ref) {
|
||||
this.pageNumber = pageNumber;
|
||||
this.pageDict = pageDict;
|
||||
this.stats = {
|
||||
create: Date.now(),
|
||||
compile: 0.0,
|
||||
fonts: 0.0,
|
||||
images: 0.0,
|
||||
render: 0.0
|
||||
};
|
||||
this.stats = new StatTimer();
|
||||
this.stats.enabled = !!globalScope.PDFJS.enableStats;
|
||||
this.xref = xref;
|
||||
this.ref = ref;
|
||||
|
||||
@ -74,14 +72,14 @@ var Page = (function PageClosure() {
|
||||
}
|
||||
|
||||
Page.prototype = {
|
||||
getPageProp: function pageGetPageProp(key) {
|
||||
return this.xref.fetchIfRef(this.pageDict.get(key));
|
||||
getPageProp: function Page_getPageProp(key) {
|
||||
return this.pageDict.get(key);
|
||||
},
|
||||
inheritPageProp: function pageInheritPageProp(key) {
|
||||
inheritPageProp: function Page_inheritPageProp(key) {
|
||||
var dict = this.pageDict;
|
||||
var obj = dict.get(key);
|
||||
while (obj === undefined) {
|
||||
dict = this.xref.fetchIfRef(dict.get('Parent'));
|
||||
dict = dict.get('Parent');
|
||||
if (!dict)
|
||||
break;
|
||||
obj = dict.get(key);
|
||||
@ -102,25 +100,35 @@ var Page = (function PageClosure() {
|
||||
return shadow(this, 'mediaBox', obj);
|
||||
},
|
||||
get view() {
|
||||
var obj = this.inheritPageProp('CropBox');
|
||||
var cropBox = this.inheritPageProp('CropBox');
|
||||
var view = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: this.width,
|
||||
height: this.height
|
||||
};
|
||||
if (!isArray(cropBox) || cropBox.length !== 4)
|
||||
return shadow(this, 'view', view);
|
||||
|
||||
var mediaBox = this.mediaBox;
|
||||
var offsetX = mediaBox[0], offsetY = mediaBox[1];
|
||||
if (isArray(obj) && obj.length == 4) {
|
||||
var tl = this.rotatePoint(obj[0] - offsetX, obj[1] - offsetY);
|
||||
var br = this.rotatePoint(obj[2] - offsetX, obj[3] - offsetY);
|
||||
view.x = Math.min(tl.x, br.x);
|
||||
view.y = Math.min(tl.y, br.y);
|
||||
view.width = Math.abs(tl.x - br.x);
|
||||
view.height = Math.abs(tl.y - br.y);
|
||||
}
|
||||
|
||||
return shadow(this, 'cropBox', view);
|
||||
// From the spec, 6th ed., p.963:
|
||||
// "The crop, bleed, trim, and art boxes should not ordinarily
|
||||
// extend beyond the boundaries of the media box. If they do, they are
|
||||
// effectively reduced to their intersection with the media box."
|
||||
cropBox = Util.intersect(cropBox, mediaBox);
|
||||
if (!cropBox)
|
||||
return shadow(this, 'view', view);
|
||||
|
||||
var tl = this.rotatePoint(cropBox[0] - offsetX, cropBox[1] - offsetY);
|
||||
var br = this.rotatePoint(cropBox[2] - offsetX, cropBox[3] - offsetY);
|
||||
view.x = Math.min(tl.x, br.x);
|
||||
view.y = Math.min(tl.y, br.y);
|
||||
view.width = Math.abs(tl.x - br.x);
|
||||
view.height = Math.abs(tl.y - br.y);
|
||||
|
||||
return shadow(this, 'view', view);
|
||||
},
|
||||
get annotations() {
|
||||
return shadow(this, 'annotations', this.inheritPageProp('Annots'));
|
||||
@ -162,10 +170,10 @@ var Page = (function PageClosure() {
|
||||
return shadow(this, 'rotate', rotate);
|
||||
},
|
||||
|
||||
startRenderingFromIRQueue: function pageStartRenderingFromIRQueue(
|
||||
IRQueue, fonts) {
|
||||
startRenderingFromOperatorList:
|
||||
function Page_startRenderingFromOperatorList(operatorList, fonts) {
|
||||
var self = this;
|
||||
this.IRQueue = IRQueue;
|
||||
this.operatorList = operatorList;
|
||||
|
||||
var displayContinuation = function pageDisplayContinuation() {
|
||||
// Always defer call to display() to work around bug in
|
||||
@ -176,20 +184,23 @@ var Page = (function PageClosure() {
|
||||
};
|
||||
|
||||
this.ensureFonts(fonts,
|
||||
function pageStartRenderingFromIRQueueEnsureFonts() {
|
||||
displayContinuation();
|
||||
});
|
||||
function pageStartRenderingFromOperatorListEnsureFonts() {
|
||||
displayContinuation();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
getIRQueue: function pageGetIRQueue(handler, dependency) {
|
||||
if (this.IRQueue) {
|
||||
getOperatorList: function Page_getOperatorList(handler, dependency) {
|
||||
if (this.operatorList) {
|
||||
// content was compiled
|
||||
return this.IRQueue;
|
||||
return this.operatorList;
|
||||
}
|
||||
|
||||
this.stats.time('Build IR Queue');
|
||||
|
||||
var xref = this.xref;
|
||||
var content = xref.fetchIfRef(this.content);
|
||||
var resources = xref.fetchIfRef(this.resources);
|
||||
var content = this.content;
|
||||
var resources = this.resources;
|
||||
if (isArray(content)) {
|
||||
// fetching items
|
||||
var i, n = content.length;
|
||||
@ -206,12 +217,13 @@ var Page = (function PageClosure() {
|
||||
|
||||
var pe = this.pe = new PartialEvaluator(
|
||||
xref, handler, 'p' + this.pageNumber + '_');
|
||||
var IRQueue = {};
|
||||
return (this.IRQueue = pe.getIRQueue(content, resources, IRQueue,
|
||||
dependency));
|
||||
|
||||
this.operatorList = pe.getOperatorList(content, resources, dependency);
|
||||
this.stats.timeEnd('Build IR Queue');
|
||||
return this.operatorList;
|
||||
},
|
||||
|
||||
extractTextContent: function pageExtractPageContent() {
|
||||
extractTextContent: function Page_extractTextContent() {
|
||||
if ('textContent' in this) {
|
||||
// text content was extracted
|
||||
return this.textContent;
|
||||
@ -241,28 +253,30 @@ var Page = (function PageClosure() {
|
||||
return (this.textContent = text);
|
||||
},
|
||||
|
||||
ensureFonts: function pageEnsureFonts(fonts, callback) {
|
||||
ensureFonts: function Page_ensureFonts(fonts, callback) {
|
||||
this.stats.time('Font Loading');
|
||||
// Convert the font names to the corresponding font obj.
|
||||
for (var i = 0, ii = fonts.length; i < ii; i++) {
|
||||
fonts[i] = this.objs.objs[fonts[i]].data;
|
||||
}
|
||||
|
||||
// Load all the fonts
|
||||
var fontObjs = FontLoader.bind(
|
||||
FontLoader.bind(
|
||||
fonts,
|
||||
function pageEnsureFontsFontObjs(fontObjs) {
|
||||
this.stats.fonts = Date.now();
|
||||
this.stats.timeEnd('Font Loading');
|
||||
|
||||
callback.call(this);
|
||||
}.bind(this),
|
||||
this.objs
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
display: function pageDisplay(gfx, callback) {
|
||||
display: function Page_display(gfx, callback) {
|
||||
var stats = this.stats;
|
||||
stats.time('Rendering');
|
||||
var xref = this.xref;
|
||||
var resources = xref.fetchIfRef(this.resources);
|
||||
var mediaBox = xref.fetchIfRef(this.mediaBox);
|
||||
var resources = this.resources;
|
||||
var mediaBox = this.mediaBox;
|
||||
assertWellFormed(isDict(resources), 'invalid page resources');
|
||||
|
||||
gfx.xref = xref;
|
||||
@ -273,21 +287,29 @@ var Page = (function PageClosure() {
|
||||
rotate: this.rotate });
|
||||
|
||||
var startIdx = 0;
|
||||
var length = this.IRQueue.fnArray.length;
|
||||
var IRQueue = this.IRQueue;
|
||||
var length = this.operatorList.fnArray.length;
|
||||
var operatorList = this.operatorList;
|
||||
var stepper = null;
|
||||
if (PDFJS.pdfBug && StepperManager.enabled) {
|
||||
stepper = StepperManager.create(this.pageNumber);
|
||||
stepper.init(operatorList);
|
||||
stepper.nextBreakPoint = stepper.getNextBreakPoint();
|
||||
}
|
||||
|
||||
var self = this;
|
||||
function next() {
|
||||
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next);
|
||||
startIdx =
|
||||
gfx.executeOperatorList(operatorList, startIdx, next, stepper);
|
||||
if (startIdx == length) {
|
||||
self.stats.render = Date.now();
|
||||
gfx.endDrawing();
|
||||
stats.timeEnd('Rendering');
|
||||
stats.timeEnd('Overall');
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
next();
|
||||
},
|
||||
rotatePoint: function pageRotatePoint(x, y, reverse) {
|
||||
rotatePoint: function Page_rotatePoint(x, y, reverse) {
|
||||
var rotate = reverse ? (360 - this.rotate) : this.rotate;
|
||||
switch (rotate) {
|
||||
case 180:
|
||||
@ -302,7 +324,7 @@ var Page = (function PageClosure() {
|
||||
return {x: x, y: this.height - y};
|
||||
}
|
||||
},
|
||||
getLinks: function pageGetLinks() {
|
||||
getLinks: function Page_getLinks() {
|
||||
var links = [];
|
||||
var annotations = pageGetAnnotations();
|
||||
var i, n = annotations.length;
|
||||
@ -313,19 +335,35 @@ var Page = (function PageClosure() {
|
||||
}
|
||||
return links;
|
||||
},
|
||||
getAnnotations: function pageGetAnnotations() {
|
||||
getAnnotations: function Page_getAnnotations() {
|
||||
var xref = this.xref;
|
||||
function getInheritableProperty(annotation, name) {
|
||||
var item = annotation;
|
||||
while (item && !item.has(name)) {
|
||||
item = xref.fetchIfRef(item.get('Parent'));
|
||||
item = item.get('Parent');
|
||||
}
|
||||
if (!item)
|
||||
return null;
|
||||
return item.get(name);
|
||||
}
|
||||
function isValidUrl(url) {
|
||||
if (!url)
|
||||
return false;
|
||||
var colon = url.indexOf(':');
|
||||
if (colon < 0)
|
||||
return false;
|
||||
var protocol = url.substr(0, colon);
|
||||
switch (protocol) {
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'ftp':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var annotations = xref.fetchIfRef(this.annotations) || [];
|
||||
var annotations = this.annotations || [];
|
||||
var i, n = annotations.length;
|
||||
var items = [];
|
||||
for (i = 0; i < n; ++i) {
|
||||
@ -348,11 +386,16 @@ var Page = (function PageClosure() {
|
||||
item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
|
||||
switch (subtype.name) {
|
||||
case 'Link':
|
||||
var a = this.xref.fetchIfRef(annotation.get('A'));
|
||||
var a = annotation.get('A');
|
||||
if (a) {
|
||||
switch (a.get('S').name) {
|
||||
case 'URI':
|
||||
item.url = a.get('URI');
|
||||
var url = a.get('URI');
|
||||
// TODO: pdf spec mentions urls can be relative to a Base
|
||||
// entry in the dictionary.
|
||||
if (!isValidUrl(url))
|
||||
url = '';
|
||||
item.url = url;
|
||||
break;
|
||||
case 'GoTo':
|
||||
item.dest = a.get('D');
|
||||
@ -376,21 +419,22 @@ var Page = (function PageClosure() {
|
||||
var fieldName = [];
|
||||
var namedItem = annotation, ref = annotationRef;
|
||||
while (namedItem) {
|
||||
var parentRef = namedItem.get('Parent');
|
||||
var parent = xref.fetchIfRef(parentRef);
|
||||
var parent = namedItem.get('Parent');
|
||||
var parentRef = namedItem.getRaw('Parent');
|
||||
var name = namedItem.get('T');
|
||||
if (name)
|
||||
if (name) {
|
||||
fieldName.unshift(stringToPDFString(name));
|
||||
else {
|
||||
} else {
|
||||
// The field name is absent, that means more than one field
|
||||
// with the same name may exist. Replacing the empty name
|
||||
// with the '`' plus index in the parent's 'Kids' array.
|
||||
// This is not in the PDF spec but necessary to id the
|
||||
// the input controls.
|
||||
var kids = xref.fetchIfRef(parent.get('Kids'));
|
||||
var kids = parent.get('Kids');
|
||||
var j, jj;
|
||||
for (j = 0, jj = kids.length; j < jj; j++) {
|
||||
if (kids[j].num == ref.num && kids[j].gen == ref.gen)
|
||||
var kidRef = kids[j];
|
||||
if (kidRef.num == ref.num && kidRef.gen == ref.gen)
|
||||
break;
|
||||
}
|
||||
fieldName.unshift('`' + j);
|
||||
@ -423,17 +467,17 @@ var Page = (function PageClosure() {
|
||||
}
|
||||
return items;
|
||||
},
|
||||
startRendering: function pageStartRendering(ctx, callback, textLayer) {
|
||||
this.startRenderingTime = Date.now();
|
||||
|
||||
// If there is no displayReadyPromise yet, then the IRQueue was never
|
||||
startRendering: function Page_startRendering(ctx, callback, textLayer) {
|
||||
var stats = this.stats;
|
||||
stats.time('Overall');
|
||||
// If there is no displayReadyPromise yet, then the operatorList was never
|
||||
// requested before. Make the request and create the promise.
|
||||
if (!this.displayReadyPromise) {
|
||||
this.pdf.startRendering(this);
|
||||
this.displayReadyPromise = new Promise();
|
||||
}
|
||||
|
||||
// Once the IRQueue and fonts are loaded, perform the actual rendering.
|
||||
// Once the operatorList and fonts are loaded, do the actual rendering.
|
||||
this.displayReadyPromise.then(
|
||||
function pageDisplayReadyPromise() {
|
||||
var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
|
||||
@ -443,14 +487,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);
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -465,9 +509,6 @@ var Page = (function PageClosure() {
|
||||
* Right now there exists one PDFDocModel on the main thread + one object
|
||||
* for each worker. If there is no worker support enabled, there are two
|
||||
* `PDFDocModel` objects on the main thread created.
|
||||
* TODO: Refactor the internal object structure, such that there is no
|
||||
* need for the `PDFDocModel` anymore and there is only one object on the
|
||||
* main thread and not one entire copy on each worker instance.
|
||||
*/
|
||||
var PDFDocModel = (function PDFDocModelClosure() {
|
||||
function PDFDocModel(arg, callback) {
|
||||
@ -483,7 +524,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
||||
assertWellFormed(stream.length > 0, 'stream must have data');
|
||||
this.stream = stream;
|
||||
this.setup();
|
||||
this.acroForm = this.xref.fetchIfRef(this.catalog.catDict.get('AcroForm'));
|
||||
this.acroForm = this.catalog.catDict.get('AcroForm');
|
||||
}
|
||||
|
||||
function find(stream, needle, limit, backwards) {
|
||||
@ -563,7 +604,7 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
||||
},
|
||||
// Find the header, remove leading garbage and setup the stream
|
||||
// starting from the header.
|
||||
checkHeader: function pdfDocCheckHeader() {
|
||||
checkHeader: function PDFDocModel_checkHeader() {
|
||||
var stream = this.stream;
|
||||
stream.reset();
|
||||
if (find(stream, '%PDF-', 1024)) {
|
||||
@ -573,21 +614,13 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
||||
}
|
||||
// May not be a PDF file, continue anyway.
|
||||
},
|
||||
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
||||
setup: function PDFDocModel_setup(ownerPassword, userPassword) {
|
||||
this.checkHeader();
|
||||
var xref = new XRef(this.stream,
|
||||
this.startXRef,
|
||||
this.mainXRefEntriesOffset);
|
||||
this.xref = xref;
|
||||
this.catalog = new Catalog(xref);
|
||||
if (xref.trailer && xref.trailer.has('ID')) {
|
||||
var fileID = '';
|
||||
var id = xref.fetchIfRef(xref.trailer.get('ID'))[0];
|
||||
id.split('').forEach(function(el) {
|
||||
fileID += Number(el.charCodeAt(0)).toString(16);
|
||||
});
|
||||
this.fileID = fileID;
|
||||
}
|
||||
},
|
||||
get numPages() {
|
||||
var linearization = this.linearization;
|
||||
@ -595,23 +628,42 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
||||
// shadow the prototype getter
|
||||
return shadow(this, 'numPages', num);
|
||||
},
|
||||
getFingerprint: function pdfDocGetFingerprint() {
|
||||
if (this.fileID) {
|
||||
return this.fileID;
|
||||
getDocumentInfo: function PDFDocModel_getDocumentInfo() {
|
||||
var info;
|
||||
if (this.xref.trailer.has('Info')) {
|
||||
var infoDict = this.xref.trailer.get('Info');
|
||||
|
||||
info = {};
|
||||
infoDict.forEach(function(key, value) {
|
||||
info[key] = typeof value !== 'string' ? value :
|
||||
stringToPDFString(value);
|
||||
});
|
||||
}
|
||||
|
||||
return shadow(this, 'getDocumentInfo', info);
|
||||
},
|
||||
getFingerprint: function PDFDocModel_getFingerprint() {
|
||||
var xref = this.xref, fileID;
|
||||
if (xref.trailer.has('ID')) {
|
||||
fileID = '';
|
||||
var id = xref.trailer.get('ID')[0];
|
||||
id.split('').forEach(function(el) {
|
||||
fileID += Number(el.charCodeAt(0)).toString(16);
|
||||
});
|
||||
} else {
|
||||
// If we got no fileID, then we generate one,
|
||||
// from the first 100 bytes of PDF
|
||||
var data = this.stream.bytes.subarray(0, 100);
|
||||
var hash = calculateMD5(data, 0, data.length);
|
||||
var strHash = '';
|
||||
fileID = '';
|
||||
for (var i = 0, length = hash.length; i < length; i++) {
|
||||
strHash += Number(hash[i]).toString(16);
|
||||
fileID += Number(hash[i]).toString(16);
|
||||
}
|
||||
|
||||
return strHash;
|
||||
}
|
||||
|
||||
return shadow(this, 'getFingerprint', fileID);
|
||||
},
|
||||
getPage: function pdfDocGetPage(n) {
|
||||
getPage: function PDFDocModel_getPage(n) {
|
||||
return this.catalog.getPage(n);
|
||||
}
|
||||
};
|
||||
@ -636,9 +688,10 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
|
||||
this.data = data;
|
||||
this.stream = stream;
|
||||
this.pdf = new PDFDocModel(stream);
|
||||
this.fingerprint = this.pdf.getFingerprint();
|
||||
this.catalog = this.pdf.catalog;
|
||||
this.pdfModel = new PDFDocModel(stream);
|
||||
this.fingerprint = this.pdfModel.getFingerprint();
|
||||
this.info = this.pdfModel.getDocumentInfo();
|
||||
this.catalog = this.pdfModel.catalog;
|
||||
this.objs = new PDFObjects();
|
||||
|
||||
this.pageCache = [];
|
||||
@ -653,13 +706,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);
|
||||
|
||||
@ -678,7 +741,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.
|
||||
@ -687,13 +752,13 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
}
|
||||
|
||||
PDFDoc.prototype = {
|
||||
setupFakeWorker: function() {
|
||||
setupFakeWorker: function PDFDoc_setupFakeWorker() {
|
||||
// If we don't use a worker, just post/sendMessage to the main thread.
|
||||
var fakeWorker = {
|
||||
postMessage: function pdfDocPostMessage(obj) {
|
||||
postMessage: function PDFDoc_postMessage(obj) {
|
||||
fakeWorker.onmessage({data: obj});
|
||||
},
|
||||
terminate: function pdfDocTerminate() {}
|
||||
terminate: function PDFDoc_terminate() {}
|
||||
};
|
||||
|
||||
var messageHandler = new MessageHandler('main', fakeWorker);
|
||||
@ -705,7 +770,7 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
},
|
||||
|
||||
|
||||
setupMessageHandler: function(messageHandler) {
|
||||
setupMessageHandler: function PDFDoc_setupMessageHandler(messageHandler) {
|
||||
this.messageHandler = messageHandler;
|
||||
|
||||
messageHandler.on('page', function pdfDocPage(data) {
|
||||
@ -713,7 +778,8 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
var page = this.pageCache[pageNum];
|
||||
var depFonts = data.depFonts;
|
||||
|
||||
page.startRenderingFromIRQueue(data.IRQueue, depFonts);
|
||||
page.stats.timeEnd('Page Request');
|
||||
page.startRenderingFromOperatorList(data.operatorList, depFonts);
|
||||
}, this);
|
||||
|
||||
messageHandler.on('obj', function pdfDocObj(data) {
|
||||
@ -740,37 +806,22 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
file = new Stream(file, 0, file.length, fontFileDict);
|
||||
}
|
||||
|
||||
// For now, resolve the font object here direclty. The real font
|
||||
// object is then created in FontLoader.bind().
|
||||
this.objs.resolve(id, {
|
||||
name: name,
|
||||
file: file,
|
||||
properties: properties
|
||||
});
|
||||
// At this point, only the font object is created but the font is
|
||||
// not yet attached to the DOM. This is done in `FontLoader.bind`.
|
||||
var font = new Font(name, file, properties);
|
||||
this.objs.resolve(id, font);
|
||||
break;
|
||||
default:
|
||||
throw 'Got unkown object type ' + type;
|
||||
error('Got unkown object type ' + type);
|
||||
}
|
||||
}, this);
|
||||
|
||||
messageHandler.on('font_ready', function pdfDocFontReady(data) {
|
||||
var id = data[0];
|
||||
var font = new FontShape(data[1]);
|
||||
|
||||
// If there is no string, then there is nothing to attach to the DOM.
|
||||
if (!font.str) {
|
||||
this.objs.resolve(id, font);
|
||||
} else {
|
||||
this.objs.setData(id, font);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
messageHandler.on('page_error', function pdfDocError(data) {
|
||||
var page = this.pageCache[data.pageNum];
|
||||
if (page.displayReadyPromise)
|
||||
page.displayReadyPromise.reject(data.error);
|
||||
else
|
||||
throw data.error;
|
||||
error(data.error);
|
||||
}, this);
|
||||
|
||||
messageHandler.on('text_extracted', function pdfTextExtracted(data) {
|
||||
@ -786,13 +837,13 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
error('Only 3 component or 1 component can be returned');
|
||||
|
||||
var img = new Image();
|
||||
img.onload = (function jpegImageLoaderOnload() {
|
||||
img.onload = (function messageHandler_onloadClosure() {
|
||||
var width = img.width;
|
||||
var height = img.height;
|
||||
var size = width * height;
|
||||
var rgbaLength = size * 4;
|
||||
var buf = new Uint8Array(size * components);
|
||||
var tmpCanvas = new ScratchCanvas(width, height);
|
||||
var tmpCanvas = createScratchCanvas(width, height);
|
||||
var tmpCtx = tmpCanvas.getContext('2d');
|
||||
tmpCtx.drawImage(img, 0, 0);
|
||||
var data = tmpCtx.getImageData(0, 0, width, height).data;
|
||||
@ -821,21 +872,22 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
},
|
||||
|
||||
get numPages() {
|
||||
return this.pdf.numPages;
|
||||
return this.pdfModel.numPages;
|
||||
},
|
||||
|
||||
startRendering: function pdfDocStartRendering(page) {
|
||||
startRendering: function PDFDoc_startRendering(page) {
|
||||
// The worker might not be ready to receive the page request yet.
|
||||
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
|
||||
page.stats.time('Page Request');
|
||||
this.messageHandler.send('page_request', page.pageNumber + 1);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
getPage: function pdfDocGetPage(n) {
|
||||
getPage: function PDFDoc_getPage(n) {
|
||||
if (this.pageCache[n])
|
||||
return this.pageCache[n];
|
||||
|
||||
var page = this.pdf.getPage(n);
|
||||
var page = this.pdfModel.getPage(n);
|
||||
// Add a reference to the objects such that Page can forward the reference
|
||||
// to the CanvasGraphics and so on.
|
||||
page.objs = this.objs;
|
||||
@ -843,13 +895,13 @@ var PDFDoc = (function PDFDocClosure() {
|
||||
return (this.pageCache[n] = page);
|
||||
},
|
||||
|
||||
extractText: function pdfDocExtractExtractText() {
|
||||
extractText: function PDFDoc_extractText() {
|
||||
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
|
||||
this.messageHandler.send('extract_text');
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
destroy: function pdfDocDestroy() {
|
||||
destroy: function PDFDoc_destroy() {
|
||||
if (this.worker)
|
||||
this.worker.terminate();
|
||||
|
||||
|
@ -21,7 +21,7 @@ var ARCFourCipher = (function ARCFourCipherClosure() {
|
||||
}
|
||||
|
||||
ARCFourCipher.prototype = {
|
||||
encryptBlock: function arcFourCipherEncryptBlock(data) {
|
||||
encryptBlock: function ARCFourCipher_encryptBlock(data) {
|
||||
var i, n = data.length, tmp, tmp2;
|
||||
var a = this.a, b = this.b, s = this.s;
|
||||
var output = new Uint8Array(n);
|
||||
@ -133,7 +133,7 @@ var NullCipher = (function NullCipherClosure() {
|
||||
}
|
||||
|
||||
NullCipher.prototype = {
|
||||
decryptBlock: function nullCipherDecryptBlock(data) {
|
||||
decryptBlock: function NullCipher_decryptBlock(data) {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
@ -371,7 +371,7 @@ var AES128Cipher = (function AES128CipherClosure() {
|
||||
}
|
||||
|
||||
AES128Cipher.prototype = {
|
||||
decryptBlock: function aes128CipherDecryptBlock(data) {
|
||||
decryptBlock: function AES128Cipher_decryptBlock(data) {
|
||||
var i, sourceLength = data.length;
|
||||
var buffer = this.buffer, bufferLength = this.bufferPosition;
|
||||
// waiting for IV values -- they are at the start of the stream
|
||||
@ -400,7 +400,7 @@ var CipherTransform = (function CipherTransformClosure() {
|
||||
this.streamCipherConstructor = streamCipherConstructor;
|
||||
}
|
||||
CipherTransform.prototype = {
|
||||
createStream: function cipherTransformCreateStream(stream) {
|
||||
createStream: function CipherTransform_createStream(stream) {
|
||||
var cipher = new this.streamCipherConstructor();
|
||||
return new DecryptStream(stream,
|
||||
function cipherTransformDecryptStream(data) {
|
||||
@ -408,7 +408,7 @@ var CipherTransform = (function CipherTransformClosure() {
|
||||
}
|
||||
);
|
||||
},
|
||||
decryptString: function cipherTransformDecryptString(s) {
|
||||
decryptString: function CipherTransform_decryptString(s) {
|
||||
var cipher = new this.stringCipherConstructor();
|
||||
var data = stringToBytes(s);
|
||||
data = cipher.decryptBlock(data);
|
||||
@ -570,12 +570,11 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
||||
};
|
||||
}
|
||||
error('Unknown crypto method');
|
||||
return null;
|
||||
}
|
||||
|
||||
CipherTransformFactory.prototype = {
|
||||
createCipherTransform: function buildCipherCreateCipherTransform(num,
|
||||
gen) {
|
||||
createCipherTransform:
|
||||
function CipherTransformFactory_createCipherTransform(num, gen) {
|
||||
if (this.algorithm == 4) {
|
||||
return new CipherTransform(
|
||||
buildCipherConstructor(this.cf, this.stmf,
|
||||
|
231
src/evaluator.js
@ -111,9 +111,22 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
EX: 'endCompat'
|
||||
};
|
||||
|
||||
function splitCombinedOperations(operations) {
|
||||
// Two operations can be combined together, trying to find which two
|
||||
// operations were concatenated.
|
||||
for (var i = operations.length - 1; i > 0; i--) {
|
||||
var op1 = operations.substring(0, i), op2 = operations.substring(i);
|
||||
if (op1 in OP_MAP && op2 in OP_MAP)
|
||||
return [op1, op2]; // operations found
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
PartialEvaluator.prototype = {
|
||||
getIRQueue: function partialEvaluatorGetIRQueue(stream, resources,
|
||||
queue, dependency) {
|
||||
getOperatorList: function PartialEvaluator_getOperatorList(stream,
|
||||
resources,
|
||||
dependency,
|
||||
queue) {
|
||||
|
||||
var self = this;
|
||||
var xref = this.xref;
|
||||
@ -131,18 +144,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSetFont(fontName, fontRef) {
|
||||
function handleSetFont(fontName, font) {
|
||||
var loadedName = null;
|
||||
|
||||
var fontRes = resources.get('Font');
|
||||
|
||||
// TODO: TOASK: Is it possible to get here? If so, what does
|
||||
// args[0].name should be like???
|
||||
assert(fontRes, 'fontRes not available');
|
||||
|
||||
fontRes = xref.fetchIfRef(fontRes);
|
||||
fontRef = fontRef || fontRes.get(fontName);
|
||||
var font = xref.fetchIfRef(fontRef);
|
||||
font = xref.fetchIfRef(font) || fontRes.get(fontName);
|
||||
assertWellFormed(isDict(font));
|
||||
if (!font.loadedName) {
|
||||
font.translated = self.translateFont(font, xref, resources,
|
||||
@ -159,6 +168,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
// a Stream in the main thread.
|
||||
if (translated.file)
|
||||
translated.file = translated.file.getBytes();
|
||||
if (translated.properties.file) {
|
||||
translated.properties.file =
|
||||
translated.properties.file.getBytes();
|
||||
}
|
||||
|
||||
handler.send('obj', [
|
||||
loadedName,
|
||||
@ -173,7 +186,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
// Ensure the font is ready before the font is set
|
||||
// and later on used for drawing.
|
||||
// TODO: This should get insert to the IRQueue only once per
|
||||
// OPTIMIZE: This should get insert to the operatorList only once per
|
||||
// page.
|
||||
insertDependency([loadedName]);
|
||||
return loadedName;
|
||||
@ -235,6 +248,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}, handler, xref, resources, image, inline);
|
||||
}
|
||||
|
||||
if (!queue)
|
||||
queue = {};
|
||||
|
||||
if (!queue.argsArray) {
|
||||
queue.argsArray = [];
|
||||
}
|
||||
@ -245,45 +261,48 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
var fnArray = queue.fnArray, argsArray = queue.argsArray;
|
||||
var dependencyArray = dependency || [];
|
||||
|
||||
resources = xref.fetchIfRef(resources) || new Dict();
|
||||
var xobjs = xref.fetchIfRef(resources.get('XObject')) || new Dict();
|
||||
var patterns = xref.fetchIfRef(resources.get('Pattern')) || new Dict();
|
||||
var parser = new Parser(new Lexer(stream), false);
|
||||
resources = resources || new Dict();
|
||||
var xobjs = resources.get('XObject') || new Dict();
|
||||
var patterns = resources.get('Pattern') || new Dict();
|
||||
var parser = new Parser(new Lexer(stream), false, xref);
|
||||
var res = resources;
|
||||
var hasNextObj = false, nextObj;
|
||||
var args = [], obj;
|
||||
var getObjBt = function getObjBt() {
|
||||
parser = this.oldParser;
|
||||
return { name: 'BT' };
|
||||
};
|
||||
var TILING_PATTERN = 1, SHADING_PATTERN = 2;
|
||||
|
||||
while (!isEOF(obj = parser.getObj())) {
|
||||
while (true) {
|
||||
if (hasNextObj) {
|
||||
obj = nextObj;
|
||||
hasNextObj = false;
|
||||
} else {
|
||||
obj = parser.getObj();
|
||||
if (isEOF(obj))
|
||||
break;
|
||||
}
|
||||
|
||||
if (isCmd(obj)) {
|
||||
var cmd = obj.cmd;
|
||||
var fn = OP_MAP[cmd];
|
||||
if (!fn) {
|
||||
// invalid content command, trying to recover
|
||||
if (cmd.substr(-2) == 'BT') {
|
||||
fn = OP_MAP[cmd.substr(0, cmd.length - 2)];
|
||||
// feeding 'BT' on next interation
|
||||
parser = {
|
||||
getObj: getObjBt,
|
||||
oldParser: parser
|
||||
};
|
||||
var cmds = splitCombinedOperations(cmd);
|
||||
if (cmds) {
|
||||
cmd = cmds[0];
|
||||
fn = OP_MAP[cmd];
|
||||
// feeding other command on the next interation
|
||||
hasNextObj = true;
|
||||
nextObj = Cmd.get(cmds[1]);
|
||||
}
|
||||
}
|
||||
assertWellFormed(fn, 'Unknown command "' + cmd + '"');
|
||||
// TODO figure out how to type-check vararg functions
|
||||
|
||||
if ((cmd == 'SCN' || cmd == 'scn') && !args[args.length - 1].code) {
|
||||
// Use the IR version for setStroke/FillColorN.
|
||||
fn += '_IR';
|
||||
|
||||
// compile tiling patterns
|
||||
var patternName = args[args.length - 1];
|
||||
// SCN/scn applies patterns along with normal colors
|
||||
if (isName(patternName)) {
|
||||
var pattern = xref.fetchIfRef(patterns.get(patternName.name));
|
||||
var pattern = patterns.get(patternName.name);
|
||||
if (pattern) {
|
||||
var dict = isStream(pattern) ? pattern.dict : pattern;
|
||||
var typeNum = dict.get('PatternType');
|
||||
@ -291,21 +310,20 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
if (typeNum == TILING_PATTERN) {
|
||||
// Create an IR of the pattern code.
|
||||
var depIdx = dependencyArray.length;
|
||||
var queueObj = {};
|
||||
var codeIR = this.getIRQueue(pattern, dict.get('Resources') ||
|
||||
resources, queueObj, dependencyArray);
|
||||
var operatorList = this.getOperatorList(pattern,
|
||||
dict.get('Resources') || resources, dependencyArray);
|
||||
|
||||
// Add the dependencies that are required to execute the
|
||||
// codeIR.
|
||||
// operatorList.
|
||||
insertDependency(dependencyArray.slice(depIdx));
|
||||
|
||||
args = TilingPattern.getIR(codeIR, dict, args);
|
||||
args = TilingPattern.getIR(operatorList, dict, args);
|
||||
}
|
||||
else if (typeNum == SHADING_PATTERN) {
|
||||
var shading = xref.fetchIfRef(dict.get('Shading'));
|
||||
var shading = dict.get('Shading');
|
||||
var matrix = dict.get('Matrix');
|
||||
var pattern = Pattern.parseShading(shading, matrix, xref, res,
|
||||
null /*ctx*/);
|
||||
var pattern = Pattern.parseShading(shading, matrix, xref,
|
||||
res);
|
||||
args = pattern.getIR();
|
||||
} else {
|
||||
error('Unkown PatternType ' + typeNum);
|
||||
@ -317,7 +335,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
var name = args[0].name;
|
||||
var xobj = xobjs.get(name);
|
||||
if (xobj) {
|
||||
xobj = xref.fetchIfRef(xobj);
|
||||
assertWellFormed(isStream(xobj), 'XObject should be a stream');
|
||||
|
||||
var type = xobj.dict.get('Subtype');
|
||||
@ -333,14 +350,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
fnArray.push('paintFormXObjectBegin');
|
||||
argsArray.push([matrix, bbox]);
|
||||
|
||||
// This adds the IRQueue of the xObj to the current queue.
|
||||
// This adds the operatorList of the xObj to the current queue.
|
||||
var depIdx = dependencyArray.length;
|
||||
|
||||
this.getIRQueue(xobj, xobj.dict.get('Resources') || resources,
|
||||
queue, dependencyArray);
|
||||
// Pass in the current `queue` object. That means the `fnArray`
|
||||
// and the `argsArray` in this scope is reused and new commands
|
||||
// are added to them.
|
||||
this.getOperatorList(xobj,
|
||||
xobj.dict.get('Resources') || resources,
|
||||
dependencyArray, queue);
|
||||
|
||||
// Add the dependencies that are required to execute the
|
||||
// codeIR.
|
||||
// operatorList.
|
||||
insertDependency(dependencyArray.slice(depIdx));
|
||||
|
||||
fn = 'paintFormXObjectEnd';
|
||||
@ -364,28 +385,27 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
args = [ColorSpace.parseToIR(args[0], xref, resources)];
|
||||
break;
|
||||
case 'shadingFill':
|
||||
var shadingRes = xref.fetchIfRef(res.get('Shading'));
|
||||
var shadingRes = res.get('Shading');
|
||||
if (!shadingRes)
|
||||
error('No shading resource found');
|
||||
|
||||
var shading = xref.fetchIfRef(shadingRes.get(args[0].name));
|
||||
var shading = shadingRes.get(args[0].name);
|
||||
if (!shading)
|
||||
error('No shading object found');
|
||||
|
||||
var shadingFill = Pattern.parseShading(shading, null, xref, res,
|
||||
null);
|
||||
var shadingFill = Pattern.parseShading(shading, null, xref, res);
|
||||
var patternIR = shadingFill.getIR();
|
||||
args = [patternIR];
|
||||
fn = 'shadingFill';
|
||||
break;
|
||||
case 'setGState':
|
||||
var dictName = args[0];
|
||||
var extGState = xref.fetchIfRef(resources.get('ExtGState'));
|
||||
var extGState = resources.get('ExtGState');
|
||||
|
||||
if (!isDict(extGState) || !extGState.has(dictName.name))
|
||||
break;
|
||||
|
||||
var gsState = xref.fetchIfRef(extGState.get(dictName.name));
|
||||
var gsState = extGState.get(dictName.name);
|
||||
|
||||
// This array holds the converted/processed state data.
|
||||
var gsStateObj = [];
|
||||
@ -450,10 +470,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
fnArray: fnArray,
|
||||
argsArray: argsArray
|
||||
};
|
||||
return queue;
|
||||
},
|
||||
|
||||
getTextContent: function partialEvaluatorGetIRQueue(stream, resources) {
|
||||
@ -532,7 +549,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
if (properties.composite) {
|
||||
// CIDSystemInfo helps to match CID to glyphs
|
||||
var cidSystemInfo = xref.fetchIfRef(dict.get('CIDSystemInfo'));
|
||||
var cidSystemInfo = dict.get('CIDSystemInfo');
|
||||
if (isDict(cidSystemInfo)) {
|
||||
properties.cidSystemInfo = {
|
||||
registry: cidSystemInfo.get('Registry'),
|
||||
@ -541,16 +558,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
};
|
||||
}
|
||||
|
||||
var cidToGidMap = xref.fetchIfRef(dict.get('CIDToGIDMap'));
|
||||
var cidToGidMap = dict.get('CIDToGIDMap');
|
||||
if (isStream(cidToGidMap))
|
||||
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'));
|
||||
var encoding = dict.get('Encoding');
|
||||
if (isDict(encoding)) {
|
||||
var baseName = encoding.get('BaseEncoding');
|
||||
if (baseName)
|
||||
@ -582,9 +601,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
properties.hasEncoding = hasEncoding;
|
||||
},
|
||||
|
||||
readToUnicode:
|
||||
function partialEvaluatorReadToUnicode(toUnicode, xref) {
|
||||
var cmapObj = xref.fetchIfRef(toUnicode);
|
||||
readToUnicode: function PartialEvaluator_readToUnicode(toUnicode, xref) {
|
||||
var cmapObj = toUnicode;
|
||||
var charToUnicode = [];
|
||||
if (isName(cmapObj)) {
|
||||
var isIdentityMap = cmapObj.name.substr(0, 9) == 'Identity-';
|
||||
@ -597,9 +615,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
var cmap = cmapObj.getBytes(cmapObj.length);
|
||||
for (var i = 0, ii = cmap.length; i < ii; i++) {
|
||||
var byte = cmap[i];
|
||||
if (byte == 0x20 || byte == 0x0D || byte == 0x0A ||
|
||||
byte == 0x3C || byte == 0x5B || byte == 0x5D) {
|
||||
var octet = cmap[i];
|
||||
if (octet == 0x20 || octet == 0x0D || octet == 0x0A ||
|
||||
octet == 0x3C || octet == 0x5B || octet == 0x5D) {
|
||||
switch (token) {
|
||||
case 'usecmap':
|
||||
error('usecmap is not implemented');
|
||||
@ -656,7 +674,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
tokens.push(token);
|
||||
token = '';
|
||||
}
|
||||
switch (byte) {
|
||||
switch (octet) {
|
||||
case 0x5B:
|
||||
// begin list parsing
|
||||
tokens.push(beginArrayToken);
|
||||
@ -670,7 +688,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
tokens.push(items);
|
||||
break;
|
||||
}
|
||||
} else if (byte == 0x3E) {
|
||||
} else if (octet == 0x3E) {
|
||||
if (token.length) {
|
||||
if (token.length <= 4) {
|
||||
// parsing hex number
|
||||
@ -679,21 +697,30 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
} else {
|
||||
// parsing hex UTF-16BE numbers
|
||||
var str = [];
|
||||
for (var i = 0, ii = token.length; i < ii; i += 4)
|
||||
str.push(parseInt(token.substr(i, 4), 16));
|
||||
for (var k = 0, kk = token.length; k < kk; k += 4) {
|
||||
var b = parseInt(token.substr(k, 4), 16);
|
||||
if (b <= 0x10) {
|
||||
k += 4;
|
||||
b = (b << 16) | parseInt(token.substr(k, 4), 16);
|
||||
b -= 0x10000;
|
||||
str.push(0xD800 | (b >> 10));
|
||||
str.push(0xDC00 | (b & 0x3FF));
|
||||
break;
|
||||
}
|
||||
str.push(b);
|
||||
}
|
||||
tokens.push(String.fromCharCode.apply(String, str));
|
||||
token = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
token += String.fromCharCode(byte);
|
||||
token += String.fromCharCode(octet);
|
||||
}
|
||||
}
|
||||
}
|
||||
return charToUnicode;
|
||||
},
|
||||
readCidToGidMap:
|
||||
function partialEvaluatorReadCidToGidMap(cidToGidStream) {
|
||||
readCidToGidMap: function PartialEvaluator_readCidToGidMap(cidToGidStream) {
|
||||
// Extract the encoding from the CIDToGIDMap
|
||||
var glyphsData = cidToGidStream.getBytes();
|
||||
|
||||
@ -710,16 +737,16 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
return result;
|
||||
},
|
||||
|
||||
extractWidths: function partialEvaluatorWidths(dict,
|
||||
extractWidths: function PartialEvaluator_extractWidths(dict,
|
||||
xref,
|
||||
descriptor,
|
||||
properties) {
|
||||
var glyphsWidths = [];
|
||||
var defaultWidth = 0;
|
||||
if (properties.composite) {
|
||||
defaultWidth = xref.fetchIfRef(dict.get('DW')) || 1000;
|
||||
defaultWidth = dict.get('DW') || 1000;
|
||||
|
||||
var widths = xref.fetchIfRef(dict.get('W'));
|
||||
var widths = dict.get('W');
|
||||
if (widths) {
|
||||
var start = 0, end = 0;
|
||||
for (var i = 0, ii = widths.length; i < ii; i++) {
|
||||
@ -740,7 +767,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}
|
||||
} else {
|
||||
var firstChar = properties.firstChar;
|
||||
var widths = xref.fetchIfRef(dict.get('Widths'));
|
||||
var widths = dict.get('Widths');
|
||||
if (widths) {
|
||||
var j = firstChar;
|
||||
for (var i = 0, ii = widths.length; i < ii; i++)
|
||||
@ -762,7 +789,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
properties.widths = glyphsWidths;
|
||||
},
|
||||
|
||||
getBaseFontMetrics: function getBaseFontMetrics(name) {
|
||||
getBaseFontMetrics: function PartialEvaluator_getBaseFontMetrics(name) {
|
||||
var defaultWidth = 0, widths = [];
|
||||
var glyphWidths = Metrics[stdFontMap[name] || name];
|
||||
if (isNum(glyphWidths)) {
|
||||
@ -777,8 +804,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
};
|
||||
},
|
||||
|
||||
translateFont: function partialEvaluatorTranslateFont(dict, xref, resources,
|
||||
dependency) {
|
||||
translateFont: function PartialEvaluator_translateFont(dict,
|
||||
xref,
|
||||
resources,
|
||||
dependency) {
|
||||
var baseDict = dict;
|
||||
var type = dict.get('Subtype');
|
||||
assertWellFormed(isName(type), 'invalid font Subtype');
|
||||
@ -793,10 +822,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
if (!df)
|
||||
return null;
|
||||
|
||||
if (isRef(df))
|
||||
df = xref.fetch(df);
|
||||
|
||||
dict = xref.fetchIfRef(isRef(df) ? df : df[0]);
|
||||
dict = isArray(df) ? xref.fetchIfRef(df[0]) : df;
|
||||
|
||||
type = dict.get('Subtype');
|
||||
assertWellFormed(isName(type), 'invalid font Subtype');
|
||||
@ -804,7 +830,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
}
|
||||
var maxCharIndex = composite ? 0xFFFF : 0xFF;
|
||||
|
||||
var descriptor = xref.fetchIfRef(dict.get('FontDescriptor'));
|
||||
var descriptor = dict.get('FontDescriptor');
|
||||
if (!descriptor) {
|
||||
if (type.name == 'Type3') {
|
||||
// FontDescriptor is only required for Type3 fonts when the document
|
||||
@ -826,8 +852,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,
|
||||
@ -845,34 +872,31 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
properties: properties
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// According to the spec if 'FontDescriptor' is declared, 'FirstChar',
|
||||
// 'LastChar' and 'Widths' should exists too, but some PDF encoders seems
|
||||
// 'LastChar' and 'Widths' should exist too, but some PDF encoders seem
|
||||
// to ignore this rule when a variant of a standart font is used.
|
||||
// TODO Fill the width array depending on which of the base font this is
|
||||
// a variant.
|
||||
var firstChar = xref.fetchIfRef(dict.get('FirstChar')) || 0;
|
||||
var lastChar = xref.fetchIfRef(dict.get('LastChar')) || maxCharIndex;
|
||||
var fontName = xref.fetchIfRef(descriptor.get('FontName'));
|
||||
var firstChar = dict.get('FirstChar') || 0;
|
||||
var lastChar = dict.get('LastChar') || maxCharIndex;
|
||||
var fontName = descriptor.get('FontName');
|
||||
// Some bad pdf's have a string as the font name.
|
||||
if (isString(fontName))
|
||||
fontName = new Name(fontName);
|
||||
assertWellFormed(isName(fontName), 'invalid font name');
|
||||
|
||||
var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
|
||||
if (fontFile) {
|
||||
fontFile = xref.fetchIfRef(fontFile);
|
||||
if (fontFile.dict) {
|
||||
var subtype = fontFile.dict.get('Subtype');
|
||||
if (subtype)
|
||||
subtype = subtype.name;
|
||||
|
||||
var length1 = fontFile.dict.get('Length1');
|
||||
if (!isInt(length1))
|
||||
length1 = xref.fetchIfRef(length1);
|
||||
|
||||
var length2 = fontFile.dict.get('Length2');
|
||||
if (!isInt(length2))
|
||||
length2 = xref.fetchIfRef(length2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -901,19 +925,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
|
||||
if (type.name === 'Type3') {
|
||||
properties.coded = true;
|
||||
// read char procs only if dependency is specified
|
||||
if (dependency) {
|
||||
var charProcs = xref.fetchIfRef(dict.get('CharProcs'));
|
||||
var fontResources = xref.fetchIfRef(dict.get('Resources')) ||
|
||||
resources;
|
||||
properties.resources = fontResources;
|
||||
properties.charProcIRQueues = {};
|
||||
for (var key in charProcs.map) {
|
||||
var glyphStream = xref.fetchIfRef(charProcs.map[key]);
|
||||
var queueObj = {};
|
||||
properties.charProcIRQueues[key] =
|
||||
this.getIRQueue(glyphStream, fontResources, queueObj, dependency);
|
||||
}
|
||||
var charProcs = dict.get('CharProcs').getAll();
|
||||
var fontResources = dict.get('Resources') || resources;
|
||||
properties.resources = fontResources;
|
||||
properties.charProcOperatorList = {};
|
||||
for (var key in charProcs) {
|
||||
var glyphStream = charProcs[key];
|
||||
properties.charProcOperatorList[key] =
|
||||
this.getOperatorList(glyphStream, fontResources, dependency);
|
||||
}
|
||||
}
|
||||
|
||||
|
2479
src/fonts.js
230
src/function.js
@ -10,7 +10,7 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
var CONSTRUCT_POSTSCRIPT = 4;
|
||||
|
||||
return {
|
||||
getSampleArray: function pdfFunctionGetSampleArray(size, outputSize, bps,
|
||||
getSampleArray: function PDFFunction_getSampleArray(size, outputSize, bps,
|
||||
str) {
|
||||
var length = 1;
|
||||
for (var i = 0, ii = size.length; i < ii; i++)
|
||||
@ -38,7 +38,7 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
return array;
|
||||
},
|
||||
|
||||
getIR: function pdfFunctionGetIR(xref, fn) {
|
||||
getIR: function PDFFunction_getIR(xref, fn) {
|
||||
var dict = fn.dict;
|
||||
if (!dict)
|
||||
dict = fn;
|
||||
@ -57,7 +57,7 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
return typeFn.call(this, fn, dict, xref);
|
||||
},
|
||||
|
||||
fromIR: function pdfFunctionFromIR(IR) {
|
||||
fromIR: function PDFFunction_fromIR(IR) {
|
||||
var type = IR[0];
|
||||
switch (type) {
|
||||
case CONSTRUCT_SAMPLED:
|
||||
@ -72,16 +72,16 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
parse: function pdfFunctionParse(xref, fn) {
|
||||
parse: function PDFFunction_parse(xref, fn) {
|
||||
var IR = this.getIR(xref, fn);
|
||||
return this.fromIR(IR);
|
||||
},
|
||||
|
||||
constructSampled: function pdfFunctionConstructSampled(str, dict) {
|
||||
constructSampled: function PDFFunction_constructSampled(str, dict) {
|
||||
function toMultiArray(arr) {
|
||||
var inputLength = arr.length;
|
||||
var outputLength = arr.length / 2;
|
||||
var out = new Array(outputLength);
|
||||
var out = [];
|
||||
var index = 0;
|
||||
for (var i = 0; i < inputLength; i += 2) {
|
||||
out[index] = [arr[i], arr[i + 1]];
|
||||
@ -125,114 +125,104 @@ 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];
|
||||
constructSampledFromIR: function PDFFunction_constructSampledFromIR(IR) {
|
||||
// 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;
|
||||
}
|
||||
},
|
||||
|
||||
constructInterpolated:
|
||||
function pdfFunctionConstructInterpolated(str, dict) {
|
||||
constructInterpolated: function PDFFunction_constructInterpolated(str,
|
||||
dict) {
|
||||
var c0 = dict.get('C0') || [0];
|
||||
var c1 = dict.get('C1') || [1];
|
||||
var n = dict.get('N');
|
||||
@ -249,7 +239,7 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
},
|
||||
|
||||
constructInterpolatedFromIR:
|
||||
function pdfFunctionconstructInterpolatedFromIR(IR) {
|
||||
function PDFFunction_constructInterpolatedFromIR(IR) {
|
||||
var c0 = IR[1];
|
||||
var diff = IR[2];
|
||||
var n = IR[3];
|
||||
@ -268,7 +258,7 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
||||
constructStiched: function PDFFunction_constructStiched(fn, dict, xref) {
|
||||
var domain = dict.get('Domain');
|
||||
|
||||
if (!domain)
|
||||
@ -278,18 +268,18 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
if (inputSize != 1)
|
||||
error('Bad domain for stiched function');
|
||||
|
||||
var fnRefs = xref.fetchIfRef(dict.get('Functions'));
|
||||
var fnRefs = dict.get('Functions');
|
||||
var fns = [];
|
||||
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
||||
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
||||
|
||||
var bounds = xref.fetchIfRef(dict.get('Bounds'));
|
||||
var encode = xref.fetchIfRef(dict.get('Encode'));
|
||||
var bounds = dict.get('Bounds');
|
||||
var encode = dict.get('Encode');
|
||||
|
||||
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
||||
},
|
||||
|
||||
constructStichedFromIR: function pdfFunctionConstructStichedFromIR(IR) {
|
||||
constructStichedFromIR: function PDFFunction_constructStichedFromIR(IR) {
|
||||
var domain = IR[1];
|
||||
var bounds = IR[2];
|
||||
var encode = IR[3];
|
||||
@ -335,7 +325,7 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
};
|
||||
},
|
||||
|
||||
constructPostScript: function pdfFunctionConstructPostScript(fn, dict,
|
||||
constructPostScript: function PDFFunction_constructPostScript(fn, dict,
|
||||
xref) {
|
||||
var domain = dict.get('Domain');
|
||||
var range = dict.get('Range');
|
||||
@ -353,8 +343,8 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
return [CONSTRUCT_POSTSCRIPT, domain, range, code];
|
||||
},
|
||||
|
||||
constructPostScriptFromIR:
|
||||
function pdfFunctionConstructPostScriptFromIR(IR) {
|
||||
constructPostScriptFromIR: function PDFFunction_constructPostScriptFromIR(
|
||||
IR) {
|
||||
var domain = IR[1];
|
||||
var range = IR[2];
|
||||
var code = IR[3];
|
||||
@ -374,7 +364,7 @@ var PDFFunction = (function PDFFunctionClosure() {
|
||||
return cache.get(key);
|
||||
|
||||
var stack = evaluator.execute(initialStack);
|
||||
var transformed = new Array(numOutputs);
|
||||
var transformed = [];
|
||||
for (i = numOutputs - 1; i >= 0; --i) {
|
||||
var out = stack.pop();
|
||||
var rangeIndex = 2 * i;
|
||||
@ -400,13 +390,13 @@ var FunctionCache = (function FunctionCacheClosure() {
|
||||
this.total = 0;
|
||||
}
|
||||
FunctionCache.prototype = {
|
||||
has: function has(key) {
|
||||
has: function FunctionCache_has(key) {
|
||||
return key in this.cache;
|
||||
},
|
||||
get: function get(key) {
|
||||
get: function FunctionCache_get(key) {
|
||||
return this.cache[key];
|
||||
},
|
||||
set: function set(key, value) {
|
||||
set: function FunctionCache_set(key, value) {
|
||||
if (this.total < MAX_CACHE_SIZE) {
|
||||
this.cache[key] = value;
|
||||
this.total++;
|
||||
@ -423,28 +413,28 @@ var PostScriptStack = (function PostScriptStackClosure() {
|
||||
}
|
||||
|
||||
PostScriptStack.prototype = {
|
||||
push: function push(value) {
|
||||
push: function PostScriptStack_push(value) {
|
||||
if (this.stack.length >= MAX_STACK_SIZE)
|
||||
error('PostScript function stack overflow.');
|
||||
this.stack.push(value);
|
||||
},
|
||||
pop: function pop() {
|
||||
pop: function PostScriptStack_pop() {
|
||||
if (this.stack.length <= 0)
|
||||
error('PostScript function stack underflow.');
|
||||
return this.stack.pop();
|
||||
},
|
||||
copy: function copy(n) {
|
||||
copy: function PostScriptStack_copy(n) {
|
||||
if (this.stack.length + n >= MAX_STACK_SIZE)
|
||||
error('PostScript function stack overflow.');
|
||||
var stack = this.stack;
|
||||
for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++)
|
||||
stack.push(stack[i]);
|
||||
},
|
||||
index: function index(n) {
|
||||
index: function PostScriptStack_index(n) {
|
||||
this.push(this.stack[this.stack.length - n - 1]);
|
||||
},
|
||||
// rotate the last n stack elements p times
|
||||
roll: function roll(n, p) {
|
||||
roll: function PostScriptStack_roll(n, p) {
|
||||
var stack = this.stack;
|
||||
var l = stack.length - n;
|
||||
var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
|
||||
@ -467,7 +457,7 @@ var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
|
||||
this.operands = operands;
|
||||
}
|
||||
PostScriptEvaluator.prototype = {
|
||||
execute: function execute(initialStack) {
|
||||
execute: function PostScriptEvaluator_execute(initialStack) {
|
||||
var stack = new PostScriptStack(initialStack);
|
||||
var counter = 0;
|
||||
var operators = this.operators;
|
||||
@ -701,31 +691,31 @@ var PostScriptParser = (function PostScriptParserClosure() {
|
||||
this.prev;
|
||||
}
|
||||
PostScriptParser.prototype = {
|
||||
nextToken: function nextToken() {
|
||||
nextToken: function PostScriptParser_nextToken() {
|
||||
this.prev = this.token;
|
||||
this.token = this.lexer.getToken();
|
||||
},
|
||||
accept: function accept(type) {
|
||||
accept: function PostScriptParser_accept(type) {
|
||||
if (this.token.type == type) {
|
||||
this.nextToken();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
expect: function expect(type) {
|
||||
expect: function PostScriptParser_expect(type) {
|
||||
if (this.accept(type))
|
||||
return true;
|
||||
error('Unexpected symbol: found ' + this.token.type + ' expected ' +
|
||||
type + '.');
|
||||
},
|
||||
parse: function parse() {
|
||||
parse: function PostScriptParser_parse() {
|
||||
this.nextToken();
|
||||
this.expect(PostScriptTokenTypes.LBRACE);
|
||||
this.parseBlock();
|
||||
this.expect(PostScriptTokenTypes.RBRACE);
|
||||
return this.operators;
|
||||
},
|
||||
parseBlock: function parseBlock() {
|
||||
parseBlock: function PostScriptParser_parseBlock() {
|
||||
while (true) {
|
||||
if (this.accept(PostScriptTokenTypes.NUMBER)) {
|
||||
this.operators.push(this.prev.value);
|
||||
@ -738,7 +728,7 @@ var PostScriptParser = (function PostScriptParserClosure() {
|
||||
}
|
||||
}
|
||||
},
|
||||
parseCondition: function parseCondition() {
|
||||
parseCondition: function PostScriptParser_parseCondition() {
|
||||
// Add two place holders that will be updated later
|
||||
var conditionLocation = this.operators.length;
|
||||
this.operators.push(null, null);
|
||||
@ -789,7 +779,7 @@ var PostScriptToken = (function PostScriptTokenClosure() {
|
||||
|
||||
var opCache = {};
|
||||
|
||||
PostScriptToken.getOperator = function getOperator(op) {
|
||||
PostScriptToken.getOperator = function PostScriptToken_getOperator(op) {
|
||||
var opValue = opCache[op];
|
||||
if (opValue)
|
||||
return opValue;
|
||||
@ -812,7 +802,7 @@ var PostScriptLexer = (function PostScriptLexerClosure() {
|
||||
this.stream = stream;
|
||||
}
|
||||
PostScriptLexer.prototype = {
|
||||
getToken: function getToken() {
|
||||
getToken: function PostScriptLexer_getToken() {
|
||||
var s = '';
|
||||
var ch;
|
||||
var comment = false;
|
||||
@ -862,7 +852,7 @@ var PostScriptLexer = (function PostScriptLexerClosure() {
|
||||
return PostScriptToken.getOperator(str);
|
||||
}
|
||||
},
|
||||
getNumber: function getNumber(ch) {
|
||||
getNumber: function PostScriptLexer_getNumber(ch) {
|
||||
var str = ch;
|
||||
var stream = this.stream;
|
||||
while (true) {
|
||||
|
@ -1508,27 +1508,7 @@ var GlyphsUnicode = {
|
||||
dalet: 0x05D3,
|
||||
daletdagesh: 0xFB33,
|
||||
daletdageshhebrew: 0xFB33,
|
||||
dalethatafpatah: 0x05D305B2,
|
||||
dalethatafpatahhebrew: 0x05D305B2,
|
||||
dalethatafsegol: 0x05D305B1,
|
||||
dalethatafsegolhebrew: 0x05D305B1,
|
||||
dalethebrew: 0x05D3,
|
||||
dalethiriq: 0x05D305B4,
|
||||
dalethiriqhebrew: 0x05D305B4,
|
||||
daletholam: 0x05D305B9,
|
||||
daletholamhebrew: 0x05D305B9,
|
||||
daletpatah: 0x05D305B7,
|
||||
daletpatahhebrew: 0x05D305B7,
|
||||
daletqamats: 0x05D305B8,
|
||||
daletqamatshebrew: 0x05D305B8,
|
||||
daletqubuts: 0x05D305BB,
|
||||
daletqubutshebrew: 0x05D305BB,
|
||||
daletsegol: 0x05D305B6,
|
||||
daletsegolhebrew: 0x05D305B6,
|
||||
daletsheva: 0x05D305B0,
|
||||
daletshevahebrew: 0x05D305B0,
|
||||
dalettsere: 0x05D305B5,
|
||||
dalettserehebrew: 0x05D305B5,
|
||||
dalfinalarabic: 0xFEAA,
|
||||
dammaarabic: 0x064F,
|
||||
dammalowarabic: 0x064F,
|
||||
@ -1845,10 +1825,6 @@ var GlyphsUnicode = {
|
||||
finalkafdagesh: 0xFB3A,
|
||||
finalkafdageshhebrew: 0xFB3A,
|
||||
finalkafhebrew: 0x05DA,
|
||||
finalkafqamats: 0x05DA05B8,
|
||||
finalkafqamatshebrew: 0x05DA05B8,
|
||||
finalkafsheva: 0x05DA05B0,
|
||||
finalkafshevahebrew: 0x05DA05B0,
|
||||
finalmem: 0x05DD,
|
||||
finalmemhebrew: 0x05DD,
|
||||
finalnun: 0x05DF,
|
||||
@ -2037,14 +2013,7 @@ var GlyphsUnicode = {
|
||||
hakatakanahalfwidth: 0xFF8A,
|
||||
halantgurmukhi: 0x0A4D,
|
||||
hamzaarabic: 0x0621,
|
||||
hamzadammaarabic: 0x0621064F,
|
||||
hamzadammatanarabic: 0x0621064C,
|
||||
hamzafathaarabic: 0x0621064E,
|
||||
hamzafathatanarabic: 0x0621064B,
|
||||
hamzalowarabic: 0x0621,
|
||||
hamzalowkasraarabic: 0x06210650,
|
||||
hamzalowkasratanarabic: 0x0621064D,
|
||||
hamzasukunarabic: 0x06210652,
|
||||
hangulfiller: 0x3164,
|
||||
hardsigncyrillic: 0x044A,
|
||||
harpoonleftbarbup: 0x21BC,
|
||||
@ -2476,10 +2445,6 @@ var GlyphsUnicode = {
|
||||
lameddagesh: 0xFB3C,
|
||||
lameddageshhebrew: 0xFB3C,
|
||||
lamedhebrew: 0x05DC,
|
||||
lamedholam: 0x05DC05B9,
|
||||
lamedholamdagesh: '05DC 05B9 05BC',
|
||||
lamedholamdageshhebrew: '05DC 05B9 05BC',
|
||||
lamedholamhebrew: 0x05DC05B9,
|
||||
lamfinalarabic: 0xFEDE,
|
||||
lamhahinitialarabic: 0xFCCA,
|
||||
laminitialarabic: 0xFEDF,
|
||||
@ -2489,8 +2454,6 @@ var GlyphsUnicode = {
|
||||
lammedialarabic: 0xFEE0,
|
||||
lammeemhahinitialarabic: 0xFD88,
|
||||
lammeeminitialarabic: 0xFCCC,
|
||||
lammeemjeeminitialarabic: 'FEDF FEE4 FEA0',
|
||||
lammeemkhahinitialarabic: 'FEDF FEE4 FEA8',
|
||||
largecircle: 0x25EF,
|
||||
lbar: 0x019A,
|
||||
lbelt: 0x026C,
|
||||
@ -2787,7 +2750,6 @@ var GlyphsUnicode = {
|
||||
noonfinalarabic: 0xFEE6,
|
||||
noonghunnaarabic: 0x06BA,
|
||||
noonghunnafinalarabic: 0xFB9F,
|
||||
noonhehinitialarabic: 0xFEE7FEEC,
|
||||
nooninitialarabic: 0xFEE7,
|
||||
noonjeeminitialarabic: 0xFCD2,
|
||||
noonjeemisolatedarabic: 0xFC4B,
|
||||
@ -3159,27 +3121,7 @@ var GlyphsUnicode = {
|
||||
qof: 0x05E7,
|
||||
qofdagesh: 0xFB47,
|
||||
qofdageshhebrew: 0xFB47,
|
||||
qofhatafpatah: 0x05E705B2,
|
||||
qofhatafpatahhebrew: 0x05E705B2,
|
||||
qofhatafsegol: 0x05E705B1,
|
||||
qofhatafsegolhebrew: 0x05E705B1,
|
||||
qofhebrew: 0x05E7,
|
||||
qofhiriq: 0x05E705B4,
|
||||
qofhiriqhebrew: 0x05E705B4,
|
||||
qofholam: 0x05E705B9,
|
||||
qofholamhebrew: 0x05E705B9,
|
||||
qofpatah: 0x05E705B7,
|
||||
qofpatahhebrew: 0x05E705B7,
|
||||
qofqamats: 0x05E705B8,
|
||||
qofqamatshebrew: 0x05E705B8,
|
||||
qofqubuts: 0x05E705BB,
|
||||
qofqubutshebrew: 0x05E705BB,
|
||||
qofsegol: 0x05E705B6,
|
||||
qofsegolhebrew: 0x05E705B6,
|
||||
qofsheva: 0x05E705B0,
|
||||
qofshevahebrew: 0x05E705B0,
|
||||
qoftsere: 0x05E705B5,
|
||||
qoftserehebrew: 0x05E705B5,
|
||||
qparen: 0x24AC,
|
||||
quarternote: 0x2669,
|
||||
qubuts: 0x05BB,
|
||||
@ -3253,32 +3195,11 @@ var GlyphsUnicode = {
|
||||
reharmenian: 0x0580,
|
||||
rehfinalarabic: 0xFEAE,
|
||||
rehiragana: 0x308C,
|
||||
rehyehaleflamarabic: '0631 FEF3 FE8E 0644',
|
||||
rekatakana: 0x30EC,
|
||||
rekatakanahalfwidth: 0xFF9A,
|
||||
resh: 0x05E8,
|
||||
reshdageshhebrew: 0xFB48,
|
||||
reshhatafpatah: 0x05E805B2,
|
||||
reshhatafpatahhebrew: 0x05E805B2,
|
||||
reshhatafsegol: 0x05E805B1,
|
||||
reshhatafsegolhebrew: 0x05E805B1,
|
||||
reshhebrew: 0x05E8,
|
||||
reshhiriq: 0x05E805B4,
|
||||
reshhiriqhebrew: 0x05E805B4,
|
||||
reshholam: 0x05E805B9,
|
||||
reshholamhebrew: 0x05E805B9,
|
||||
reshpatah: 0x05E805B7,
|
||||
reshpatahhebrew: 0x05E805B7,
|
||||
reshqamats: 0x05E805B8,
|
||||
reshqamatshebrew: 0x05E805B8,
|
||||
reshqubuts: 0x05E805BB,
|
||||
reshqubutshebrew: 0x05E805BB,
|
||||
reshsegol: 0x05E805B6,
|
||||
reshsegolhebrew: 0x05E805B6,
|
||||
reshsheva: 0x05E805B0,
|
||||
reshshevahebrew: 0x05E805B0,
|
||||
reshtsere: 0x05E805B5,
|
||||
reshtserehebrew: 0x05E805B5,
|
||||
reversedtilde: 0x223D,
|
||||
reviahebrew: 0x0597,
|
||||
reviamugrashhebrew: 0x0597,
|
||||
@ -3477,7 +3398,6 @@ var GlyphsUnicode = {
|
||||
shaddadammaarabic: 0xFC61,
|
||||
shaddadammatanarabic: 0xFC5E,
|
||||
shaddafathaarabic: 0xFC60,
|
||||
shaddafathatanarabic: 0x0651064B,
|
||||
shaddakasraarabic: 0xFC62,
|
||||
shaddakasratanarabic: 0xFC5F,
|
||||
shade: 0x2592,
|
||||
@ -3674,7 +3594,6 @@ var GlyphsUnicode = {
|
||||
tchehfinalarabic: 0xFB7B,
|
||||
tchehinitialarabic: 0xFB7C,
|
||||
tchehmedialarabic: 0xFB7D,
|
||||
tchehmeeminitialarabic: 0xFB7CFEE4,
|
||||
tcircle: 0x24E3,
|
||||
tcircumflexbelow: 0x1E71,
|
||||
tcommaaccent: 0x0163,
|
||||
|
26
src/image.js
@ -94,7 +94,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
}
|
||||
}
|
||||
|
||||
var mask = xref.fetchIfRef(dict.get('Mask'));
|
||||
var mask = dict.get('Mask');
|
||||
|
||||
if (mask) {
|
||||
TODO('masked images');
|
||||
@ -106,8 +106,8 @@ var PDFImage = (function PDFImageClosure() {
|
||||
* Handles processing of image data and calls the callback with an argument
|
||||
* of a PDFImage when the image is ready to be used.
|
||||
*/
|
||||
PDFImage.buildImage = function buildImage(callback, handler, xref, res,
|
||||
image, inline) {
|
||||
PDFImage.buildImage = function PDFImage_buildImage(callback, handler, xref,
|
||||
res, image, inline) {
|
||||
var imageDataPromise = new Promise();
|
||||
var smaskPromise = new Promise();
|
||||
// The image data and smask data may not be ready yet, wait till both are
|
||||
@ -120,7 +120,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
|
||||
handleImageData(handler, xref, res, image, imageDataPromise);
|
||||
|
||||
var smask = xref.fetchIfRef(image.dict.get('SMask'));
|
||||
var smask = image.dict.get('SMask');
|
||||
if (smask)
|
||||
handleImageData(handler, xref, res, smask, smaskPromise);
|
||||
else
|
||||
@ -139,7 +139,8 @@ var PDFImage = (function PDFImageClosure() {
|
||||
* @param {Number} h2 New height.
|
||||
* @return {TypedArray} Resized image data.
|
||||
*/
|
||||
PDFImage.resize = function resize(pixels, bpc, components, w1, h1, w2, h2) {
|
||||
PDFImage.resize = function PDFImage_resize(pixels, bpc, components,
|
||||
w1, h1, w2, h2) {
|
||||
var length = w2 * h2 * components;
|
||||
var temp = bpc <= 8 ? new Uint8Array(length) :
|
||||
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
||||
@ -177,7 +178,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
return this.height;
|
||||
return Math.max(this.height, this.smask.height);
|
||||
},
|
||||
getComponents: function getComponents(buffer) {
|
||||
getComponents: function PDFImage_getComponents(buffer) {
|
||||
var bpc = this.bpc;
|
||||
var needsDecode = this.needsDecode;
|
||||
var decodeMap = this.decode;
|
||||
@ -265,7 +266,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
}
|
||||
return output;
|
||||
},
|
||||
getOpacity: function getOpacity(width, height) {
|
||||
getOpacity: function PDFImage_getOpacity(width, height) {
|
||||
var smask = this.smask;
|
||||
var originalWidth = this.width;
|
||||
var originalHeight = this.height;
|
||||
@ -285,7 +286,8 @@ var PDFImage = (function PDFImageClosure() {
|
||||
}
|
||||
return buf;
|
||||
},
|
||||
applyStencilMask: function applyStencilMask(buffer, inverseDecode) {
|
||||
applyStencilMask: function PDFImage_applyStencilMask(buffer,
|
||||
inverseDecode) {
|
||||
var width = this.width, height = this.height;
|
||||
var bitStrideLength = (width + 7) >> 3;
|
||||
var imgArray = this.getImageBytes(bitStrideLength * height);
|
||||
@ -308,7 +310,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
}
|
||||
}
|
||||
},
|
||||
fillRgbaBuffer: function fillRgbaBuffer(buffer, width, height) {
|
||||
fillRgbaBuffer: function PDFImage_fillRgbaBuffer(buffer, width, height) {
|
||||
var numComps = this.numComps;
|
||||
var originalWidth = this.width;
|
||||
var originalHeight = this.height;
|
||||
@ -335,7 +337,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
buffer[i + 3] = opacity[opacityPos++];
|
||||
}
|
||||
},
|
||||
fillGrayBuffer: function fillGrayBuffer(buffer) {
|
||||
fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) {
|
||||
var numComps = this.numComps;
|
||||
if (numComps != 1)
|
||||
error('Reading gray scale from a color image: ' + numComps);
|
||||
@ -355,7 +357,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
for (var i = 0; i < length; ++i)
|
||||
buffer[i] = (scale * comps[i]) | 0;
|
||||
},
|
||||
getImageBytes: function getImageBytes(length) {
|
||||
getImageBytes: function PDFImage_getImageBytes(length) {
|
||||
this.image.reset();
|
||||
return this.image.getBytes(length);
|
||||
}
|
||||
@ -365,7 +367,7 @@ var PDFImage = (function PDFImageClosure() {
|
||||
|
||||
function loadJpegStream(id, imageData, objs) {
|
||||
var img = new Image();
|
||||
img.onload = (function jpegImageLoaderOnload() {
|
||||
img.onload = (function loadJpegStream_onloadClosure() {
|
||||
objs.resolve(id, img);
|
||||
});
|
||||
img.src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||
|
2250
src/jpx.js
66
src/metadata.js
Normal file
@ -0,0 +1,66 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
var Metadata = PDFJS.Metadata = (function MetadataClosure() {
|
||||
function Metadata(meta) {
|
||||
if (typeof meta === 'string') {
|
||||
var parser = new DOMParser();
|
||||
meta = parser.parseFromString(meta, 'application/xml');
|
||||
} else if (!(meta instanceof Document)) {
|
||||
error('Metadata: Invalid metadata object');
|
||||
}
|
||||
|
||||
this.metaDocument = meta;
|
||||
this.metadata = {};
|
||||
this.parse();
|
||||
}
|
||||
|
||||
Metadata.prototype = {
|
||||
parse: function Metadata_parse() {
|
||||
var doc = this.metaDocument;
|
||||
var rdf = doc.documentElement;
|
||||
|
||||
if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in <xmpmeta>
|
||||
rdf = rdf.firstChild;
|
||||
while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf')
|
||||
rdf = rdf.nextSibling;
|
||||
}
|
||||
|
||||
var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null;
|
||||
if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes())
|
||||
return;
|
||||
|
||||
var childNodes = rdf.childNodes, desc, namespace, entries, entry;
|
||||
|
||||
for (var i = 0, length = childNodes.length; i < length; i++) {
|
||||
desc = childNodes[i];
|
||||
if (desc.nodeName.toLowerCase() !== 'rdf:description')
|
||||
continue;
|
||||
|
||||
entries = [];
|
||||
for (var ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
|
||||
if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text')
|
||||
entries.push(desc.childNodes[ii]);
|
||||
}
|
||||
|
||||
for (ii = 0, iLength = entries.length; ii < iLength; ii++) {
|
||||
var entry = entries[ii];
|
||||
var name = entry.nodeName.toLowerCase();
|
||||
this.metadata[name] = entry.textContent.trim();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get: function Metadata_get(name) {
|
||||
return this.metadata[name] || null;
|
||||
},
|
||||
|
||||
has: function Metadata_has(name) {
|
||||
return typeof this.metadata[name] !== 'undefined';
|
||||
}
|
||||
};
|
||||
|
||||
return Metadata;
|
||||
})();
|
312
src/obj.js
@ -22,7 +22,7 @@ var Cmd = (function CmdClosure() {
|
||||
|
||||
var cmdCache = {};
|
||||
|
||||
Cmd.get = function cmdGet(cmd) {
|
||||
Cmd.get = function Cmd_get(cmd) {
|
||||
var cmdValue = cmdCache[cmd];
|
||||
if (cmdValue)
|
||||
return cmdValue;
|
||||
@ -34,36 +34,52 @@ var Cmd = (function CmdClosure() {
|
||||
})();
|
||||
|
||||
var Dict = (function DictClosure() {
|
||||
function Dict() {
|
||||
// xref is optional
|
||||
function Dict(xref) {
|
||||
// Map should only be used internally, use functions below to access.
|
||||
this.map = Object.create(null);
|
||||
this.xref = xref;
|
||||
}
|
||||
|
||||
Dict.prototype = {
|
||||
get: function dictGet(key1, key2, key3) {
|
||||
// automatically dereferences Ref objects
|
||||
get: function Dict_get(key1, key2, key3) {
|
||||
var value;
|
||||
var xref = this.xref;
|
||||
if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map ||
|
||||
typeof key2 == 'undefined') {
|
||||
return value;
|
||||
return xref ? this.xref.fetchIfRef(value) : value;
|
||||
}
|
||||
if (typeof (value = this.map[key2]) != 'undefined' || key2 in this.map ||
|
||||
typeof key3 == 'undefined') {
|
||||
return value;
|
||||
return xref ? this.xref.fetchIfRef(value) : value;
|
||||
}
|
||||
|
||||
return this.map[key3] || null;
|
||||
value = this.map[key3] || null;
|
||||
return xref ? this.xref.fetchIfRef(value) : value;
|
||||
},
|
||||
// no dereferencing
|
||||
getRaw: function Dict_getRaw(key) {
|
||||
return this.map[key];
|
||||
},
|
||||
// creates new map and dereferences all Refs
|
||||
getAll: function Dict_getAll() {
|
||||
var all = {};
|
||||
for (var key in this.map)
|
||||
all[key] = this.get(key);
|
||||
return all;
|
||||
},
|
||||
|
||||
set: function dictSet(key, value) {
|
||||
set: function Dict_set(key, value) {
|
||||
this.map[key] = value;
|
||||
},
|
||||
|
||||
has: function dictHas(key) {
|
||||
has: function Dict_has(key) {
|
||||
return key in this.map;
|
||||
},
|
||||
|
||||
forEach: function dictForEach(callback) {
|
||||
forEach: function Dict_forEach(callback) {
|
||||
for (var key in this.map) {
|
||||
callback(key, this.map[key]);
|
||||
callback(key, this.get(key));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -90,11 +106,11 @@ var RefSet = (function RefSetClosure() {
|
||||
}
|
||||
|
||||
RefSet.prototype = {
|
||||
has: function refSetHas(ref) {
|
||||
has: function RefSet_has(ref) {
|
||||
return !!this.dict['R' + ref.num + '.' + ref.gen];
|
||||
},
|
||||
|
||||
put: function refSetPut(ref) {
|
||||
put: function RefSet_put(ref) {
|
||||
this.dict['R' + ref.num + '.' + ref.gen] = ref;
|
||||
}
|
||||
};
|
||||
@ -111,20 +127,33 @@ var Catalog = (function CatalogClosure() {
|
||||
}
|
||||
|
||||
Catalog.prototype = {
|
||||
get metadata() {
|
||||
var stream = this.catDict.get('Metadata');
|
||||
var metadata;
|
||||
if (stream && isDict(stream.dict)) {
|
||||
var type = stream.dict.get('Type');
|
||||
var subtype = stream.dict.get('Subtype');
|
||||
|
||||
if (isName(type) && isName(subtype) &&
|
||||
type.name === 'Metadata' && subtype.name === 'XML') {
|
||||
metadata = stringToPDFString(bytesToString(stream.getBytes()));
|
||||
}
|
||||
}
|
||||
|
||||
return shadow(this, 'metadata', metadata);
|
||||
},
|
||||
get toplevelPagesDict() {
|
||||
var pagesObj = this.catDict.get('Pages');
|
||||
assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference');
|
||||
var xrefObj = this.xref.fetch(pagesObj);
|
||||
assertWellFormed(isDict(xrefObj), 'invalid top-level pages dictionary');
|
||||
assertWellFormed(isDict(pagesObj), 'invalid top-level pages dictionary');
|
||||
// shadow the prototype getter
|
||||
return shadow(this, 'toplevelPagesDict', xrefObj);
|
||||
return shadow(this, 'toplevelPagesDict', pagesObj);
|
||||
},
|
||||
get documentOutline() {
|
||||
var xref = this.xref;
|
||||
var obj = xref.fetchIfRef(this.catDict.get('Outlines'));
|
||||
var obj = this.catDict.get('Outlines');
|
||||
var root = { items: [] };
|
||||
if (isDict(obj)) {
|
||||
obj = obj.get('First');
|
||||
obj = obj.getRaw('First');
|
||||
var processed = new RefSet();
|
||||
if (isRef(obj)) {
|
||||
var queue = [{obj: obj, parent: root}];
|
||||
@ -133,18 +162,20 @@ var Catalog = (function CatalogClosure() {
|
||||
processed.put(obj);
|
||||
while (queue.length > 0) {
|
||||
var i = queue.shift();
|
||||
var outlineDict = xref.fetch(i.obj);
|
||||
var outlineDict = xref.fetchIfRef(i.obj);
|
||||
if (outlineDict === null)
|
||||
continue;
|
||||
if (!outlineDict.has('Title'))
|
||||
error('Invalid outline item');
|
||||
var dest = outlineDict.get('A');
|
||||
if (dest)
|
||||
dest = xref.fetchIfRef(dest).get('D');
|
||||
dest = dest.get('D');
|
||||
else if (outlineDict.has('Dest')) {
|
||||
dest = outlineDict.get('Dest');
|
||||
dest = outlineDict.getRaw('Dest');
|
||||
if (isName(dest))
|
||||
dest = dest.name;
|
||||
}
|
||||
var title = xref.fetchIfRef(outlineDict.get('Title'));
|
||||
var title = outlineDict.get('Title');
|
||||
var outlineItem = {
|
||||
dest: dest,
|
||||
title: stringToPDFString(title),
|
||||
@ -155,12 +186,12 @@ var Catalog = (function CatalogClosure() {
|
||||
items: []
|
||||
};
|
||||
i.parent.items.push(outlineItem);
|
||||
obj = outlineDict.get('First');
|
||||
obj = outlineDict.getRaw('First');
|
||||
if (isRef(obj) && !processed.has(obj)) {
|
||||
queue.push({obj: obj, parent: outlineItem});
|
||||
processed.put(obj);
|
||||
}
|
||||
obj = outlineDict.get('Next');
|
||||
obj = outlineDict.getRaw('Next');
|
||||
if (isRef(obj) && !processed.has(obj)) {
|
||||
queue.push({obj: obj, parent: i.parent});
|
||||
processed.put(obj);
|
||||
@ -180,7 +211,7 @@ var Catalog = (function CatalogClosure() {
|
||||
// shadow the prototype getter
|
||||
return shadow(this, 'num', obj);
|
||||
},
|
||||
traverseKids: function catalogTraverseKids(pagesDict) {
|
||||
traverseKids: function Catalog_traverseKids(pagesDict) {
|
||||
var pageCache = this.pageCache;
|
||||
var kids = pagesDict.get('Kids');
|
||||
assertWellFormed(isArray(kids),
|
||||
@ -188,7 +219,7 @@ var Catalog = (function CatalogClosure() {
|
||||
for (var i = 0, ii = kids.length; i < ii; ++i) {
|
||||
var kid = kids[i];
|
||||
assertWellFormed(isRef(kid),
|
||||
'page dictionary kid is not a reference');
|
||||
'page dictionary kid is not a reference');
|
||||
var obj = this.xref.fetch(kid);
|
||||
if (isDict(obj, 'Page') || (isDict(obj) && !obj.has('Kids'))) {
|
||||
pageCache.push(new Page(this.xref, pageCache.length, obj, kid));
|
||||
@ -202,8 +233,7 @@ var Catalog = (function CatalogClosure() {
|
||||
}
|
||||
},
|
||||
get destinations() {
|
||||
function fetchDestination(xref, ref) {
|
||||
var dest = xref.fetchIfRef(ref);
|
||||
function fetchDestination(dest) {
|
||||
return isDict(dest) ? dest.get('D') : dest;
|
||||
}
|
||||
|
||||
@ -211,16 +241,16 @@ var Catalog = (function CatalogClosure() {
|
||||
var dests = {}, nameTreeRef, nameDictionaryRef;
|
||||
var obj = this.catDict.get('Names');
|
||||
if (obj)
|
||||
nameTreeRef = xref.fetchIfRef(obj).get('Dests');
|
||||
nameTreeRef = obj.getRaw('Dests');
|
||||
else if (this.catDict.has('Dests'))
|
||||
nameDictionaryRef = this.catDict.get('Dests');
|
||||
|
||||
if (nameDictionaryRef) {
|
||||
// reading simple destination dictionary
|
||||
obj = xref.fetchIfRef(nameDictionaryRef);
|
||||
obj = nameDictionaryRef;
|
||||
obj.forEach(function catalogForEach(key, value) {
|
||||
if (!value) return;
|
||||
dests[key] = fetchDestination(xref, value);
|
||||
dests[key] = fetchDestination(value);
|
||||
});
|
||||
}
|
||||
if (nameTreeRef) {
|
||||
@ -244,13 +274,13 @@ var Catalog = (function CatalogClosure() {
|
||||
}
|
||||
var names = obj.get('Names');
|
||||
for (i = 0, n = names.length; i < n; i += 2) {
|
||||
dests[names[i]] = fetchDestination(xref, names[i + 1]);
|
||||
dests[names[i]] = fetchDestination(xref.fetchIfRef(names[i + 1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return shadow(this, 'destinations', dests);
|
||||
},
|
||||
getPage: function catalogGetPage(n) {
|
||||
getPage: function Catalog_getPage(n) {
|
||||
var pageCache = this.pageCache;
|
||||
if (!pageCache) {
|
||||
pageCache = this.pageCache = [];
|
||||
@ -269,6 +299,7 @@ var XRef = (function XRefClosure() {
|
||||
this.entries = [];
|
||||
this.xrefstms = {};
|
||||
var trailerDict = this.readXRef(startXRef);
|
||||
trailerDict.xref = this;
|
||||
this.trailer = trailerDict;
|
||||
// prepare the XRef cache
|
||||
this.cache = [];
|
||||
@ -276,89 +307,84 @@ var XRef = (function XRefClosure() {
|
||||
var encrypt = trailerDict.get('Encrypt');
|
||||
if (encrypt) {
|
||||
var fileId = trailerDict.get('ID');
|
||||
this.encrypt = new CipherTransformFactory(this.fetch(encrypt),
|
||||
this.encrypt = new CipherTransformFactory(encrypt,
|
||||
fileId[0] /*, password */);
|
||||
}
|
||||
|
||||
// get the root dictionary (catalog) object
|
||||
if (!isRef(this.root = trailerDict.get('Root')))
|
||||
if (!(this.root = trailerDict.get('Root')))
|
||||
error('Invalid root reference');
|
||||
}
|
||||
|
||||
XRef.prototype = {
|
||||
readXRefTable: function readXRefTable(parser) {
|
||||
readXRefTable: function XRef_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;
|
||||
},
|
||||
readXRefStream: function readXRefStream(stream) {
|
||||
readXRefStream: function XRef_readXRefStream(stream) {
|
||||
var streamParameters = stream.parameters;
|
||||
var byteWidths = streamParameters.get('W');
|
||||
var range = streamParameters.get('Index');
|
||||
@ -407,12 +433,9 @@ var XRef = (function XRefClosure() {
|
||||
}
|
||||
range.splice(0, 2);
|
||||
}
|
||||
var prev = streamParameters.get('Prev');
|
||||
if (isInt(prev))
|
||||
this.readXRef(prev);
|
||||
return streamParameters;
|
||||
},
|
||||
indexObjects: function indexObjects() {
|
||||
indexObjects: function XRef_indexObjects() {
|
||||
// Simple scan through the PDF content to find objects,
|
||||
// trailers and XRef streams.
|
||||
function readToken(data, offset) {
|
||||
@ -504,7 +527,7 @@ var XRef = (function XRefClosure() {
|
||||
var dict;
|
||||
for (var i = 0, ii = trailers.length; i < ii; ++i) {
|
||||
stream.pos = trailers[i];
|
||||
var parser = new Parser(new Lexer(stream), true);
|
||||
var parser = new Parser(new Lexer(stream), true, null);
|
||||
var obj = parser.getObj();
|
||||
if (!isCmd(obj, 'trailer'))
|
||||
continue;
|
||||
@ -520,46 +543,73 @@ var XRef = (function XRefClosure() {
|
||||
return dict;
|
||||
// nothing helps
|
||||
error('Invalid PDF structure');
|
||||
return null;
|
||||
},
|
||||
readXRef: function readXref(startXRef) {
|
||||
readXRef: function XRef_readXRef(startXRef) {
|
||||
var stream = this.stream;
|
||||
stream.pos = startXRef;
|
||||
|
||||
try {
|
||||
var parser = new Parser(new Lexer(stream), true);
|
||||
var parser = new Parser(new Lexer(stream), true, null);
|
||||
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');
|
||||
return this.indexObjects();
|
||||
},
|
||||
getEntry: function xRefGetEntry(i) {
|
||||
getEntry: function XRef_getEntry(i) {
|
||||
var e = this.entries[i];
|
||||
if (e === null)
|
||||
return null;
|
||||
return e.free ? null : e; // returns null is the entry is free
|
||||
},
|
||||
fetchIfRef: function xRefFetchIfRef(obj) {
|
||||
fetchIfRef: function XRef_fetchIfRef(obj) {
|
||||
if (!isRef(obj))
|
||||
return obj;
|
||||
return this.fetch(obj);
|
||||
},
|
||||
fetch: function xRefFetch(ref, suppressEncryption) {
|
||||
fetch: function XRef_fetch(ref, suppressEncryption) {
|
||||
assertWellFormed(isRef(ref), 'ref object is not a reference');
|
||||
var num = ref.num;
|
||||
if (num in this.cache)
|
||||
return this.cache[num];
|
||||
@ -574,7 +624,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();
|
||||
@ -621,7 +671,7 @@ var XRef = (function XRefClosure() {
|
||||
if (!isInt(first) || !isInt(n)) {
|
||||
error('invalid first and n parameters for ObjStm stream');
|
||||
}
|
||||
parser = new Parser(new Lexer(stream), false);
|
||||
parser = new Parser(new Lexer(stream), false, this);
|
||||
var i, entries = [], nums = [];
|
||||
// read the object numbers to populate cache
|
||||
for (i = 0; i < n; ++i) {
|
||||
@ -646,8 +696,8 @@ var XRef = (function XRefClosure() {
|
||||
}
|
||||
return e;
|
||||
},
|
||||
getCatalogObj: function xRefGetCatalogObj() {
|
||||
return this.fetch(this.root);
|
||||
getCatalogObj: function XRef_getCatalogObj() {
|
||||
return this.root;
|
||||
}
|
||||
};
|
||||
|
||||
@ -673,7 +723,7 @@ var PDFObjects = (function PDFObjectsClosure() {
|
||||
* Ensures there is an object defined for `objId`. Stores `data` on the
|
||||
* object *if* it is created.
|
||||
*/
|
||||
ensureObj: function pdfObjectsEnsureObj(objId, data) {
|
||||
ensureObj: function PDFObjects_ensureObj(objId, data) {
|
||||
if (this.objs[objId])
|
||||
return this.objs[objId];
|
||||
return this.objs[objId] = new Promise(objId, data);
|
||||
@ -688,7 +738,7 @@ var PDFObjects = (function PDFObjectsClosure() {
|
||||
* function and the object is already resolved, the callback gets called
|
||||
* right away.
|
||||
*/
|
||||
get: function pdfObjectsGet(objId, callback) {
|
||||
get: function PDFObjects_get(objId, callback) {
|
||||
// If there is a callback, then the get can be async and the object is
|
||||
// not required to be resolved right now
|
||||
if (callback) {
|
||||
@ -702,18 +752,16 @@ 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;
|
||||
return null;
|
||||
} else {
|
||||
return obj.data;
|
||||
}
|
||||
if (!obj || !obj.isResolved)
|
||||
error('Requesting object that isn\'t resolved yet ' + objId);
|
||||
|
||||
return obj.data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves the object `objId` with optional `data`.
|
||||
*/
|
||||
resolve: function pdfObjectsResolve(objId, data) {
|
||||
resolve: function PDFObjects_resolve(objId, data) {
|
||||
var objs = this.objs;
|
||||
|
||||
// In case there is a promise already on this object, just resolve it.
|
||||
@ -724,11 +772,11 @@ var PDFObjects = (function PDFObjectsClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
onData: function pdfObjectsOnData(objId, callback) {
|
||||
onData: function PDFObjects_onData(objId, callback) {
|
||||
this.ensureObj(objId).onData(callback);
|
||||
},
|
||||
|
||||
isResolved: function pdfObjectsIsResolved(objId) {
|
||||
isResolved: function PDFObjects_isResolved(objId) {
|
||||
var objs = this.objs;
|
||||
if (!objs[objId]) {
|
||||
return false;
|
||||
@ -737,7 +785,7 @@ var PDFObjects = (function PDFObjectsClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
hasData: function pdfObjectsHasData(objId) {
|
||||
hasData: function PDFObjects_hasData(objId) {
|
||||
var objs = this.objs;
|
||||
if (!objs[objId]) {
|
||||
return false;
|
||||
@ -749,7 +797,7 @@ var PDFObjects = (function PDFObjectsClosure() {
|
||||
/**
|
||||
* Sets the data of an object but *doesn't* resolve it.
|
||||
*/
|
||||
setData: function pdfObjectsSetData(objId, data) {
|
||||
setData: function PDFObjects_setData(objId, data) {
|
||||
// Watchout! If you call `this.ensureObj(objId, data)` you're going to
|
||||
// create a *resolved* promise which shouldn't be the case!
|
||||
this.ensureObj(objId).data = data;
|
||||
|
114
src/parser.js
@ -19,11 +19,11 @@ var Parser = (function ParserClosure() {
|
||||
}
|
||||
|
||||
Parser.prototype = {
|
||||
refill: function parserRefill() {
|
||||
refill: function Parser_refill() {
|
||||
this.buf1 = this.lexer.getObj();
|
||||
this.buf2 = this.lexer.getObj();
|
||||
},
|
||||
shift: function parserShift() {
|
||||
shift: function Parser_shift() {
|
||||
if (isCmd(this.buf2, 'ID')) {
|
||||
this.buf1 = this.buf2;
|
||||
this.buf2 = null;
|
||||
@ -34,7 +34,7 @@ var Parser = (function ParserClosure() {
|
||||
this.buf2 = this.lexer.getObj();
|
||||
}
|
||||
},
|
||||
getObj: function parserGetObj(cipherTransform) {
|
||||
getObj: function Parser_getObj(cipherTransform) {
|
||||
if (isCmd(this.buf1, 'BI')) { // inline image
|
||||
this.shift();
|
||||
return this.makeInlineImage(cipherTransform);
|
||||
@ -51,17 +51,16 @@ var Parser = (function ParserClosure() {
|
||||
}
|
||||
if (isCmd(this.buf1, '<<')) { // dictionary or stream
|
||||
this.shift();
|
||||
var dict = new Dict();
|
||||
var dict = new Dict(this.xref);
|
||||
while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) {
|
||||
if (!isName(this.buf1)) {
|
||||
if (!isName(this.buf1))
|
||||
error('Dictionary key must be a name object');
|
||||
} else {
|
||||
var key = this.buf1.name;
|
||||
this.shift();
|
||||
if (isEOF(this.buf1))
|
||||
break;
|
||||
dict.set(key, this.getObj(cipherTransform));
|
||||
}
|
||||
|
||||
var key = this.buf1.name;
|
||||
this.shift();
|
||||
if (isEOF(this.buf1))
|
||||
break;
|
||||
dict.set(key, this.getObj(cipherTransform));
|
||||
}
|
||||
if (isEOF(this.buf1))
|
||||
error('End of file inside dictionary');
|
||||
@ -99,38 +98,37 @@ var Parser = (function ParserClosure() {
|
||||
this.shift();
|
||||
return obj;
|
||||
},
|
||||
makeInlineImage: function parserMakeInlineImage(cipherTransform) {
|
||||
makeInlineImage: function Parser_makeInlineImage(cipherTransform) {
|
||||
var lexer = this.lexer;
|
||||
var stream = lexer.stream;
|
||||
|
||||
// parse dictionary
|
||||
var dict = new Dict();
|
||||
while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
|
||||
if (!isName(this.buf1)) {
|
||||
if (!isName(this.buf1))
|
||||
error('Dictionary key must be a name object');
|
||||
} else {
|
||||
var key = this.buf1.name;
|
||||
this.shift();
|
||||
if (isEOF(this.buf1))
|
||||
break;
|
||||
dict.set(key, this.getObj(cipherTransform));
|
||||
}
|
||||
|
||||
var key = this.buf1.name;
|
||||
this.shift();
|
||||
if (isEOF(this.buf1))
|
||||
break;
|
||||
dict.set(key, this.getObj(cipherTransform));
|
||||
}
|
||||
|
||||
// parse image stream
|
||||
var startPos = stream.pos;
|
||||
|
||||
// searching for the /\sEI\s/
|
||||
// searching for the /EI\s/
|
||||
var state = 0, ch;
|
||||
while (state != 4 && (ch = stream.getByte()) != null) {
|
||||
switch (ch) {
|
||||
case 0x20:
|
||||
case 0x0D:
|
||||
case 0x0A:
|
||||
state = state === 3 ? 4 : 1;
|
||||
state = state === 3 ? 4 : 0;
|
||||
break;
|
||||
case 0x45:
|
||||
state = state === 1 ? 2 : 0;
|
||||
state = 2;
|
||||
break;
|
||||
case 0x49:
|
||||
state = state === 2 ? 3 : 0;
|
||||
@ -162,7 +160,11 @@ var Parser = (function ParserClosure() {
|
||||
|
||||
return imageStream;
|
||||
},
|
||||
makeStream: function parserMakeStream(dict, cipherTransform) {
|
||||
fetchIfRef: function Parser_fetchIfRef(obj) {
|
||||
// not relying on the xref.fetchIfRef -- xref might not be set
|
||||
return isRef(obj) ? this.xref.fetch(obj) : obj;
|
||||
},
|
||||
makeStream: function Parser_makeStream(dict, cipherTransform) {
|
||||
var lexer = this.lexer;
|
||||
var stream = lexer.stream;
|
||||
|
||||
@ -171,14 +173,9 @@ var Parser = (function ParserClosure() {
|
||||
var pos = stream.pos;
|
||||
|
||||
// get length
|
||||
var length = dict.get('Length');
|
||||
var xref = this.xref;
|
||||
if (xref)
|
||||
length = xref.fetchIfRef(length);
|
||||
if (!isInt(length)) {
|
||||
var length = this.fetchIfRef(dict.get('Length'));
|
||||
if (!isInt(length))
|
||||
error('Bad ' + length + ' attribute in stream');
|
||||
length = 0;
|
||||
}
|
||||
|
||||
// skip over the stream data
|
||||
stream.pos = pos + length;
|
||||
@ -195,9 +192,9 @@ var Parser = (function ParserClosure() {
|
||||
stream.parameters = dict;
|
||||
return stream;
|
||||
},
|
||||
filter: function parserFilter(stream, dict, length) {
|
||||
var filter = dict.get('Filter', 'F');
|
||||
var params = dict.get('DecodeParms', 'DP');
|
||||
filter: function Parser_filter(stream, dict, length) {
|
||||
var filter = this.fetchIfRef(dict.get('Filter', 'F'));
|
||||
var params = this.fetchIfRef(dict.get('DecodeParms', 'DP'));
|
||||
if (isName(filter))
|
||||
return this.makeFilter(stream, filter.name, length, params);
|
||||
if (isArray(filter)) {
|
||||
@ -207,19 +204,18 @@ var Parser = (function ParserClosure() {
|
||||
filter = filterArray[i];
|
||||
if (!isName(filter))
|
||||
error('Bad filter name: ' + filter);
|
||||
else {
|
||||
params = null;
|
||||
if (isArray(paramsArray) && (i in paramsArray))
|
||||
params = paramsArray[i];
|
||||
stream = this.makeFilter(stream, filter.name, length, params);
|
||||
// after the first stream the length variable is invalid
|
||||
length = null;
|
||||
}
|
||||
|
||||
params = null;
|
||||
if (isArray(paramsArray) && (i in paramsArray))
|
||||
params = paramsArray[i];
|
||||
stream = this.makeFilter(stream, filter.name, length, params);
|
||||
// after the first stream the length variable is invalid
|
||||
length = null;
|
||||
}
|
||||
}
|
||||
return stream;
|
||||
},
|
||||
makeFilter: function parserMakeFilter(stream, name, length, params) {
|
||||
makeFilter: function Parser_makeFilter(stream, name, length, params) {
|
||||
if (name == 'FlateDecode' || name == 'Fl') {
|
||||
if (params) {
|
||||
return new PredictorStream(new FlateStream(stream), params);
|
||||
@ -269,7 +265,7 @@ var Lexer = (function LexerClosure() {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
Lexer.isSpace = function lexerIsSpace(ch) {
|
||||
Lexer.isSpace = function Lexer_isSpace(ch) {
|
||||
return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a';
|
||||
};
|
||||
|
||||
@ -304,7 +300,7 @@ var Lexer = (function LexerClosure() {
|
||||
}
|
||||
|
||||
Lexer.prototype = {
|
||||
getNumber: function lexerGetNumber(ch) {
|
||||
getNumber: function Lexer_getNumber(ch) {
|
||||
var floating = false;
|
||||
var str = ch;
|
||||
var stream = this.stream;
|
||||
@ -332,7 +328,7 @@ var Lexer = (function LexerClosure() {
|
||||
error('Invalid floating point number: ' + value);
|
||||
return value;
|
||||
},
|
||||
getString: function lexerGetString() {
|
||||
getString: function Lexer_getString() {
|
||||
var numParen = 1;
|
||||
var done = false;
|
||||
var str = '';
|
||||
@ -416,7 +412,7 @@ var Lexer = (function LexerClosure() {
|
||||
} while (!done);
|
||||
return str;
|
||||
},
|
||||
getName: function lexerGetName(ch) {
|
||||
getName: function Lexer_getName(ch) {
|
||||
var str = '';
|
||||
var stream = this.stream;
|
||||
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
|
||||
@ -443,7 +439,7 @@ var Lexer = (function LexerClosure() {
|
||||
str.length);
|
||||
return new Name(str);
|
||||
},
|
||||
getHexString: function lexerGetHexString(ch) {
|
||||
getHexString: function Lexer_getHexString(ch) {
|
||||
var str = '';
|
||||
var stream = this.stream;
|
||||
for (;;) {
|
||||
@ -472,7 +468,7 @@ var Lexer = (function LexerClosure() {
|
||||
}
|
||||
return str;
|
||||
},
|
||||
getObj: function lexerGetObj() {
|
||||
getObj: function Lexer_getObj() {
|
||||
// skip whitespace and comments
|
||||
var comment = false;
|
||||
var stream = this.stream;
|
||||
@ -526,17 +522,15 @@ var Lexer = (function LexerClosure() {
|
||||
// fall through
|
||||
case ')':
|
||||
error('Illegal character: ' + ch);
|
||||
return Error;
|
||||
}
|
||||
|
||||
// command
|
||||
var str = ch;
|
||||
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
|
||||
stream.skip();
|
||||
if (str.length == 128) {
|
||||
if (str.length == 128)
|
||||
error('Command token too long: ' + str.length);
|
||||
break;
|
||||
}
|
||||
|
||||
str += ch;
|
||||
}
|
||||
if (str == 'true')
|
||||
@ -547,7 +541,7 @@ var Lexer = (function LexerClosure() {
|
||||
return null;
|
||||
return Cmd.get(str);
|
||||
},
|
||||
skipToNextLine: function lexerSkipToNextLine() {
|
||||
skipToNextLine: function Lexer_skipToNextLine() {
|
||||
var stream = this.stream;
|
||||
while (true) {
|
||||
var ch = stream.getChar();
|
||||
@ -560,7 +554,7 @@ var Lexer = (function LexerClosure() {
|
||||
}
|
||||
}
|
||||
},
|
||||
skip: function lexerSkip() {
|
||||
skip: function Lexer_skip() {
|
||||
this.stream.skip();
|
||||
}
|
||||
};
|
||||
@ -570,7 +564,7 @@ var Lexer = (function LexerClosure() {
|
||||
|
||||
var Linearization = (function LinearizationClosure() {
|
||||
function Linearization(stream) {
|
||||
this.parser = new Parser(new Lexer(stream), false);
|
||||
this.parser = new Parser(new Lexer(stream), false, null);
|
||||
var obj1 = this.parser.getObj();
|
||||
var obj2 = this.parser.getObj();
|
||||
var obj3 = this.parser.getObj();
|
||||
@ -584,7 +578,7 @@ var Linearization = (function LinearizationClosure() {
|
||||
}
|
||||
|
||||
Linearization.prototype = {
|
||||
getInt: function linearizationGetInt(name) {
|
||||
getInt: function Linearization_getInt(name) {
|
||||
var linDict = this.linDict;
|
||||
var obj;
|
||||
if (isDict(linDict) &&
|
||||
@ -593,9 +587,8 @@ var Linearization = (function LinearizationClosure() {
|
||||
return obj;
|
||||
}
|
||||
error('"' + name + '" field in linearization table is invalid');
|
||||
return 0;
|
||||
},
|
||||
getHint: function linearizationGetHint(index) {
|
||||
getHint: function Linearization_getHint(index) {
|
||||
var linDict = this.linDict;
|
||||
var obj1, obj2;
|
||||
if (isDict(linDict) &&
|
||||
@ -606,7 +599,6 @@ var Linearization = (function LinearizationClosure() {
|
||||
return obj2;
|
||||
}
|
||||
error('Hints table in linearization table is invalid: ' + index);
|
||||
return 0;
|
||||
},
|
||||
get length() {
|
||||
if (!isDict(this.linDict))
|
||||
|
@ -17,17 +17,17 @@ var Pattern = (function PatternClosure() {
|
||||
Pattern.prototype = {
|
||||
// Input: current Canvas context
|
||||
// Output: the appropriate fillStyle or strokeStyle
|
||||
getPattern: function pattern_getStyle(ctx) {
|
||||
getPattern: function Pattern_getPattern(ctx) {
|
||||
error('Should not call Pattern.getStyle: ' + ctx);
|
||||
}
|
||||
};
|
||||
|
||||
Pattern.shadingFromIR = function pattern_shadingFromIR(ctx, raw) {
|
||||
return Shadings[raw[0]].fromIR(ctx, raw);
|
||||
Pattern.shadingFromIR = function Pattern_shadingFromIR(raw) {
|
||||
return Shadings[raw[0]].fromIR(raw);
|
||||
};
|
||||
|
||||
Pattern.parseShading = function pattern_shading(shading, matrix, xref,
|
||||
res, ctx) {
|
||||
Pattern.parseShading = function Pattern_parseShading(shading, matrix, xref,
|
||||
res) {
|
||||
|
||||
var dict = isStream(shading) ? shading.dict : shading;
|
||||
var type = dict.get('ShadingType');
|
||||
@ -36,7 +36,7 @@ var Pattern = (function PatternClosure() {
|
||||
case PatternType.AXIAL:
|
||||
case PatternType.RADIAL:
|
||||
// Both radial and axial shadings are handled by RadialAxial shading.
|
||||
return new Shadings.RadialAxial(dict, matrix, xref, res, ctx);
|
||||
return new Shadings.RadialAxial(dict, matrix, xref, res);
|
||||
default:
|
||||
return new Shadings.Dummy();
|
||||
}
|
||||
@ -79,10 +79,9 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
||||
this.extendEnd = extendEnd;
|
||||
|
||||
var fnObj = dict.get('Function');
|
||||
fnObj = xref.fetchIfRef(fnObj);
|
||||
if (isArray(fnObj))
|
||||
error('No support for array of functions');
|
||||
else if (!isPDFFunction(fnObj))
|
||||
if (!isPDFFunction(fnObj))
|
||||
error('Invalid function');
|
||||
var fn = PDFFunction.parse(xref, fnObj);
|
||||
|
||||
@ -102,40 +101,44 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
||||
this.colorStops = colorStops;
|
||||
}
|
||||
|
||||
RadialAxial.fromIR = function radialAxialShadingGetIR(ctx, raw) {
|
||||
RadialAxial.fromIR = function RadialAxial_fromIR(raw) {
|
||||
var type = raw[1];
|
||||
var colorStops = raw[2];
|
||||
var p0 = raw[3];
|
||||
var p1 = raw[4];
|
||||
var r0 = raw[5];
|
||||
var r1 = raw[6];
|
||||
return {
|
||||
type: 'Pattern',
|
||||
getPattern: function(ctx) {
|
||||
var curMatrix = ctx.mozCurrentTransform;
|
||||
if (curMatrix) {
|
||||
var userMatrix = ctx.mozCurrentTransformInverse;
|
||||
|
||||
var curMatrix = ctx.mozCurrentTransform;
|
||||
if (curMatrix) {
|
||||
var userMatrix = ctx.mozCurrentTransformInverse;
|
||||
p0 = Util.applyTransform(p0, curMatrix);
|
||||
p0 = Util.applyTransform(p0, userMatrix);
|
||||
|
||||
p0 = Util.applyTransform(p0, curMatrix);
|
||||
p0 = Util.applyTransform(p0, userMatrix);
|
||||
p1 = Util.applyTransform(p1, curMatrix);
|
||||
p1 = Util.applyTransform(p1, userMatrix);
|
||||
}
|
||||
|
||||
p1 = Util.applyTransform(p1, curMatrix);
|
||||
p1 = Util.applyTransform(p1, userMatrix);
|
||||
}
|
||||
var grad;
|
||||
if (type == PatternType.AXIAL)
|
||||
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
|
||||
else if (type == PatternType.RADIAL)
|
||||
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
|
||||
|
||||
var grad;
|
||||
if (type == PatternType.AXIAL)
|
||||
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
|
||||
else if (type == PatternType.RADIAL)
|
||||
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
|
||||
|
||||
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
|
||||
var c = colorStops[i];
|
||||
grad.addColorStop(c[0], c[1]);
|
||||
}
|
||||
return grad;
|
||||
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
|
||||
var c = colorStops[i];
|
||||
grad.addColorStop(c[0], c[1]);
|
||||
}
|
||||
return grad;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
RadialAxial.prototype = {
|
||||
getIR: function radialAxialShadingGetIR() {
|
||||
getIR: function RadialAxial_getIR() {
|
||||
var coordsArr = this.coordsArr;
|
||||
var type = this.shadingType;
|
||||
if (type == PatternType.AXIAL) {
|
||||
@ -170,12 +173,12 @@ Shadings.Dummy = (function DummyClosure() {
|
||||
this.type = 'Pattern';
|
||||
}
|
||||
|
||||
Dummy.fromIR = function dummyShadingFromIR() {
|
||||
Dummy.fromIR = function Dummy_fromIR() {
|
||||
return 'hotpink';
|
||||
};
|
||||
|
||||
Dummy.prototype = {
|
||||
getIR: function dummyShadingGetIR() {
|
||||
getIR: function Dummy_getIR() {
|
||||
return ['Dummy'];
|
||||
}
|
||||
};
|
||||
@ -190,7 +193,7 @@ var TilingPattern = (function TilingPatternClosure() {
|
||||
var MAX_PATTERN_SIZE = 512;
|
||||
|
||||
function TilingPattern(IR, color, ctx, objs) {
|
||||
var IRQueue = IR[2];
|
||||
var operatorList = IR[2];
|
||||
this.matrix = IR[3];
|
||||
var bbox = IR[4];
|
||||
var xstep = IR[5];
|
||||
@ -222,7 +225,7 @@ var TilingPattern = (function TilingPatternClosure() {
|
||||
width = height = MAX_PATTERN_SIZE;
|
||||
}
|
||||
|
||||
var tmpCanvas = new ScratchCanvas(width, height);
|
||||
var tmpCanvas = createScratchCanvas(width, height);
|
||||
|
||||
// set the new canvas element context as the graphics context
|
||||
var tmpCtx = tmpCanvas.getContext('2d');
|
||||
@ -259,12 +262,12 @@ var TilingPattern = (function TilingPatternClosure() {
|
||||
graphics.endPath();
|
||||
}
|
||||
|
||||
graphics.executeIRQueue(IRQueue);
|
||||
graphics.executeOperatorList(operatorList);
|
||||
|
||||
this.canvas = tmpCanvas;
|
||||
}
|
||||
|
||||
TilingPattern.getIR = function tiling_getIR(codeIR, dict, args) {
|
||||
TilingPattern.getIR = function TilingPattern_getIR(operatorList, dict, args) {
|
||||
var matrix = dict.get('Matrix');
|
||||
var bbox = dict.get('BBox');
|
||||
var xstep = dict.get('XStep');
|
||||
@ -272,12 +275,12 @@ var TilingPattern = (function TilingPatternClosure() {
|
||||
var paintType = dict.get('PaintType');
|
||||
|
||||
return [
|
||||
'TilingPattern', args, codeIR, matrix, bbox, xstep, ystep, paintType
|
||||
'TilingPattern', args, operatorList, matrix, bbox, xstep, ystep, paintType
|
||||
];
|
||||
};
|
||||
|
||||
TilingPattern.prototype = {
|
||||
getPattern: function tiling_getPattern() {
|
||||
getPattern: function TilingPattern_getPattern() {
|
||||
var matrix = this.matrix;
|
||||
var curMatrix = this.curMatrix;
|
||||
var ctx = this.ctx;
|
||||
|
151
src/stream.js
@ -18,14 +18,14 @@ var Stream = (function StreamClosure() {
|
||||
get length() {
|
||||
return this.end - this.start;
|
||||
},
|
||||
getByte: function stream_getByte() {
|
||||
getByte: function Stream_getByte() {
|
||||
if (this.pos >= this.end)
|
||||
return null;
|
||||
return this.bytes[this.pos++];
|
||||
},
|
||||
// returns subarray of original buffer
|
||||
// should only be read
|
||||
getBytes: function stream_getBytes(length) {
|
||||
getBytes: function Stream_getBytes(length) {
|
||||
var bytes = this.bytes;
|
||||
var pos = this.pos;
|
||||
var strEnd = this.end;
|
||||
@ -40,28 +40,28 @@ var Stream = (function StreamClosure() {
|
||||
this.pos = end;
|
||||
return bytes.subarray(pos, end);
|
||||
},
|
||||
lookChar: function stream_lookChar() {
|
||||
lookChar: function Stream_lookChar() {
|
||||
if (this.pos >= this.end)
|
||||
return null;
|
||||
return String.fromCharCode(this.bytes[this.pos]);
|
||||
},
|
||||
getChar: function stream_getChar() {
|
||||
getChar: function Stream_getChar() {
|
||||
if (this.pos >= this.end)
|
||||
return null;
|
||||
return String.fromCharCode(this.bytes[this.pos++]);
|
||||
},
|
||||
skip: function stream_skip(n) {
|
||||
skip: function Stream_skip(n) {
|
||||
if (!n)
|
||||
n = 1;
|
||||
this.pos += n;
|
||||
},
|
||||
reset: function stream_reset() {
|
||||
reset: function Stream_reset() {
|
||||
this.pos = this.start;
|
||||
},
|
||||
moveStart: function stream_moveStart() {
|
||||
moveStart: function Stream_moveStart() {
|
||||
this.start = this.pos;
|
||||
},
|
||||
makeSubStream: function stream_makeSubstream(start, length, dict) {
|
||||
makeSubStream: function Stream_makeSubStream(start, length, dict) {
|
||||
return new Stream(this.bytes.buffer, start, length, dict);
|
||||
},
|
||||
isStream: true
|
||||
@ -94,7 +94,7 @@ var DecodeStream = (function DecodeStreamClosure() {
|
||||
}
|
||||
|
||||
DecodeStream.prototype = {
|
||||
ensureBuffer: function decodestream_ensureBuffer(requested) {
|
||||
ensureBuffer: function DecodeStream_ensureBuffer(requested) {
|
||||
var buffer = this.buffer;
|
||||
var current = buffer ? buffer.byteLength : 0;
|
||||
if (requested < current)
|
||||
@ -107,7 +107,7 @@ var DecodeStream = (function DecodeStreamClosure() {
|
||||
buffer2[i] = buffer[i];
|
||||
return (this.buffer = buffer2);
|
||||
},
|
||||
getByte: function decodestream_getByte() {
|
||||
getByte: function DecodeStream_getByte() {
|
||||
var pos = this.pos;
|
||||
while (this.bufferLength <= pos) {
|
||||
if (this.eof)
|
||||
@ -116,7 +116,7 @@ var DecodeStream = (function DecodeStreamClosure() {
|
||||
}
|
||||
return this.buffer[this.pos++];
|
||||
},
|
||||
getBytes: function decodestream_getBytes(length) {
|
||||
getBytes: function DecodeStream_getBytes(length) {
|
||||
var end, pos = this.pos;
|
||||
|
||||
if (length) {
|
||||
@ -144,7 +144,7 @@ var DecodeStream = (function DecodeStreamClosure() {
|
||||
this.pos = end;
|
||||
return this.buffer.subarray(pos, end);
|
||||
},
|
||||
lookChar: function decodestream_lookChar() {
|
||||
lookChar: function DecodeStream_lookChar() {
|
||||
var pos = this.pos;
|
||||
while (this.bufferLength <= pos) {
|
||||
if (this.eof)
|
||||
@ -153,7 +153,7 @@ var DecodeStream = (function DecodeStreamClosure() {
|
||||
}
|
||||
return String.fromCharCode(this.buffer[this.pos]);
|
||||
},
|
||||
getChar: function decodestream_getChar() {
|
||||
getChar: function DecodeStream_getChar() {
|
||||
var pos = this.pos;
|
||||
while (this.bufferLength <= pos) {
|
||||
if (this.eof)
|
||||
@ -162,18 +162,18 @@ var DecodeStream = (function DecodeStreamClosure() {
|
||||
}
|
||||
return String.fromCharCode(this.buffer[this.pos++]);
|
||||
},
|
||||
makeSubStream: function decodestream_makeSubstream(start, length, dict) {
|
||||
makeSubStream: function DecodeStream_makeSubStream(start, length, dict) {
|
||||
var end = start + length;
|
||||
while (this.bufferLength <= end && !this.eof)
|
||||
this.readBlock();
|
||||
return new Stream(this.buffer, start, length, dict);
|
||||
},
|
||||
skip: function decodestream_skip(n) {
|
||||
skip: function DecodeStream_skip(n) {
|
||||
if (!n)
|
||||
n = 1;
|
||||
this.pos += n;
|
||||
},
|
||||
reset: function decodestream_reset() {
|
||||
reset: function DecodeStream_reset() {
|
||||
this.pos = 0;
|
||||
}
|
||||
};
|
||||
@ -188,14 +188,14 @@ var FakeStream = (function FakeStreamClosure() {
|
||||
}
|
||||
|
||||
FakeStream.prototype = Object.create(DecodeStream.prototype);
|
||||
FakeStream.prototype.readBlock = function fakeStreamReadBlock() {
|
||||
FakeStream.prototype.readBlock = function FakeStream_readBlock() {
|
||||
var bufferLength = this.bufferLength;
|
||||
bufferLength += 1024;
|
||||
var buffer = this.ensureBuffer(bufferLength);
|
||||
this.bufferLength = bufferLength;
|
||||
};
|
||||
|
||||
FakeStream.prototype.getBytes = function fakeStreamGetBytes(length) {
|
||||
FakeStream.prototype.getBytes = function FakeStream_getBytes(length) {
|
||||
var end, pos = this.pos;
|
||||
|
||||
if (length) {
|
||||
@ -368,7 +368,7 @@ var FlateStream = (function FlateStreamClosure() {
|
||||
|
||||
FlateStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
FlateStream.prototype.getBits = function flateStreamGetBits(bits) {
|
||||
FlateStream.prototype.getBits = function FlateStream_getBits(bits) {
|
||||
var codeSize = this.codeSize;
|
||||
var codeBuf = this.codeBuf;
|
||||
var bytes = this.bytes;
|
||||
@ -388,7 +388,7 @@ var FlateStream = (function FlateStreamClosure() {
|
||||
return b;
|
||||
};
|
||||
|
||||
FlateStream.prototype.getCode = function flateStreamGetCode(table) {
|
||||
FlateStream.prototype.getCode = function FlateStream_getCode(table) {
|
||||
var codes = table[0];
|
||||
var maxLen = table[1];
|
||||
var codeSize = this.codeSize;
|
||||
@ -453,7 +453,7 @@ var FlateStream = (function FlateStreamClosure() {
|
||||
return [codes, maxLen];
|
||||
};
|
||||
|
||||
FlateStream.prototype.readBlock = function flateStreamReadBlock() {
|
||||
FlateStream.prototype.readBlock = function FlateStream_readBlock() {
|
||||
// read block header
|
||||
var hdr = this.getBits(3);
|
||||
if (hdr & 1)
|
||||
@ -623,7 +623,6 @@ var PredictorStream = (function PredictorStreamClosure() {
|
||||
|
||||
var bufferLength = this.bufferLength;
|
||||
var buffer = this.ensureBuffer(bufferLength + rowBytes);
|
||||
var currentRow = buffer.subarray(bufferLength, bufferLength + rowBytes);
|
||||
|
||||
var bits = this.bits;
|
||||
var colors = this.colors;
|
||||
@ -632,6 +631,7 @@ var PredictorStream = (function PredictorStreamClosure() {
|
||||
|
||||
var inbuf = 0, outbuf = 0;
|
||||
var inbits = 0, outbits = 0;
|
||||
var pos = bufferLength;
|
||||
|
||||
if (bits === 1) {
|
||||
for (var i = 0; i < rowBytes; ++i) {
|
||||
@ -639,19 +639,21 @@ var PredictorStream = (function PredictorStreamClosure() {
|
||||
inbuf = (inbuf << 8) | c;
|
||||
// bitwise addition is exclusive or
|
||||
// first shift inbuf and then add
|
||||
currentRow[i] = (c ^ (inbuf >> colors)) & 0xFF;
|
||||
buffer[pos++] = (c ^ (inbuf >> colors)) & 0xFF;
|
||||
// truncate inbuf (assumes colors < 16)
|
||||
inbuf &= 0xFFFF;
|
||||
}
|
||||
} else if (bits === 8) {
|
||||
for (var i = 0; i < colors; ++i)
|
||||
currentRow[i] = rawBytes[i];
|
||||
for (; i < rowBytes; ++i)
|
||||
currentRow[i] = currentRow[i - colors] + rawBytes[i];
|
||||
buffer[pos++] = rawBytes[i];
|
||||
for (; i < rowBytes; ++i) {
|
||||
buffer[pos] = buffer[pos - colors] + rawBytes[i];
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
var compArray = new Uint8Array(colors + 1);
|
||||
var bitMask = (1 << bits) - 1;
|
||||
var j = 0, k = 0;
|
||||
var j = 0, k = bufferLength;
|
||||
var columns = this.columns;
|
||||
for (var i = 0; i < columns; ++i) {
|
||||
for (var kk = 0; kk < colors; ++kk) {
|
||||
@ -665,13 +667,13 @@ var PredictorStream = (function PredictorStreamClosure() {
|
||||
outbuf = (outbuf << bits) | compArray[kk];
|
||||
outbits += bits;
|
||||
if (outbits >= 8) {
|
||||
currentRow[k++] = (outbuf >> (outbits - 8)) & 0xFF;
|
||||
buffer[k++] = (outbuf >> (outbits - 8)) & 0xFF;
|
||||
outbits -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (outbits > 0) {
|
||||
currentRow[k++] = (outbuf << (8 - outbits)) +
|
||||
buffer[k++] = (outbuf << (8 - outbits)) +
|
||||
(inbuf & ((1 << (8 - outbits)) - 1));
|
||||
}
|
||||
}
|
||||
@ -690,32 +692,35 @@ var PredictorStream = (function PredictorStreamClosure() {
|
||||
var bufferLength = this.bufferLength;
|
||||
var buffer = this.ensureBuffer(bufferLength + rowBytes);
|
||||
|
||||
var currentRow = buffer.subarray(bufferLength, bufferLength + rowBytes);
|
||||
var prevRow = buffer.subarray(bufferLength - rowBytes, bufferLength);
|
||||
if (prevRow.length == 0)
|
||||
prevRow = new Uint8Array(rowBytes);
|
||||
|
||||
var j = bufferLength;
|
||||
switch (predictor) {
|
||||
case 0:
|
||||
for (var i = 0; i < rowBytes; ++i)
|
||||
currentRow[i] = rawBytes[i];
|
||||
buffer[j++] = rawBytes[i];
|
||||
break;
|
||||
case 1:
|
||||
for (var i = 0; i < pixBytes; ++i)
|
||||
currentRow[i] = rawBytes[i];
|
||||
for (; i < rowBytes; ++i)
|
||||
currentRow[i] = (currentRow[i - pixBytes] + rawBytes[i]) & 0xFF;
|
||||
buffer[j++] = rawBytes[i];
|
||||
for (; i < rowBytes; ++i) {
|
||||
buffer[j] = (buffer[j - pixBytes] + rawBytes[i]) & 0xFF;
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (var i = 0; i < rowBytes; ++i)
|
||||
currentRow[i] = (prevRow[i] + rawBytes[i]) & 0xFF;
|
||||
buffer[j++] = (prevRow[i] + rawBytes[i]) & 0xFF;
|
||||
break;
|
||||
case 3:
|
||||
for (var i = 0; i < pixBytes; ++i)
|
||||
currentRow[i] = (prevRow[i] >> 1) + rawBytes[i];
|
||||
buffer[j++] = (prevRow[i] >> 1) + rawBytes[i];
|
||||
for (; i < rowBytes; ++i) {
|
||||
currentRow[i] = (((prevRow[i] + currentRow[i - pixBytes]) >> 1) +
|
||||
buffer[j] = (((prevRow[i] + buffer[j - pixBytes]) >> 1) +
|
||||
rawBytes[i]) & 0xFF;
|
||||
j++;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
@ -724,12 +729,12 @@ var PredictorStream = (function PredictorStreamClosure() {
|
||||
for (var i = 0; i < pixBytes; ++i) {
|
||||
var up = prevRow[i];
|
||||
var c = rawBytes[i];
|
||||
currentRow[i] = up + c;
|
||||
buffer[j++] = up + c;
|
||||
}
|
||||
for (; i < rowBytes; ++i) {
|
||||
var up = prevRow[i];
|
||||
var upLeft = prevRow[i - pixBytes];
|
||||
var left = currentRow[i - pixBytes];
|
||||
var left = buffer[j - pixBytes];
|
||||
var p = left + up - upLeft;
|
||||
|
||||
var pa = p - left;
|
||||
@ -744,11 +749,11 @@ var PredictorStream = (function PredictorStreamClosure() {
|
||||
|
||||
var c = rawBytes[i];
|
||||
if (pa <= pb && pa <= pc)
|
||||
currentRow[i] = left + c;
|
||||
buffer[j++] = left + c;
|
||||
else if (pb <= pc)
|
||||
currentRow[i] = up + c;
|
||||
buffer[j++] = up + c;
|
||||
else
|
||||
currentRow[i] = upLeft + c;
|
||||
buffer[j++] = upLeft + c;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -818,31 +823,35 @@ var JpegStream = (function JpegStreamClosure() {
|
||||
|
||||
JpegStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
JpegStream.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) {
|
||||
JpegStream.prototype.ensureBuffer = function JpegStream_ensureBuffer(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() {
|
||||
JpegStream.prototype.getIR = function JpegStream_getIR() {
|
||||
return bytesToString(this.bytes);
|
||||
};
|
||||
JpegStream.prototype.getChar = function jpegStreamGetChar() {
|
||||
JpegStream.prototype.getChar = function JpegStream_getChar() {
|
||||
error('internal error: getChar is not valid on JpegStream');
|
||||
};
|
||||
/**
|
||||
* Checks if the image can be decoded and displayed by the browser without any
|
||||
* further processing such as color space conversions.
|
||||
*/
|
||||
JpegStream.prototype.isNativelySupported = function isNativelySupported(xref,
|
||||
res) {
|
||||
JpegStream.prototype.isNativelySupported =
|
||||
function JpegStream_isNativelySupported(xref, res) {
|
||||
var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
|
||||
// when bug 674619 lands, let's check if browser can do
|
||||
// normal cmyk and then we won't need to decode in JS
|
||||
@ -856,8 +865,8 @@ var JpegStream = (function JpegStreamClosure() {
|
||||
/**
|
||||
* Checks if the image can be decoded by the browser.
|
||||
*/
|
||||
JpegStream.prototype.isNativelyDecodable = function isNativelyDecodable(xref,
|
||||
res) {
|
||||
JpegStream.prototype.isNativelyDecodable =
|
||||
function JpegStream_isNativelyDecodable(xref, res) {
|
||||
var cs = ColorSpace.parse(this.dict.get('ColorSpace'), xref, res);
|
||||
var numComps = cs.numComps;
|
||||
if (numComps == 1 || numComps == 3)
|
||||
@ -883,7 +892,7 @@ var JpxStream = (function JpxStreamClosure() {
|
||||
|
||||
JpxStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
JpxStream.prototype.ensureBuffer = function jpxStreamEnsureBuffer(req) {
|
||||
JpxStream.prototype.ensureBuffer = function JpxStream_ensureBuffer(req) {
|
||||
if (this.bufferLength)
|
||||
return;
|
||||
|
||||
@ -963,7 +972,7 @@ var JpxStream = (function JpxStreamClosure() {
|
||||
this.buffer = data;
|
||||
this.bufferLength = data.length;
|
||||
};
|
||||
JpxStream.prototype.getChar = function jpxStreamGetChar() {
|
||||
JpxStream.prototype.getChar = function JpxStream_getChar() {
|
||||
error('internal error: getChar is not valid on JpxStream');
|
||||
};
|
||||
|
||||
@ -983,7 +992,7 @@ var DecryptStream = (function DecryptStreamClosure() {
|
||||
|
||||
DecryptStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
DecryptStream.prototype.readBlock = function decryptStreamReadBlock() {
|
||||
DecryptStream.prototype.readBlock = function DecryptStream_readBlock() {
|
||||
var chunk = this.str.getBytes(chunkSize);
|
||||
if (!chunk || chunk.length == 0) {
|
||||
this.eof = true;
|
||||
@ -1014,7 +1023,7 @@ var Ascii85Stream = (function Ascii85StreamClosure() {
|
||||
|
||||
Ascii85Stream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
Ascii85Stream.prototype.readBlock = function ascii85StreamReadBlock() {
|
||||
Ascii85Stream.prototype.readBlock = function Ascii85Stream_readBlock() {
|
||||
var tildaCode = '~'.charCodeAt(0);
|
||||
var zCode = 'z'.charCodeAt(0);
|
||||
var str = this.str;
|
||||
@ -1109,7 +1118,7 @@ var AsciiHexStream = (function AsciiHexStreamClosure() {
|
||||
|
||||
AsciiHexStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
AsciiHexStream.prototype.readBlock = function asciiHexStreamReadBlock() {
|
||||
AsciiHexStream.prototype.readBlock = function AsciiHexStream_readBlock() {
|
||||
var gtCode = '>'.charCodeAt(0), bytes = this.str.getBytes(), c, n,
|
||||
decodeLength, buffer, bufferLength, i, length;
|
||||
|
||||
@ -1152,7 +1161,7 @@ var RunLengthStream = (function RunLengthStreamClosure() {
|
||||
|
||||
RunLengthStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
RunLengthStream.prototype.readBlock = function runLengthStreamReadBlock() {
|
||||
RunLengthStream.prototype.readBlock = function RunLengthStream_readBlock() {
|
||||
// The repeatHeader has following format. The first byte defines type of run
|
||||
// and amount of bytes to repeat/copy: n = 0 through 127 - copy next n bytes
|
||||
// (in addition to the second byte from the header), n = 129 through 255 -
|
||||
@ -1662,7 +1671,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
|
||||
CCITTFaxStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
CCITTFaxStream.prototype.readBlock = function ccittFaxStreamReadBlock() {
|
||||
CCITTFaxStream.prototype.readBlock = function CCITTFaxStream_readBlock() {
|
||||
while (!this.eof) {
|
||||
var c = this.lookChar();
|
||||
this.buf = EOF;
|
||||
@ -1720,7 +1729,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
this.codingPos = codingPos;
|
||||
};
|
||||
|
||||
CCITTFaxStream.prototype.lookChar = function ccittFaxStreamLookChar() {
|
||||
CCITTFaxStream.prototype.lookChar = function CCITTFaxStream_lookChar() {
|
||||
if (this.buf != EOF)
|
||||
return this.buf;
|
||||
|
||||
@ -2047,7 +2056,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
if (this.eoblock) {
|
||||
code = this.lookBits(7);
|
||||
p = twoDimTable[code];
|
||||
if (p[0] > 0) {
|
||||
if (p && p[0] > 0) {
|
||||
this.eatBits(p[0]);
|
||||
return p[1];
|
||||
}
|
||||
@ -2131,7 +2140,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
return 1;
|
||||
};
|
||||
|
||||
CCITTFaxStream.prototype.lookBits = function ccittFaxStreamLookBits(n) {
|
||||
CCITTFaxStream.prototype.lookBits = function CCITTFaxStream_lookBits(n) {
|
||||
var c;
|
||||
while (this.inputBits < n) {
|
||||
if ((c = this.str.getByte()) == null) {
|
||||
@ -2146,7 +2155,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n));
|
||||
};
|
||||
|
||||
CCITTFaxStream.prototype.eatBits = function ccittFaxStreamEatBits(n) {
|
||||
CCITTFaxStream.prototype.eatBits = function CCITTFaxStream_eatBits(n) {
|
||||
if ((this.inputBits -= n) < 0)
|
||||
this.inputBits = 0;
|
||||
};
|
||||
@ -2183,7 +2192,7 @@ var LZWStream = (function LZWStreamClosure() {
|
||||
|
||||
LZWStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
LZWStream.prototype.readBits = function lzwStreamReadBits(n) {
|
||||
LZWStream.prototype.readBits = function LZWStream_readBits(n) {
|
||||
var bitsCached = this.bitsCached;
|
||||
var cachedData = this.cachedData;
|
||||
while (bitsCached < n) {
|
||||
@ -2201,7 +2210,7 @@ var LZWStream = (function LZWStreamClosure() {
|
||||
return (cachedData >>> bitsCached) & ((1 << n) - 1);
|
||||
};
|
||||
|
||||
LZWStream.prototype.readBlock = function lzwStreamReadBlock() {
|
||||
LZWStream.prototype.readBlock = function LZWStream_readBlock() {
|
||||
var blockSize = 512;
|
||||
var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize;
|
||||
var i, j, q;
|
||||
|
164
src/util.js
@ -78,21 +78,99 @@ var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
||||
|
||||
var Util = (function UtilClosure() {
|
||||
function Util() {}
|
||||
Util.makeCssRgb = function makergb(r, g, b) {
|
||||
|
||||
Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
|
||||
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
|
||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||
};
|
||||
Util.makeCssCmyk = function makecmyk(c, m, y, k) {
|
||||
|
||||
Util.makeCssCmyk = function Util_makeCssCmyk(c, m, y, k) {
|
||||
c = (new DeviceCmykCS()).getRgb([c, m, y, k]);
|
||||
var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0;
|
||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||
};
|
||||
Util.applyTransform = function apply(p, m) {
|
||||
|
||||
// For 2d affine transforms
|
||||
Util.applyTransform = function Util_applyTransform(p, m) {
|
||||
var xt = p[0] * m[0] + p[1] * m[2] + m[4];
|
||||
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
||||
return [xt, yt];
|
||||
};
|
||||
|
||||
// Apply a generic 3d matrix M on a 3-vector v:
|
||||
// | a b c | | X |
|
||||
// | d e f | x | Y |
|
||||
// | g h i | | Z |
|
||||
// M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
|
||||
// with v as [X,Y,Z]
|
||||
Util.apply3dTransform = function Util_apply3dTransform(m, v) {
|
||||
return [
|
||||
m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
|
||||
m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
|
||||
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
|
||||
];
|
||||
}
|
||||
|
||||
// Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
|
||||
// For coordinate systems whose origin lies in the bottom-left, this
|
||||
// means normalization to (BL,TR) ordering. For systems with origin in the
|
||||
// top-left, this means (TL,BR) ordering.
|
||||
Util.normalizeRect = function Util_normalizeRect(rect) {
|
||||
var r = rect.slice(0); // clone rect
|
||||
if (rect[0] > rect[2]) {
|
||||
r[0] = rect[2];
|
||||
r[2] = rect[0];
|
||||
}
|
||||
if (rect[1] > rect[3]) {
|
||||
r[1] = rect[3];
|
||||
r[3] = rect[1];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Returns a rectangle [x1, y1, x2, y2] corresponding to the
|
||||
// intersection of rect1 and rect2. If no intersection, returns 'false'
|
||||
// The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
|
||||
Util.intersect = function Util_intersect(rect1, rect2) {
|
||||
function compare(a, b) {
|
||||
return a - b;
|
||||
};
|
||||
|
||||
// Order points along the axes
|
||||
var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
|
||||
orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
|
||||
result = [];
|
||||
|
||||
rect1 = Util.normalizeRect(rect1);
|
||||
rect2 = Util.normalizeRect(rect2);
|
||||
|
||||
// X: first and second points belong to different rectangles?
|
||||
if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
|
||||
(orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
|
||||
// Intersection must be between second and third points
|
||||
result[0] = orderedX[1];
|
||||
result[2] = orderedX[2];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Y: first and second points belong to different rectangles?
|
||||
if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
|
||||
(orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
|
||||
// Intersection must be between second and third points
|
||||
result[1] = orderedY[1];
|
||||
result[3] = orderedY[2];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Util.sign = function Util_sign(num) {
|
||||
return num < 0 ? -1 : 1;
|
||||
};
|
||||
|
||||
return Util;
|
||||
})();
|
||||
|
||||
@ -226,7 +304,7 @@ var Promise = (function PromiseClosure() {
|
||||
* @param {Promise[]} promises Array of promises to wait for.
|
||||
* @return {Promise} New dependant promise.
|
||||
*/
|
||||
Promise.all = function(promises) {
|
||||
Promise.all = function Promise_all(promises) {
|
||||
var deferred = new Promise();
|
||||
var unresolved = promises.length;
|
||||
var results = [];
|
||||
@ -255,8 +333,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,12 +346,12 @@ 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;
|
||||
},
|
||||
|
||||
onData: function promiseOnData(callback) {
|
||||
onData: function Promise_onData(callback) {
|
||||
if (this._data !== EMPTY_PROMISE) {
|
||||
callback(this._data);
|
||||
} else {
|
||||
@ -281,12 +359,12 @@ var Promise = (function PromiseClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
resolve: function promiseResolve(data) {
|
||||
resolve: function Promise_resolve(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;
|
||||
@ -298,12 +376,12 @@ var Promise = (function PromiseClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
reject: function proimseReject(reason) {
|
||||
reject: function Promise_reject(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;
|
||||
@ -315,16 +393,16 @@ var Promise = (function PromiseClosure() {
|
||||
}
|
||||
},
|
||||
|
||||
then: function promiseThen(callback, errback) {
|
||||
then: function Promise_then(callback, errback) {
|
||||
if (!callback) {
|
||||
throw 'Requiring callback' + this.name;
|
||||
error('Requiring callback' + this.name);
|
||||
}
|
||||
|
||||
// If the promise is already resolved, call the callback directly.
|
||||
if (this.isResolved) {
|
||||
var data = this.data;
|
||||
callback.call(null, data);
|
||||
} else if (this.isRejected && errorback) {
|
||||
} else if (this.isRejected && errback) {
|
||||
var error = this.error;
|
||||
errback.call(null, error);
|
||||
} else {
|
||||
@ -338,3 +416,55 @@ var Promise = (function PromiseClosure() {
|
||||
return Promise;
|
||||
})();
|
||||
|
||||
var StatTimer = (function StatTimerClosure() {
|
||||
function rpad(str, pad, length) {
|
||||
while (str.length < length)
|
||||
str += pad;
|
||||
return str;
|
||||
}
|
||||
function StatTimer() {
|
||||
this.started = {};
|
||||
this.times = [];
|
||||
this.enabled = true;
|
||||
}
|
||||
StatTimer.prototype = {
|
||||
time: function StatTimer_time(name) {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
if (name in this.started)
|
||||
throw 'Timer is already running for ' + name;
|
||||
this.started[name] = Date.now();
|
||||
},
|
||||
timeEnd: function StatTimer_timeEnd(name) {
|
||||
if (!this.enabled)
|
||||
return;
|
||||
if (!(name in this.started))
|
||||
throw 'Timer has not been started for ' + name;
|
||||
this.times.push({
|
||||
'name': name,
|
||||
'start': this.started[name],
|
||||
'end': Date.now()
|
||||
});
|
||||
// Remove timer from started so it can be called again.
|
||||
delete this.started[name];
|
||||
},
|
||||
toString: function StatTimer_toString() {
|
||||
var times = this.times;
|
||||
var out = '';
|
||||
// Find the longest name for padding purposes.
|
||||
var longest = 0;
|
||||
for (var i = 0, ii = times.length; i < ii; ++i) {
|
||||
var name = times[i]['name'];
|
||||
if (name.length > longest)
|
||||
longest = name.length;
|
||||
}
|
||||
for (var i = 0, ii = times.length; i < ii; ++i) {
|
||||
var span = times[i];
|
||||
var duration = span.end - span.start;
|
||||
out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
return StatTimer;
|
||||
})();
|
||||
|
@ -122,9 +122,9 @@ function readFontDictData(aString, aMap) {
|
||||
token = '';
|
||||
var parsed = false;
|
||||
while (!parsed) {
|
||||
var byte = aString[i++];
|
||||
var octet = aString[i++];
|
||||
|
||||
var nibbles = [parseInt(byte / 16, 10), parseInt(byte % 16, 10)];
|
||||
var nibbles = [parseInt(octet / 16, 10), parseInt(octet % 16, 10)];
|
||||
for (var j = 0; j < nibbles.length; j++) {
|
||||
var nibble = nibbles[j];
|
||||
switch (nibble) {
|
||||
@ -336,7 +336,7 @@ var Type2Parser = function type2Parser(aFilePath) {
|
||||
var privateDict = [];
|
||||
for (var i = 0; i < priv.size; i++)
|
||||
privateDict.push(aStream.getByte());
|
||||
dump('private:' + privateDict);
|
||||
dump('privateData:' + privateDict);
|
||||
parseAsToken(privateDict, CFFDictPrivateDataMap);
|
||||
|
||||
for (var p in font.map)
|
||||
|
110
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];
|
||||
},
|
||||
@ -79,7 +79,7 @@ MessageHandler.prototype = {
|
||||
|
||||
var WorkerMessageHandler = {
|
||||
setup: function wphSetup(handler) {
|
||||
var pdfDoc = null;
|
||||
var pdfModel = null;
|
||||
|
||||
handler.on('test', function wphSetupTest(data) {
|
||||
handler.send('test', data instanceof Uint8Array);
|
||||
@ -88,7 +88,7 @@ var WorkerMessageHandler = {
|
||||
handler.on('doc', function wphSetupDoc(data) {
|
||||
// Create only the model of the PDFDoc, which is enough for
|
||||
// processing the content of the pdf.
|
||||
pdfDoc = new PDFDocModel(new Stream(data));
|
||||
pdfModel = new PDFDocModel(new Stream(data));
|
||||
});
|
||||
|
||||
handler.on('page_request', function wphSetupPageRequest(pageNum) {
|
||||
@ -103,17 +103,33 @@ var WorkerMessageHandler = {
|
||||
var start = Date.now();
|
||||
|
||||
var dependency = [];
|
||||
var IRQueue = null;
|
||||
var operatorList = null;
|
||||
try {
|
||||
var page = pdfDoc.getPage(pageNum);
|
||||
var page = pdfModel.getPage(pageNum);
|
||||
// Pre compile the pdf page and fetch the fonts/images.
|
||||
IRQueue = page.getIRQueue(handler, dependency);
|
||||
operatorList = page.getOperatorList(handler, dependency);
|
||||
} catch (e) {
|
||||
var minimumStackMessage =
|
||||
'worker.js: while trying to getPage() and getOperatorList()';
|
||||
|
||||
// Turn the error into an obj that can be serialized
|
||||
e = {
|
||||
message: typeof e === 'object' ? e.message : e,
|
||||
stack: typeof e === 'object' ? e.stack : null
|
||||
};
|
||||
if (typeof e === 'string') {
|
||||
e = {
|
||||
message: e,
|
||||
stack: minimumStackMessage
|
||||
};
|
||||
} else if (typeof e === 'object') {
|
||||
e = {
|
||||
message: e.message || e.toString(),
|
||||
stack: e.stack || minimumStackMessage
|
||||
};
|
||||
} else {
|
||||
e = {
|
||||
message: 'Unknown exception type: ' + (typeof e),
|
||||
stack: minimumStackMessage
|
||||
};
|
||||
}
|
||||
|
||||
handler.send('page_error', {
|
||||
pageNum: pageNum,
|
||||
error: e
|
||||
@ -121,8 +137,8 @@ var WorkerMessageHandler = {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum,
|
||||
Date.now() - start, IRQueue.fnArray.length);
|
||||
console.log('page=%d - getOperatorList: time=%dms, len=%d', pageNum,
|
||||
Date.now() - start, operatorList.fnArray.length);
|
||||
|
||||
// Filter the dependecies for fonts.
|
||||
var fonts = {};
|
||||
@ -135,62 +151,13 @@ var WorkerMessageHandler = {
|
||||
|
||||
handler.send('page', {
|
||||
pageNum: pageNum,
|
||||
IRQueue: IRQueue,
|
||||
operatorList: operatorList,
|
||||
depFonts: Object.keys(fonts)
|
||||
});
|
||||
}, this);
|
||||
|
||||
handler.on('font', function wphSetupFont(data) {
|
||||
var objId = data[0];
|
||||
var name = data[1];
|
||||
var file = data[2];
|
||||
var properties = data[3];
|
||||
|
||||
var font = {
|
||||
name: name,
|
||||
file: file,
|
||||
properties: properties
|
||||
};
|
||||
|
||||
// Some fonts don't have a file, e.g. the build in ones like Arial.
|
||||
if (file) {
|
||||
var fontFileDict = new Dict();
|
||||
fontFileDict.map = file.dict.map;
|
||||
|
||||
var fontFile = new Stream(file.bytes, file.start,
|
||||
file.end - file.start, fontFileDict);
|
||||
|
||||
// Check if this is a FlateStream. Otherwise just use the created
|
||||
// Stream one. This makes complex_ttf_font.pdf work.
|
||||
var cmf = file.bytes[0];
|
||||
if ((cmf & 0x0f) == 0x08) {
|
||||
font.file = new FlateStream(fontFile);
|
||||
} else {
|
||||
font.file = fontFile;
|
||||
}
|
||||
}
|
||||
|
||||
var obj = new Font(font.name, font.file, font.properties);
|
||||
|
||||
var str = '';
|
||||
var objData = obj.data;
|
||||
if (objData) {
|
||||
var length = objData.length;
|
||||
for (var j = 0; j < length; ++j)
|
||||
str += String.fromCharCode(objData[j]);
|
||||
}
|
||||
|
||||
obj.str = str;
|
||||
|
||||
// Remove the data array form the font object, as it's not needed
|
||||
// anymore as we sent over the ready str.
|
||||
delete obj.data;
|
||||
|
||||
handler.send('font_ready', [objId, obj]);
|
||||
});
|
||||
|
||||
handler.on('extract_text', function wphExtractText() {
|
||||
var numPages = pdfDoc.numPages;
|
||||
var numPages = pdfModel.numPages;
|
||||
var index = [];
|
||||
var start = Date.now();
|
||||
|
||||
@ -203,12 +170,12 @@ var WorkerMessageHandler = {
|
||||
}
|
||||
|
||||
var textContent = '';
|
||||
try {
|
||||
var page = pdfDoc.getPage(pageNum);
|
||||
// try {
|
||||
var page = pdfModel.getPage(pageNum);
|
||||
textContent = page.extractTextContent();
|
||||
} catch (e) {
|
||||
// Skip errored pages
|
||||
}
|
||||
// } catch (e) {
|
||||
// // Skip errored pages
|
||||
// }
|
||||
|
||||
index.push(textContent);
|
||||
|
||||
@ -241,6 +208,7 @@ var workerConsole = {
|
||||
action: 'console_error',
|
||||
data: args
|
||||
});
|
||||
throw 'pdf.js execution error';
|
||||
},
|
||||
|
||||
time: function time(name) {
|
||||
@ -250,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);
|
||||
}
|
||||
|
@ -24,7 +24,8 @@ var files = [
|
||||
'stream.js',
|
||||
'worker.js',
|
||||
'../external/jpgjs/jpg.js',
|
||||
'jpx.js'
|
||||
'jpx.js',
|
||||
'bidi.js'
|
||||
];
|
||||
|
||||
// Load all the files.
|
||||
|
@ -248,16 +248,21 @@ function done() {
|
||||
}
|
||||
}
|
||||
|
||||
function sendTaskResult(snapshot, task, failure) {
|
||||
var result = { browser: browser,
|
||||
id: task.id,
|
||||
numPages: task.pdfDoc ?
|
||||
(task.pageLimit || task.pdfDoc.numPages) : 0,
|
||||
failure: failure,
|
||||
file: task.file,
|
||||
round: task.round,
|
||||
page: task.pageNum,
|
||||
snapshot: snapshot };
|
||||
function sendTaskResult(snapshot, task, failure, result) {
|
||||
// Optional result argument is for retrying XHR requests - see below
|
||||
if (!result) {
|
||||
result = JSON.stringify({
|
||||
browser: browser,
|
||||
id: task.id,
|
||||
numPages: task.pdfDoc ?
|
||||
(task.pageLimit || task.pdfDoc.numPages) : 0,
|
||||
failure: failure,
|
||||
file: task.file,
|
||||
round: task.round,
|
||||
page: task.pageNum,
|
||||
snapshot: snapshot
|
||||
});
|
||||
}
|
||||
|
||||
var r = new XMLHttpRequest();
|
||||
// (The POST URI is ignored atm.)
|
||||
@ -266,10 +271,13 @@ function sendTaskResult(snapshot, task, failure) {
|
||||
r.onreadystatechange = function sendTaskResultOnreadystatechange(e) {
|
||||
if (r.readyState == 4) {
|
||||
inFlightRequests--;
|
||||
// Retry until successful
|
||||
if (r.status !== 200)
|
||||
sendTaskResult(null, null, null, result);
|
||||
}
|
||||
};
|
||||
document.getElementById('inFlightCount').innerHTML = inFlightRequests++;
|
||||
r.send(JSON.stringify(result));
|
||||
r.send(result);
|
||||
}
|
||||
|
||||
function clear(ctx) {
|
||||
|
8
test/pdfs/.gitignore
vendored
@ -19,8 +19,16 @@
|
||||
!issue840.pdf
|
||||
!scan-bad.pdf
|
||||
!freeculture.pdf
|
||||
!pdfkit_compressed.pdf
|
||||
!TAMReview.pdf
|
||||
!issue918.pdf
|
||||
!issue1249.pdf
|
||||
!smaskdim.pdf
|
||||
!type4psfunc.pdf
|
||||
!issue1350.pdf
|
||||
!S2.pdf
|
||||
!zerowidthline.pdf
|
||||
!issue1002.pdf
|
||||
!issue925.pdf
|
||||
!gradientfill.pdf
|
||||
|
||||
|
BIN
test/pdfs/TAMReview.pdf
Normal file
BIN
test/pdfs/gradientfill.pdf
Normal file
1
test/pdfs/html5checker.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://hsivonen.iki.fi/thesis/html5-conformance-checker.pdf
|
BIN
test/pdfs/issue1002.pdf
Normal file
1
test/pdfs/issue1049.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://ernestinefont.com/wp-content/themes/iA3%201.2.1/assets/pdf/ErnestinePro-InfoGuide.pdf
|
1
test/pdfs/issue1096.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.faithaliveresources.org/Content/Site135/FilesSamples/105315400440pdf_00000009843.pdf
|
1
test/pdfs/issue1127.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
https://vmp.ethz.ch/pdfs/diplome/vordiplome/Block%201/Algorithmen_%26_Komplexitaet/AlgoKo_f08_Aufg.pdf
|
1
test/pdfs/issue1133.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.cscw2012.org/docs/program.pdf
|
1
test/pdfs/issue1155.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.madbad.altervista.org/_altervista_ht/2142.pdf
|
1
test/pdfs/issue1169.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.cs.txstate.edu/~mb92/papers/gpgpu11.pdf
|
1
test/pdfs/issue1243.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.nsa.gov/public_info/_files/nash_letters/nash_letters1.pdf
|
BIN
test/pdfs/issue1249.pdf
Normal file
1
test/pdfs/issue1257.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://hse-econ.fi/tervio/MediocritiesAndSuperstars.pdf
|
1
test/pdfs/issue1309.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.lufthansa.com/mediapool/pdf/31/media_907231.pdf
|
1
test/pdfs/issue1317.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://iliad.fr/presse/2012/CP_080312_Free_mobile.pdf
|
2184
test/pdfs/issue1350.pdf
Normal file
1
test/pdfs/issue1466.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://web.missouri.edu/~fanxud/publications/On%20the%20performance%20quantification%20of%20resonant%20refractive%20index%20sensors.pdf
|
BIN
test/pdfs/issue925.pdf
Executable file
BIN
test/pdfs/pdfkit_compressed.pdf
Normal file
1
test/pdfs/preistabelle.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.fyve.de/downloads/Preistabelle_FYVE_Oktober_2010.pdf
|
@ -1 +1 @@
|
||||
http://www.mit.edu/~6.033/writing-samples/usmanm_dp1.pdf
|
||||
http://web.mit.edu/6.033/2011/wwwdocs/writing-samples/usmanm_dp1.pdf
|
||||
|
@ -1 +1 @@
|
||||
http://www.cdc.gov/ncidod/dvbid/westnile/languages/chinese.pdf
|
||||
http://web.archive.org/web/20110623114753/http://www.cdc.gov/ncidod/dvbid/westnile/languages/chinese.pdf
|
||||
|
@ -14,7 +14,7 @@
|
||||
<Description>
|
||||
<em:id>toolkit@mozilla.org</em:id>
|
||||
<em:minVersion>3.0</em:minVersion>
|
||||
<em:maxVersion>7.0a1</em:maxVersion>
|
||||
<em:maxVersion>13.0a1</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
|
@ -64,7 +64,9 @@ MIMEs = {
|
||||
'.svg': 'image/svg+xml',
|
||||
'.pdf': 'application/pdf',
|
||||
'.xhtml': 'application/xhtml+xml',
|
||||
'.gif': 'image/gif',
|
||||
'.ico': 'image/x-icon',
|
||||
'.png': 'image/png',
|
||||
'.log': 'text/plain'
|
||||
}
|
||||
|
||||
|
@ -402,6 +402,27 @@
|
||||
"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": "issue1249-load",
|
||||
"file": "pdfs/issue1249.pdf",
|
||||
"md5": "4f81339fa09422a7db980f34ea963609",
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
},
|
||||
{ "id": "liveprogramming",
|
||||
"file": "pdfs/liveprogramming.pdf",
|
||||
"md5": "7bd4dad1188232ef597d36fd72c33e52",
|
||||
@ -423,11 +444,132 @@
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1169",
|
||||
"file": "pdfs/issue1169.pdf",
|
||||
"md5": "3df3ed21fd43ac7fdb21e2015c8a7809",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "zerowidthline",
|
||||
"file": "pdfs/zerowidthline.pdf",
|
||||
"md5": "295d26e61a85635433f8e4b768953f60",
|
||||
"rounds": 1,
|
||||
"link": false,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "html5checker",
|
||||
"file": "pdfs/html5checker.pdf",
|
||||
"md5": "74bbd80d1e7eb5f2951582233ef9ebab",
|
||||
"rounds": 1,
|
||||
"pageLimit": 7,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "pdfkit_compressed",
|
||||
"file": "pdfs/pdfkit_compressed.pdf",
|
||||
"md5": "ffe9c571d0a1572e234253e6c7cdee6c",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "tamreview",
|
||||
"file": "pdfs/TAMReview.pdf",
|
||||
"md5": "8039aba56790d3597d2bc8c794a51301",
|
||||
"rounds": 1,
|
||||
"pageLimit": 5,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "preistabelle",
|
||||
"file": "pdfs/preistabelle.pdf",
|
||||
"md5": "d2f0b2086160d4f3d325c79a5dc1fb4d",
|
||||
"rounds": 1,
|
||||
"pageLimit": 2,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1350",
|
||||
"file": "pdfs/issue1350.pdf",
|
||||
"md5": "92f72a04a4d9d05b2dd433b51f32ab1f",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue925",
|
||||
"file": "pdfs/issue925.pdf",
|
||||
"md5": "f58fe943090aff89dcc8e771bc0db4c2",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1466",
|
||||
"file": "pdfs/issue1466.pdf",
|
||||
"md5": "8a8877432e5bb10cfd50d60488d947bb",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1133",
|
||||
"file": "pdfs/issue1133.pdf",
|
||||
"md5": "d1b61580cb100e3df93d33703af1773a",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1049",
|
||||
"file": "pdfs/issue1049.pdf",
|
||||
"md5": "15473fffcdde9fb8f3756a4cf1aab347",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1155",
|
||||
"file": "pdfs/issue1155.pdf",
|
||||
"md5": "b732ef25c16c9c20a77e40edef5aa6fe",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1002",
|
||||
"file": "pdfs/issue1002.pdf",
|
||||
"md5": "af62d6cd95079322d4af18edd960d15c",
|
||||
"rounds": 1,
|
||||
"link": false,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1243",
|
||||
"file": "pdfs/issue1243.pdf",
|
||||
"md5": "130c849b83513d5ac5e03c6421fc7489",
|
||||
"rounds": 1,
|
||||
"pageLimit": 2,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1257",
|
||||
"file": "pdfs/issue1257.pdf",
|
||||
"md5": "9111533826bc21ed774e8e01603a2f54",
|
||||
"rounds": 1,
|
||||
"pageLimit": 2,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1309",
|
||||
"file": "pdfs/issue1309.pdf",
|
||||
"md5": "e835fb7f3dab3073ad37d0bd3c6399fa",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1317",
|
||||
"file": "pdfs/issue1317.pdf",
|
||||
"md5": "6fb46275b30c48c8985617d4f86199e3",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "gradientfill",
|
||||
"file": "pdfs/gradientfill.pdf",
|
||||
"md5": "cbc1988e4803f647fa83467a85f0e231",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
}
|
||||
]
|
||||
|
@ -23,6 +23,7 @@
|
||||
<script type="text/javascript" src="/src/worker.js"></script>
|
||||
<script type="text/javascript" src="/external/jpgjs/jpg.js"></script>
|
||||
<script type="text/javascript" src="/src/jpx.js"></script>
|
||||
<script type="text/javascript" src="/src/bidi.js"></script>
|
||||
<script type="text/javascript" src="driver.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
223
test/unit/font_spec.js
Normal file
@ -0,0 +1,223 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('font', function() {
|
||||
function hexDump(bytes) {
|
||||
var line = '';
|
||||
for (var i = 0, ii = bytes.length; i < ii; ++i) {
|
||||
var b = bytes[i].toString(16);
|
||||
if (b.length < 2)
|
||||
b = '0' + b;
|
||||
line += b.toString(16);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
// This example font comes from the CFF spec:
|
||||
// http://www.adobe.com/content/dam/Adobe/en/devnet/font/pdfs/5176.CFF.pdf
|
||||
var exampleFont = '0100040100010101134142434445462b' +
|
||||
'54696d65732d526f6d616e000101011f' +
|
||||
'f81b00f81c02f81d03f819041c6f000d' +
|
||||
'fb3cfb6efa7cfa1605e911b8f1120003' +
|
||||
'01010813183030312e30303754696d65' +
|
||||
'7320526f6d616e54696d657300000002' +
|
||||
'010102030e0e7d99f92a99fb7695f773' +
|
||||
'8b06f79a93fc7c8c077d99f85695f75e' +
|
||||
'9908fb6e8cf87393f7108b09a70adf0b' +
|
||||
'f78e14';
|
||||
var fontData = [];
|
||||
for (var i = 0; i < exampleFont.length; i += 2) {
|
||||
var hex = exampleFont.substr(i, 2);
|
||||
fontData.push(parseInt(hex, 16));
|
||||
}
|
||||
var bytes = new Uint8Array(fontData);
|
||||
fontData = {getBytes: function() { return bytes}};
|
||||
|
||||
function bytesToString(bytesArray) {
|
||||
var str = '';
|
||||
for (var i = 0, ii = bytesArray.length; i < ii; i++)
|
||||
str += String.fromCharCode(bytesArray[i]);
|
||||
return str;
|
||||
}
|
||||
|
||||
describe('CFFParser', function() {
|
||||
var parser = new CFFParser(fontData);
|
||||
var cff = parser.parse();
|
||||
|
||||
it('parses header', function() {
|
||||
var header = cff.header;
|
||||
expect(header.major).toEqual(1);
|
||||
expect(header.minor).toEqual(0);
|
||||
expect(header.hdrSize).toEqual(4);
|
||||
expect(header.offSize).toEqual(1);
|
||||
});
|
||||
|
||||
it('parses name index', function() {
|
||||
var names = cff.names;
|
||||
expect(names.length).toEqual(1);
|
||||
expect(names[0]).toEqual('ABCDEF+Times-Roman');
|
||||
});
|
||||
|
||||
it('sanitizes name index', function() {
|
||||
var index = new CFFIndex();
|
||||
index.add(['['.charCodeAt(0), 'a'.charCodeAt(0)]);
|
||||
|
||||
var names = parser.parseNameIndex(index);
|
||||
expect(names).toEqual(['_a']);
|
||||
|
||||
index = new CFFIndex();
|
||||
var longName = [];
|
||||
for (var i = 0; i < 129; i++)
|
||||
longName.push(0);
|
||||
index.add(longName);
|
||||
names = parser.parseNameIndex(index);
|
||||
expect(names[0].length).toEqual(127);
|
||||
});
|
||||
|
||||
it('parses string index', function() {
|
||||
var strings = cff.strings;
|
||||
expect(strings.count).toEqual(3);
|
||||
expect(strings.get(0)).toEqual('.notdef');
|
||||
expect(strings.get(391)).toEqual('001.007');
|
||||
});
|
||||
|
||||
it('parses top dict', function() {
|
||||
var topDict = cff.topDict;
|
||||
// 391 version 392 FullName 393 FamilyName 389 Weight 28416 UniqueID
|
||||
// -168 -218 1000 898 FontBBox 94 CharStrings 45 102 Private
|
||||
expect(topDict.getByName('version')).toEqual(391);
|
||||
expect(topDict.getByName('FullName')).toEqual(392);
|
||||
expect(topDict.getByName('FamilyName')).toEqual(393);
|
||||
expect(topDict.getByName('Weight')).toEqual(389);
|
||||
expect(topDict.getByName('UniqueID')).toEqual(28416);
|
||||
expect(topDict.getByName('FontBBox')).toEqual([-168, -218, 1000, 898]);
|
||||
expect(topDict.getByName('CharStrings')).toEqual(94);
|
||||
expect(topDict.getByName('Private')).toEqual([45, 102]);
|
||||
});
|
||||
|
||||
it('parses predefined charsets', function() {
|
||||
var charset = parser.parseCharsets(0, 0, null, true);
|
||||
expect(charset.predefined).toEqual(true);
|
||||
});
|
||||
|
||||
it('parses charset format 0', function() {
|
||||
// The first three bytes make the offset large enough to skip predefined.
|
||||
var bytes = new Uint8Array([0x00, 0x00, 0x00,
|
||||
0x00, // format
|
||||
0x00, 0x02 // sid/cid
|
||||
]);
|
||||
parser.bytes = bytes;
|
||||
var charset = parser.parseCharsets(3, 2, new CFFStrings(), false);
|
||||
expect(charset.charset[1]).toEqual('exclam');
|
||||
|
||||
// CID font
|
||||
var charset = parser.parseCharsets(3, 2, new CFFStrings(), true);
|
||||
expect(charset.charset[1]).toEqual(2);
|
||||
});
|
||||
|
||||
it('parses charset format 1', function() {
|
||||
// The first three bytes make the offset large enough to skip predefined.
|
||||
var bytes = new Uint8Array([0x00, 0x00, 0x00,
|
||||
0x01, // format
|
||||
0x00, 0x08, // sid/cid start
|
||||
0x01 // sid/cid left
|
||||
]);
|
||||
parser.bytes = bytes;
|
||||
var charset = parser.parseCharsets(3, 2, new CFFStrings(), false);
|
||||
expect(charset.charset).toEqual(['.notdef', 'quoteright', 'parenleft']);
|
||||
|
||||
// CID font
|
||||
var charset = parser.parseCharsets(3, 2, new CFFStrings(), true);
|
||||
expect(charset.charset).toEqual(['.notdef', 8, 9]);
|
||||
});
|
||||
|
||||
it('parses charset format 2', function() {
|
||||
// format 2 is the same as format 1 but the left is card16
|
||||
// The first three bytes make the offset large enough to skip predefined.
|
||||
var bytes = new Uint8Array([0x00, 0x00, 0x00,
|
||||
0x02, // format
|
||||
0x00, 0x08, // sid/cid start
|
||||
0x00, 0x01 // sid/cid left
|
||||
]);
|
||||
parser.bytes = bytes;
|
||||
var charset = parser.parseCharsets(3, 2, new CFFStrings(), false);
|
||||
expect(charset.charset).toEqual(['.notdef', 'quoteright', 'parenleft']);
|
||||
|
||||
// CID font
|
||||
var charset = parser.parseCharsets(3, 2, new CFFStrings(), true);
|
||||
expect(charset.charset).toEqual(['.notdef', 8, 9]);
|
||||
});
|
||||
|
||||
it('parses encoding format 0', function() {
|
||||
// The first two bytes make the offset large enough to skip predefined.
|
||||
var bytes = new Uint8Array([0x00, 0x00,
|
||||
0x00, // format
|
||||
0x01, // count
|
||||
0x08 // start
|
||||
]);
|
||||
parser.bytes = bytes;
|
||||
var encoding = parser.parseEncoding(2, {}, new CFFStrings(), null);
|
||||
expect(encoding.encoding).toEqual({0x8: 1});
|
||||
});
|
||||
|
||||
it('parses encoding format 1', function() {
|
||||
// The first two bytes make the offset large enough to skip predefined.
|
||||
var bytes = new Uint8Array([0x00, 0x00,
|
||||
0x01, // format
|
||||
0x01, // num ranges
|
||||
0x07, // range1 start
|
||||
0x01 // range2 left
|
||||
]);
|
||||
parser.bytes = bytes;
|
||||
var encoding = parser.parseEncoding(2, {}, new CFFStrings(), null);
|
||||
expect(encoding.encoding).toEqual({0x7: 0x01, 0x08: 0x02});
|
||||
});
|
||||
|
||||
it('parses fdselect format 0', function() {
|
||||
var bytes = new Uint8Array([0x00, // format
|
||||
0x00, // gid: 0 fd: 0
|
||||
0x01 // gid: 1 fd: 1
|
||||
]);
|
||||
parser.bytes = bytes;
|
||||
var fdSelect = parser.parseFDSelect(0, 2);
|
||||
expect(fdSelect.fdSelect).toEqual([0, 1]);
|
||||
});
|
||||
|
||||
it('parses fdselect format 3', function() {
|
||||
var bytes = new Uint8Array([0x03, // format
|
||||
0x00, 0x02, // range count
|
||||
0x00, 0x00, // first gid
|
||||
0x09, // font dict 1 id
|
||||
0x00, 0x02, // nex gid
|
||||
0x0a, // font dict 2 gid
|
||||
0x00, 0x04 // sentinel (last gid)
|
||||
]);
|
||||
parser.bytes = bytes;
|
||||
var fdSelect = parser.parseFDSelect(0, 2);
|
||||
expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
|
||||
});
|
||||
// TODO fdArray
|
||||
});
|
||||
describe('CFFCompiler', function() {
|
||||
it('encodes integers', function() {
|
||||
var c = new CFFCompiler();
|
||||
// all the examples from the spec
|
||||
expect(c.encodeInteger(0)).toEqual([0x8b]);
|
||||
expect(c.encodeInteger(100)).toEqual([0xef]);
|
||||
expect(c.encodeInteger(-100)).toEqual([0x27]);
|
||||
expect(c.encodeInteger(1000)).toEqual([0xfa, 0x7c]);
|
||||
expect(c.encodeInteger(-1000)).toEqual([0xfe, 0x7c]);
|
||||
expect(c.encodeInteger(10000)).toEqual([0x1c, 0x27, 0x10]);
|
||||
expect(c.encodeInteger(-10000)).toEqual([0x1c, 0xd8, 0xf0]);
|
||||
expect(c.encodeInteger(100000)).toEqual([0x1d, 0x00, 0x01, 0x86, 0xa0]);
|
||||
expect(c.encodeInteger(-100000)).toEqual([0x1d, 0xff, 0xfe, 0x79, 0x60]);
|
||||
});
|
||||
it('encodes floats', function() {
|
||||
var c = new CFFCompiler();
|
||||
expect(c.encodeFloat(-2.25)).toEqual([0x1e, 0xe2, 0xa2, 0x5f]);
|
||||
expect(c.encodeFloat(5e-11)).toEqual([0x1e, 0x5c, 0x11, 0xff]);
|
||||
});
|
||||
// TODO a lot more compiler tests
|
||||
});
|
||||
});
|
@ -22,8 +22,10 @@ load:
|
||||
- ../../src/pattern.js
|
||||
- ../../src/stream.js
|
||||
- ../../src/worker.js
|
||||
- ../../src/bidi.js
|
||||
- ../../external/jpgjs/jpg.js
|
||||
- ../unit/obj_spec.js
|
||||
- ../unit/font_spec.js
|
||||
- ../unit/function_spec.js
|
||||
- ../unit/crypto_spec.js
|
||||
- ../unit/stream_spec.js
|
||||
|
@ -217,10 +217,20 @@
|
||||
var div = document.createElement('div');
|
||||
if ('dataset' in div)
|
||||
return; // dataset property exists
|
||||
Object.defineProperty(HTMLElement.prototype, 'dataset', {
|
||||
get: function htmlElementDatasetGetter() {
|
||||
// adding dataset field to the actual object
|
||||
return (this.dataset = {});
|
||||
var oldCreateElement = document.createElement;
|
||||
document.createElement = function newCreateElement() {
|
||||
var result = oldCreateElement.apply(document, arguments);
|
||||
if (arguments[0] === 'div') {
|
||||
// creating dataset property for the div elements
|
||||
result.dataset = {};
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
|
||||
// Check console compatability
|
||||
(function checkConsoleCompatibility() {
|
||||
if (typeof console == 'undefined') {
|
||||
console = {log: function() {}};
|
||||
}
|
||||
})();
|
||||
|
475
web/debugger.js
Normal file
@ -0,0 +1,475 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
var FontInspector = (function FontInspectorClosure() {
|
||||
var fonts;
|
||||
var panelWidth = 300;
|
||||
var active = false;
|
||||
var fontAttribute = 'data-font-name';
|
||||
function removeSelection() {
|
||||
var divs = document.querySelectorAll('div[' + fontAttribute + ']');
|
||||
for (var i = 0, ii = divs.length; i < ii; ++i) {
|
||||
var div = divs[i];
|
||||
div.className = '';
|
||||
}
|
||||
}
|
||||
function resetSelection() {
|
||||
var divs = document.querySelectorAll('div[' + fontAttribute + ']');
|
||||
for (var i = 0, ii = divs.length; i < ii; ++i) {
|
||||
var div = divs[i];
|
||||
div.className = 'debuggerHideText';
|
||||
}
|
||||
}
|
||||
function selectFont(fontName, show) {
|
||||
var divs = document.querySelectorAll('div[' + fontAttribute + '=' +
|
||||
fontName + ']');
|
||||
for (var i = 0, ii = divs.length; i < ii; ++i) {
|
||||
var div = divs[i];
|
||||
div.className = show ? 'debuggerShowText' : 'debuggerHideText';
|
||||
}
|
||||
}
|
||||
function textLayerClick(e) {
|
||||
if (!e.target.dataset.fontName || e.target.tagName != 'DIV')
|
||||
return;
|
||||
var fontName = e.target.dataset.fontName;
|
||||
var selects = document.getElementsByTagName('input');
|
||||
for (var i = 0; i < selects.length; ++i) {
|
||||
var select = selects[i];
|
||||
if (select.dataset.fontName != fontName) continue;
|
||||
select.checked = !select.checked;
|
||||
selectFont(fontName, select.checked);
|
||||
select.scrollIntoView();
|
||||
}
|
||||
}
|
||||
return {
|
||||
// Poperties/functions needed by PDFBug.
|
||||
id: 'FontInspector',
|
||||
name: 'Font Inspector',
|
||||
panel: null,
|
||||
manager: null,
|
||||
init: function init() {
|
||||
var panel = this.panel;
|
||||
panel.setAttribute('style', 'padding: 5px;');
|
||||
var tmp = document.createElement('button');
|
||||
tmp.addEventListener('click', resetSelection);
|
||||
tmp.textContent = 'Refresh';
|
||||
panel.appendChild(tmp);
|
||||
|
||||
fonts = document.createElement('div');
|
||||
panel.appendChild(fonts);
|
||||
},
|
||||
enabled: false,
|
||||
get active() {
|
||||
return active;
|
||||
},
|
||||
set active(value) {
|
||||
active = value;
|
||||
if (active) {
|
||||
document.body.addEventListener('click', textLayerClick, true);
|
||||
resetSelection();
|
||||
} else {
|
||||
document.body.removeEventListener('click', textLayerClick, true);
|
||||
removeSelection();
|
||||
}
|
||||
},
|
||||
// FontInspector specific functions.
|
||||
fontAdded: function fontAdded(fontObj, url) {
|
||||
function properties(obj, list) {
|
||||
var moreInfo = document.createElement('table');
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var tr = document.createElement('tr');
|
||||
var td1 = document.createElement('td');
|
||||
td1.textContent = list[i];
|
||||
tr.appendChild(td1);
|
||||
var td2 = document.createElement('td');
|
||||
td2.textContent = obj[list[i]].toString();
|
||||
tr.appendChild(td2);
|
||||
moreInfo.appendChild(tr);
|
||||
}
|
||||
return moreInfo;
|
||||
}
|
||||
var moreInfo = properties(fontObj, ['name', 'type']);
|
||||
var m = /url\(['"]?([^\)"']+)/.exec(url);
|
||||
var fontName = fontObj.loadedName;
|
||||
var font = document.createElement('div');
|
||||
var name = document.createElement('span');
|
||||
name.textContent = fontName;
|
||||
var download = document.createElement('a');
|
||||
download.href = m[1];
|
||||
download.textContent = 'Download';
|
||||
var logIt = document.createElement('a');
|
||||
logIt.href = '';
|
||||
logIt.textContent = 'Log';
|
||||
logIt.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
console.log(fontObj);
|
||||
});
|
||||
var select = document.createElement('input');
|
||||
select.setAttribute('type', 'checkbox');
|
||||
select.dataset.fontName = fontName;
|
||||
select.addEventListener('click', (function(select, fontName) {
|
||||
return (function() {
|
||||
selectFont(fontName, select.checked);
|
||||
});
|
||||
})(select, fontName));
|
||||
font.appendChild(select);
|
||||
font.appendChild(name);
|
||||
font.appendChild(document.createTextNode(' '));
|
||||
font.appendChild(download);
|
||||
font.appendChild(document.createTextNode(' '));
|
||||
font.appendChild(logIt);
|
||||
font.appendChild(moreInfo);
|
||||
fonts.appendChild(font);
|
||||
// Somewhat of a hack, should probably add a hook for when the text layer
|
||||
// is done rendering.
|
||||
setTimeout(function() {
|
||||
if (this.active)
|
||||
resetSelection();
|
||||
}.bind(this), 2000);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Manages all the page steppers.
|
||||
var StepperManager = (function StepperManagerClosure() {
|
||||
var steppers = [];
|
||||
var stepperDiv = null;
|
||||
var stepperControls = null;
|
||||
var stepperChooser = null;
|
||||
var breakPoints = {};
|
||||
return {
|
||||
// Poperties/functions needed by PDFBug.
|
||||
id: 'Stepper',
|
||||
name: 'Stepper',
|
||||
panel: null,
|
||||
manager: null,
|
||||
init: function init() {
|
||||
var self = this;
|
||||
this.panel.setAttribute('style', 'padding: 5px;');
|
||||
stepperControls = document.createElement('div');
|
||||
stepperChooser = document.createElement('select');
|
||||
stepperChooser.addEventListener('change', function(event) {
|
||||
self.selectStepper(this.value);
|
||||
});
|
||||
stepperControls.appendChild(stepperChooser);
|
||||
stepperDiv = document.createElement('div');
|
||||
this.panel.appendChild(stepperControls);
|
||||
this.panel.appendChild(stepperDiv);
|
||||
if (sessionStorage.getItem('pdfjsBreakPoints'))
|
||||
breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));
|
||||
},
|
||||
enabled: false,
|
||||
active: false,
|
||||
// Stepper specific functions.
|
||||
create: function create(pageNumber) {
|
||||
var debug = document.createElement('div');
|
||||
debug.id = 'stepper' + pageNumber;
|
||||
debug.setAttribute('hidden', true);
|
||||
debug.className = 'stepper';
|
||||
stepperDiv.appendChild(debug);
|
||||
var b = document.createElement('option');
|
||||
b.textContent = 'Page ' + (pageNumber + 1);
|
||||
b.value = pageNumber;
|
||||
stepperChooser.appendChild(b);
|
||||
var initBreakPoints = breakPoints[pageNumber] || [];
|
||||
var stepper = new Stepper(debug, pageNumber, initBreakPoints);
|
||||
steppers.push(stepper);
|
||||
if (steppers.length === 1)
|
||||
this.selectStepper(pageNumber, false);
|
||||
return stepper;
|
||||
},
|
||||
selectStepper: function selectStepper(pageNumber, selectPanel) {
|
||||
if (selectPanel)
|
||||
this.manager.selectPanel(1);
|
||||
for (var i = 0; i < steppers.length; ++i) {
|
||||
var stepper = steppers[i];
|
||||
if (stepper.pageNumber == pageNumber)
|
||||
stepper.panel.removeAttribute('hidden');
|
||||
else
|
||||
stepper.panel.setAttribute('hidden', true);
|
||||
}
|
||||
var options = stepperChooser.options;
|
||||
for (var i = 0; i < options.length; ++i) {
|
||||
var option = options[i];
|
||||
option.selected = option.value == pageNumber;
|
||||
}
|
||||
},
|
||||
saveBreakPoints: function saveBreakPoints(pageNumber, bps) {
|
||||
breakPoints[pageNumber] = bps;
|
||||
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// The stepper for each page's IRQueue.
|
||||
var Stepper = (function StepperClosure() {
|
||||
function Stepper(panel, pageNumber, initialBreakPoints) {
|
||||
this.panel = panel;
|
||||
this.len;
|
||||
this.breakPoint = 0;
|
||||
this.nextBreakPoint = null;
|
||||
this.pageNumber = pageNumber;
|
||||
this.breakPoints = initialBreakPoints;
|
||||
this.currentIdx = -1;
|
||||
}
|
||||
Stepper.prototype = {
|
||||
init: function init(IRQueue) {
|
||||
// Shorter way to create element and optionally set textContent.
|
||||
function c(tag, textContent) {
|
||||
var d = document.createElement(tag);
|
||||
if (textContent)
|
||||
d.textContent = textContent;
|
||||
return d;
|
||||
}
|
||||
var panel = this.panel;
|
||||
this.len = IRQueue.fnArray.length;
|
||||
var content = c('div', 'c=continue, s=step');
|
||||
var table = c('table');
|
||||
content.appendChild(table);
|
||||
table.cellSpacing = 0;
|
||||
var headerRow = c('tr');
|
||||
table.appendChild(headerRow);
|
||||
headerRow.appendChild(c('th', 'Break'));
|
||||
headerRow.appendChild(c('th', 'Idx'));
|
||||
headerRow.appendChild(c('th', 'fn'));
|
||||
headerRow.appendChild(c('th', 'args'));
|
||||
|
||||
for (var i = 0; i < IRQueue.fnArray.length; i++) {
|
||||
var line = c('tr');
|
||||
line.className = 'line';
|
||||
line.dataset.idx = i;
|
||||
table.appendChild(line);
|
||||
var checked = this.breakPoints.indexOf(i) != -1;
|
||||
var args = IRQueue.argsArray[i] ? IRQueue.argsArray[i] : [];
|
||||
|
||||
var breakCell = c('td');
|
||||
var cbox = c('input');
|
||||
cbox.type = 'checkbox';
|
||||
cbox.className = 'points';
|
||||
cbox.checked = checked;
|
||||
var self = this;
|
||||
cbox.onclick = (function(x) {
|
||||
return function() {
|
||||
if (this.checked)
|
||||
self.breakPoints.push(x);
|
||||
else
|
||||
self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
|
||||
StepperManager.saveBreakPoints(self.pageNumber, self.breakPoints);
|
||||
}
|
||||
})(i);
|
||||
|
||||
breakCell.appendChild(cbox);
|
||||
line.appendChild(breakCell);
|
||||
line.appendChild(c('td', i.toString()));
|
||||
line.appendChild(c('td', IRQueue.fnArray[i]));
|
||||
line.appendChild(c('td', args.join(', ')));
|
||||
}
|
||||
panel.appendChild(content);
|
||||
var self = this;
|
||||
},
|
||||
getNextBreakPoint: function getNextBreakPoint() {
|
||||
this.breakPoints.sort(function(a, b) { return a - b; });
|
||||
for (var i = 0; i < this.breakPoints.length; i++) {
|
||||
if (this.breakPoints[i] > this.currentIdx)
|
||||
return this.breakPoints[i];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
breakIt: function breakIt(idx, callback) {
|
||||
StepperManager.selectStepper(this.pageNumber, true);
|
||||
var self = this;
|
||||
var dom = document;
|
||||
self.currentIdx = idx;
|
||||
var listener = function(e) {
|
||||
switch (e.keyCode) {
|
||||
case 83: // step
|
||||
dom.removeEventListener('keydown', listener, false);
|
||||
self.nextBreakPoint = self.currentIdx + 1;
|
||||
self.goTo(-1);
|
||||
callback();
|
||||
break;
|
||||
case 67: // continue
|
||||
dom.removeEventListener('keydown', listener, false);
|
||||
var breakPoint = self.getNextBreakPoint();
|
||||
self.nextBreakPoint = breakPoint;
|
||||
self.goTo(-1);
|
||||
callback();
|
||||
break;
|
||||
}
|
||||
}
|
||||
dom.addEventListener('keydown', listener, false);
|
||||
self.goTo(idx);
|
||||
},
|
||||
goTo: function goTo(idx) {
|
||||
var allRows = this.panel.getElementsByClassName('line');
|
||||
for (var x = 0, xx = allRows.length; x < xx; ++x) {
|
||||
var row = allRows[x];
|
||||
if (row.dataset.idx == idx) {
|
||||
row.style.backgroundColor = 'rgb(251,250,207)';
|
||||
row.scrollIntoView();
|
||||
} else {
|
||||
row.style.backgroundColor = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return Stepper;
|
||||
})();
|
||||
|
||||
var Stats = (function Stats() {
|
||||
var stats = [];
|
||||
function clear(node) {
|
||||
while (node.hasChildNodes())
|
||||
node.removeChild(node.lastChild);
|
||||
}
|
||||
function getStatIndex(pageNumber) {
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
if (stats[i].pageNumber === pageNumber)
|
||||
return i;
|
||||
return false;
|
||||
}
|
||||
return {
|
||||
// Poperties/functions needed by PDFBug.
|
||||
id: 'Stats',
|
||||
name: 'Stats',
|
||||
panel: null,
|
||||
manager: null,
|
||||
init: function init() {
|
||||
this.panel.setAttribute('style', 'padding: 5px;');
|
||||
PDFJS.enableStats = true;
|
||||
},
|
||||
enabled: false,
|
||||
active: false,
|
||||
// Stats specific functions.
|
||||
add: function(pageNumber, stat) {
|
||||
if (!stat)
|
||||
return;
|
||||
var statsIndex = getStatIndex(pageNumber);
|
||||
if (statsIndex !== false) {
|
||||
var b = stats[statsIndex];
|
||||
this.panel.removeChild(b.div);
|
||||
stats.splice(statsIndex, 1);
|
||||
}
|
||||
var wrapper = document.createElement('div');
|
||||
wrapper.className = 'stats';
|
||||
var title = document.createElement('div');
|
||||
title.className = 'title';
|
||||
title.textContent = 'Page: ' + pageNumber;
|
||||
var statsDiv = document.createElement('div');
|
||||
statsDiv.textContent = stat.toString();
|
||||
wrapper.appendChild(title);
|
||||
wrapper.appendChild(statsDiv);
|
||||
stats.push({ pageNumber: pageNumber, div: wrapper });
|
||||
stats.sort(function(a, b) { return a.pageNumber - b.pageNumber});
|
||||
clear(this.panel);
|
||||
for (var i = 0, ii = stats.length; i < ii; ++i)
|
||||
this.panel.appendChild(stats[i].div);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Manages all the debugging tools.
|
||||
var PDFBug = (function PDFBugClosure() {
|
||||
var panelWidth = 300;
|
||||
var buttons = [];
|
||||
var activePanel = null;
|
||||
|
||||
return {
|
||||
tools: [
|
||||
FontInspector,
|
||||
StepperManager,
|
||||
Stats
|
||||
],
|
||||
enable: function(ids) {
|
||||
var all = false, tools = this.tools;
|
||||
if (ids.length === 1 && ids[0] === 'all')
|
||||
all = true;
|
||||
for (var i = 0; i < tools.length; ++i) {
|
||||
var tool = tools[i];
|
||||
if (all || ids.indexOf(tool.id) !== -1)
|
||||
tool.enabled = true;
|
||||
}
|
||||
if (!all) {
|
||||
// Sort the tools by the order they are enabled.
|
||||
tools.sort(function(a, b) {
|
||||
var indexA = ids.indexOf(a.id);
|
||||
indexA = indexA < 0 ? tools.length : indexA;
|
||||
var indexB = ids.indexOf(b.id);
|
||||
indexB = indexB < 0 ? tools.length : indexB;
|
||||
return indexA - indexB;
|
||||
});
|
||||
}
|
||||
},
|
||||
init: function init() {
|
||||
/*
|
||||
* Basic Layout:
|
||||
* PDFBug
|
||||
* Controls
|
||||
* Panels
|
||||
* Panel
|
||||
* Panel
|
||||
* ...
|
||||
*/
|
||||
var ui = document.createElement('div');
|
||||
ui.id = 'PDFBug';
|
||||
|
||||
var controls = document.createElement('div');
|
||||
controls.setAttribute('class', 'controls');
|
||||
ui.appendChild(controls);
|
||||
|
||||
var panels = document.createElement('div');
|
||||
panels.setAttribute('class', 'panels');
|
||||
ui.appendChild(panels);
|
||||
|
||||
document.body.appendChild(ui);
|
||||
document.body.style.paddingRight = panelWidth + 'px';
|
||||
|
||||
// Initialize all the debugging tools.
|
||||
var tools = this.tools;
|
||||
for (var i = 0; i < tools.length; ++i) {
|
||||
var tool = tools[i];
|
||||
var panel = document.createElement('div');
|
||||
var panelButton = document.createElement('button');
|
||||
panelButton.textContent = tool.name;
|
||||
var self = this;
|
||||
panelButton.addEventListener('click', (function(selected) {
|
||||
return function(event) {
|
||||
event.preventDefault();
|
||||
self.selectPanel(selected);
|
||||
};
|
||||
})(i));
|
||||
controls.appendChild(panelButton);
|
||||
panels.appendChild(panel);
|
||||
tool.panel = panel;
|
||||
tool.manager = this;
|
||||
if (tool.enabled)
|
||||
tool.init();
|
||||
else
|
||||
panel.textContent = tool.name + ' is disabled. To enable add ' +
|
||||
' "' + tool.id + '" to the pdfBug parameter ' +
|
||||
'and refresh (seperate multiple by commas).';
|
||||
buttons.push(panelButton);
|
||||
}
|
||||
this.selectPanel(0);
|
||||
},
|
||||
selectPanel: function selectPanel(index) {
|
||||
if (index === activePanel)
|
||||
return;
|
||||
activePanel = index;
|
||||
var tools = this.tools;
|
||||
for (var j = 0; j < tools.length; ++j) {
|
||||
if (j == index) {
|
||||
buttons[j].setAttribute('class', 'active');
|
||||
tools[j].active = true;
|
||||
tools[j].panel.removeAttribute('hidden');
|
||||
} else {
|
||||
buttons[j].setAttribute('class', '');
|
||||
tools[j].active = false;
|
||||
tools[j].panel.setAttribute('hidden', 'true');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
@ -20,7 +20,8 @@
|
||||
height="48.000000px"
|
||||
width="48.000000px"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
version="1.1">
|
||||
version="1.1"
|
||||
viewbox="0 0 48 48">
|
||||
<defs
|
||||
id="defs3">
|
||||
<inkscape:perspective
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -16,7 +16,8 @@
|
||||
id="svg2994"
|
||||
height="48px"
|
||||
width="48px"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
viewbox="0 0 48 48">
|
||||
<defs
|
||||
id="defs3">
|
||||
<inkscape:perspective
|
||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@ -16,7 +16,8 @@
|
||||
id="svg2913"
|
||||
height="48px"
|
||||
width="48px"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
viewbox="0 0 48 48">
|
||||
<defs
|
||||
id="defs3">
|
||||
<inkscape:perspective
|
||||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
@ -19,7 +19,8 @@
|
||||
inkscape:export-filename="/home/jimmac/Desktop/wi-fi.png"
|
||||
inkscape:export-xdpi="90.000000"
|
||||
inkscape:export-ydpi="90.000000"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
viewbox="0 0 48 48">
|
||||
<defs
|
||||
id="defs3">
|
||||
<inkscape:perspective
|
||||
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.2 KiB |
@ -19,7 +19,8 @@
|
||||
inkscape:export-filename="/home/jimmac/Desktop/wi-fi.png"
|
||||
inkscape:export-xdpi="90.000000"
|
||||
inkscape:export-ydpi="90.000000"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
viewbox="0 0 48 48">
|
||||
<defs
|
||||
id="defs3">
|
||||
<inkscape:perspective
|
||||
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
BIN
web/images/loading-icon.gif
Normal file
After Width: | Height: | Size: 2.5 KiB |
297
web/images/pin-down.svg
Normal file
@ -0,0 +1,297 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="48"
|
||||
height="48"
|
||||
id="svg3075"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="pin-down.svg"
|
||||
viewPort="0 0 48 48">
|
||||
<defs
|
||||
id="defs3077">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3804">
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3806" />
|
||||
<stop
|
||||
style="stop-color:#000000;stop-opacity:0;"
|
||||
offset="1"
|
||||
id="stop3808" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3965">
|
||||
<stop
|
||||
id="stop3967"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3969" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3885">
|
||||
<stop
|
||||
style="stop-color:#a8b5e9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3889" />
|
||||
<stop
|
||||
id="stop3891"
|
||||
offset="1"
|
||||
style="stop-color:#1d4488;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3865">
|
||||
<stop
|
||||
style="stop-color:#0e0ec3;stop-opacity:0"
|
||||
offset="0"
|
||||
id="stop3867" />
|
||||
<stop
|
||||
id="stop3883"
|
||||
offset="0.5"
|
||||
style="stop-color:#95b1e4;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#0d29c0;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3869" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3853">
|
||||
<stop
|
||||
style="stop-color:#717171;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3855" />
|
||||
<stop
|
||||
id="stop3861"
|
||||
offset="0.5"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#818181;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3857" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3792"
|
||||
cx="13.508819"
|
||||
cy="30.521608"
|
||||
fx="13.508819"
|
||||
fy="30.521608"
|
||||
r="13.254341"
|
||||
gradientTransform="matrix(1,0,0,1.045977,0,-1.4434017)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="linearGradient3802"
|
||||
x1="15.306904"
|
||||
y1="13.407407"
|
||||
x2="29.35461"
|
||||
y2="30.15519"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2304178,0,0,1.1235308,-2.1158755,998.83747)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3804"
|
||||
id="radialGradient3812"
|
||||
cx="20.111172"
|
||||
cy="28.238274"
|
||||
fx="20.111172"
|
||||
fy="28.238274"
|
||||
r="7.6291947"
|
||||
gradientTransform="matrix(1.2304178,0,0,1.1452771,-2.1158755,998.22337)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3822"
|
||||
cx="23.985939"
|
||||
cy="24.847366"
|
||||
fx="23.985939"
|
||||
fy="24.847366"
|
||||
r="10.593476"
|
||||
gradientTransform="matrix(0.63682384,0.44303926,-1.1714282,1.6838088,35.523491,-26.055439)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter3856"
|
||||
x="-0.30370581"
|
||||
width="1.6074116"
|
||||
y="-0.32771564"
|
||||
height="1.6554313">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="4.7808869"
|
||||
id="feGaussianBlur3858" />
|
||||
</filter>
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3865"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,1.045977,0,-1.4434017)"
|
||||
cx="13.508819"
|
||||
cy="30.521608"
|
||||
fx="13.508819"
|
||||
fy="30.521608"
|
||||
r="13.254341" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="linearGradient3867"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2304178,0,0,1.1235308,-2.1158755,998.83747)"
|
||||
x1="15.306904"
|
||||
y1="13.407407"
|
||||
x2="29.35461"
|
||||
y2="30.15519" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3804"
|
||||
id="radialGradient3869"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.2304178,0,0,1.1452771,-2.1158755,998.22337)"
|
||||
cx="20.111172"
|
||||
cy="28.238274"
|
||||
fx="20.111172"
|
||||
fy="28.238274"
|
||||
r="7.6291947" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3871"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.63682384,0.44303926,-1.1714282,1.6838088,35.523491,-26.055439)"
|
||||
cx="23.985939"
|
||||
cy="24.847366"
|
||||
fx="23.985939"
|
||||
fy="24.847366"
|
||||
r="10.593476" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="linearGradient3875"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.98683814,0,0,0.9524914,3.4991888,1004.1467)"
|
||||
x1="15.306904"
|
||||
y1="13.407407"
|
||||
x2="29.35461"
|
||||
y2="30.15519" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3804"
|
||||
id="radialGradient3877"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.195641,0.23932984,-0.18533175,0.95255553,4.5333676,999.33159)"
|
||||
cx="20.111172"
|
||||
cy="28.238274"
|
||||
fx="20.111172"
|
||||
fy="28.238274"
|
||||
r="7.6291947" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3880"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1.5847553,0.52693722,-0.99805104,2.7064773,14.11088,-45.304477)"
|
||||
cx="18.133854"
|
||||
cy="19.778509"
|
||||
fx="18.133854"
|
||||
fy="19.778509"
|
||||
r="10.593476" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3882"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,1.045977,0,-1.4434017)"
|
||||
cx="13.508819"
|
||||
cy="30.521608"
|
||||
fx="13.508819"
|
||||
fy="30.521608"
|
||||
r="13.254341" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4.9558805"
|
||||
inkscape:cx="3.0237013"
|
||||
inkscape:cy="17.287267"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1291"
|
||||
inkscape:window-height="776"
|
||||
inkscape:window-x="16"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata3080">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1004.3622)">
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;filter:url(#filter3856)"
|
||||
d="m 14.326415,1019.2702 c -8.3327876,4.0675 -9.8235436,10.8833 -8.8783416,15.1336 4.6840646,7.9754 8.3608166,13.8165 24.0118786,12.9139 9.657617,-3.7312 12.9762,-9.3269 13.519293,-15.7389 -0.547269,-4.3839 -1.957958,-9.3396 -5.649854,-14.9317 -3.965534,-2.471 -6.300859,-4.4246 -10.290805,-4.2374 -8.25193,0.5026 -8.752485,4.4502 -12.712171,6.8605 z"
|
||||
id="path3826"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccc"
|
||||
transform="matrix(0.69099294,0,0,0.75978808,7.3427938,249.11025)" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:url(#radialGradient3882);fill-opacity:1;stroke:none"
|
||||
id="path3011"
|
||||
sodipodi:cx="21.176477"
|
||||
sodipodi:cy="31.393986"
|
||||
sodipodi:rx="13.254341"
|
||||
sodipodi:ry="13.863736"
|
||||
d="m 34.430819,31.393986 a 13.254341,13.863736 0 1 1 -26.5086827,0 13.254341,13.863736 0 1 1 26.5086827,0 z"
|
||||
transform="matrix(0.98683814,0,0,0.83062636,2.696034,1005.3655)" />
|
||||
<path
|
||||
style="fill:url(#linearGradient3875);fill-opacity:1;stroke:url(#radialGradient3877);stroke-width:0.9695127;stroke-opacity:1"
|
||||
d="m 17.246758,1026.7905 c -1.7156,4.5052 -2.482464,10.6205 8.726963,10.7476 4.849099,-1.8941 3.522783,-5.3561 6.021544,-11.8282 l -10.973104,-1.5977 z"
|
||||
id="path3794"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:url(#radialGradient3880);fill-opacity:1;stroke:none"
|
||||
id="path3814"
|
||||
sodipodi:cx="24.718111"
|
||||
sodipodi:cy="23.38278"
|
||||
sodipodi:rx="10.593476"
|
||||
sodipodi:ry="9.6854639"
|
||||
d="m 35.311587,23.38278 a 10.593476,9.6854639 0 1 1 -21.186952,0 10.593476,9.6854639 0 1 1 21.186952,0 z"
|
||||
transform="matrix(0.85425691,0,0,0.84187503,3.9779774,1006.7561)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 9.6 KiB |
230
web/images/pin-up.svg
Normal file
@ -0,0 +1,230 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="48"
|
||||
height="48"
|
||||
id="svg3075"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.1 r9760"
|
||||
sodipodi:docname="pin-up.svg"
|
||||
viewPort="0 0 48 48">
|
||||
<defs
|
||||
id="defs3077">
|
||||
<linearGradient
|
||||
id="linearGradient3965">
|
||||
<stop
|
||||
id="stop3967"
|
||||
offset="0"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:0"
|
||||
offset="1"
|
||||
id="stop3969" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3885">
|
||||
<stop
|
||||
style="stop-color:#a8b5e9;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3889" />
|
||||
<stop
|
||||
id="stop3891"
|
||||
offset="1"
|
||||
style="stop-color:#1d4488;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3865">
|
||||
<stop
|
||||
style="stop-color:#0e0ec3;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3867" />
|
||||
<stop
|
||||
id="stop3883"
|
||||
offset="0.5"
|
||||
style="stop-color:#95b1e4;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#0d29c0;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3869" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3853">
|
||||
<stop
|
||||
style="stop-color:#717171;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop3855" />
|
||||
<stop
|
||||
id="stop3861"
|
||||
offset="0.5"
|
||||
style="stop-color:#ffffff;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#818181;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3857" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3853"
|
||||
id="linearGradient3859"
|
||||
x1="7.7696066"
|
||||
y1="34.979828"
|
||||
x2="11.854106"
|
||||
y2="39.107044"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(4.8388015,1001.6582)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3871"
|
||||
cx="14.801222"
|
||||
cy="1030.6609"
|
||||
fx="14.801222"
|
||||
fy="1030.6609"
|
||||
r="10.177785"
|
||||
gradientTransform="matrix(1,0,0,1.0108042,4.8388015,-13.880529)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3865"
|
||||
id="linearGradient3881"
|
||||
x1="15.012629"
|
||||
y1="11.922465"
|
||||
x2="31.098303"
|
||||
y2="28.858271"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(1,0,0,0.97315436,4.8388015,1002.4769)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3885"
|
||||
id="radialGradient3909"
|
||||
cx="16.437693"
|
||||
cy="22.596292"
|
||||
fx="16.437693"
|
||||
fy="22.596292"
|
||||
r="1.7789712"
|
||||
gradientTransform="matrix(1,0,0,8.3599999,0,-166.30871)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3865"
|
||||
id="linearGradient3927"
|
||||
x1="26.47109"
|
||||
y1="1010.7343"
|
||||
x2="35.294788"
|
||||
y2="1019.8425"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(4.5541661,-2.1347654)" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3965"
|
||||
id="radialGradient3995"
|
||||
cx="23.189369"
|
||||
cy="25.704245"
|
||||
fx="23.189369"
|
||||
fy="25.704245"
|
||||
r="37.336674"
|
||||
gradientTransform="matrix(1,0,0,1.0332422,0,-0.85446479)"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<filter
|
||||
inkscape:collect="always"
|
||||
id="filter4009"
|
||||
x="-0.19299152"
|
||||
width="1.385983"
|
||||
y="-0.18351803"
|
||||
height="1.3670361">
|
||||
<feGaussianBlur
|
||||
inkscape:collect="always"
|
||||
stdDeviation="3.8667902"
|
||||
id="feGaussianBlur4011" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="3.2819435"
|
||||
inkscape:cx="18.697469"
|
||||
inkscape:cy="17.287267"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="970"
|
||||
inkscape:window-height="778"
|
||||
inkscape:window-x="284"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata3080">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1004.3622)">
|
||||
<path
|
||||
style="fill:url(#radialGradient3995);stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1.0;filter:url(#filter4009)"
|
||||
d="M -0.85390618,50.988672 14.231769,27.790888 C 12.21393,25.133052 9.5514307,24.605255 9.9622384,18.824874 13.947134,14.236899 17.362759,16.258973 21.347654,16.54779 l 8.966014,-8.6813789 c 1.467204,-2.4778468 -1.023584,-4.6422045 0.569271,-7.25820222 4.802307,-0.84764718 6.662499,1.15219542 11.527733,6.26197842 4.061691,4.1873637 5.648882,7.0611607 4.411848,9.5352857 -1.075122,2.776443 -4.518349,-0.692782 -5.835025,0.56927 l -9.108332,10.104556 c -0.418785,3.74872 2.078647,7.861968 -1.280859,11.243098 -4.132171,0.818036 -6.734336,-1.933944 -9.819921,-3.557942 z"
|
||||
id="path3955"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(0,1004.3622)"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
<g
|
||||
id="g3929">
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3083"
|
||||
d="m 3.2884874,1051.0662 c 3.1862139,-6.2911 11.3693156,-15.19 15.4471616,-20.0327 l 2.86533,3.0086 c -3.476851,3.6575 -10.192375,10.8664 -18.3124916,17.0241 z"
|
||||
style="fill:url(#linearGradient3859);fill-opacity:1;stroke:#a5a5a5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3863"
|
||||
d="m 11.10078,1023.3294 c 5.038264,10.1095 11.83652,14.8875 18.358981,18.2167 1.196291,-2.5422 1.454996,-5.6203 0,-9.6776 l -8.539061,-8.6814 c -3.704654,-1.8936 -6.871076,-1.3652 -9.81992,0.1423 z"
|
||||
style="fill:url(#radialGradient3871);fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3873"
|
||||
d="m 33.729292,1011.5171 -13.235545,11.4952 c 2.869602,4.2703 6.221839,7.4544 9.108332,9.1408 l 11.385416,-13.0187 z"
|
||||
style="fill:url(#linearGradient3881);fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3893"
|
||||
d="m 33.228885,1011.6148 c 1.843189,2.7806 3.431654,5.6597 7.19852,7.6953 l 5.398891,1.7423 c -7.6738,-4.7914 -10.989683,-9.5828 -13.947133,-14.3741 z"
|
||||
style="fill:url(#linearGradient3927);fill-opacity:1;stroke:none" />
|
||||
<path
|
||||
transform="matrix(0.68275275,-0.5590416,0.45791123,0.47036287,17.42507,1012.2127)"
|
||||
d="m 18.216664,22.596292 a 1.7789712,14.872199 0 1 1 -3.557943,0 1.7789712,14.872199 0 1 1 3.557943,0 z"
|
||||
sodipodi:ry="14.872199"
|
||||
sodipodi:rx="1.7789712"
|
||||
sodipodi:cy="22.596292"
|
||||
sodipodi:cx="16.437693"
|
||||
id="path3901"
|
||||
style="fill:url(#radialGradient3909);fill-opacity:1;stroke:none"
|
||||
sodipodi:type="arc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.1 KiB |
@ -16,7 +16,8 @@
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
|
||||
sodipodi:docname="list-add.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
viewbox="0 0 48 48">
|
||||
<defs
|
||||
id="defs6433">
|
||||
<inkscape:perspective
|
||||
@ -418,12 +419,12 @@
|
||||
d="M 33.278212 34.94062 A 10.31934 2.320194 0 1 1 12.639532,34.94062 A 10.31934 2.320194 0 1 1 33.278212 34.94062 z"
|
||||
transform="matrix(1.550487,0,0,1.978714,-12.4813,-32.49103)" />
|
||||
<path
|
||||
style="font-size:59.901077px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:#75a1d0;fill-opacity:1.0000000;stroke:#3465a4;stroke-width:1.0000004px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans"
|
||||
style="fill:#75a1d0;fill-opacity:1.0000000;stroke:#3465a4;stroke-width:1.0000004px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
d="M 27.514356,37.542682 L 27.514356,28.515722 L 37.492820,28.475543 L 37.492820,21.480219 L 27.523285,21.480219 L 27.514356,11.520049 L 20.498082,11.531210 L 20.502546,21.462362 L 10.512920,21.536022 L 10.477206,28.504561 L 20.511475,28.475543 L 20.518171,37.515896 L 27.514356,37.542682 z "
|
||||
id="text1314"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
<path
|
||||
style="font-size:59.901077px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;opacity:0.40860215;fill:url(#linearGradient4975);fill-opacity:1.0000000;stroke:url(#linearGradient7922);stroke-width:1.0000006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans"
|
||||
style="opacity:0.40860215;fill:url(#linearGradient4975);fill-opacity:1.0000000;stroke:url(#linearGradient7922);stroke-width:1.0000006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
d="M 26.498702,36.533920 L 26.498702,27.499738 L 36.501304,27.499738 L 36.494607,22.475309 L 26.507630,22.475309 L 26.507630,12.480335 L 21.512796,12.498193 L 21.521725,22.475309 L 11.495536,22.493166 L 11.468750,27.466256 L 21.533143,27.475185 L 21.519750,36.502670 L 26.498702,36.533920 z "
|
||||
id="path7076"
|
||||
sodipodi:nodetypes="ccccccccccccc" />
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -16,7 +16,8 @@
|
||||
inkscape:version="0.46"
|
||||
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
|
||||
sodipodi:docname="list-remove.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape">
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
viewbox="0 0 48 48">
|
||||
<defs
|
||||
id="defs6433">
|
||||
<inkscape:perspective
|
||||
@ -406,12 +407,12 @@
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<path
|
||||
style="font-size:59.901077px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;fill:#75a1d0;fill-opacity:1.0000000;stroke:#3465a4;stroke-width:1.0000004px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans"
|
||||
style="fill:#75a1d0;fill-opacity:1.0000000;stroke:#3465a4;stroke-width:1.0000004px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
d="M 27.514356,28.359472 L 39.633445,28.475543 L 39.633445,21.480219 L 27.523285,21.480219 L 20.502546,21.462362 L 8.5441705,21.489147 L 8.5084565,28.457686 L 20.511475,28.475543 L 27.514356,28.359472 z "
|
||||
id="text1314"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
<path
|
||||
style="font-size:59.901077px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125.00000%;writing-mode:lr-tb;text-anchor:start;opacity:0.40860215;fill:url(#linearGradient4975);fill-opacity:1.0000000;stroke:url(#linearGradient7922);stroke-width:1.0000006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000;font-family:Bitstream Vera Sans"
|
||||
style="opacity:0.40860215;fill:url(#linearGradient4975);fill-opacity:1.0000000;stroke:url(#linearGradient7922);stroke-width:1.0000006px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1.0000000"
|
||||
d="M 38.579429,27.484113 L 38.588357,22.475309 L 9.5267863,22.493166 L 9.5000003,27.466256 L 38.579429,27.484113 z "
|
||||
id="path7076"
|
||||
sodipodi:nodetypes="ccccc" />
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
14
web/viewer-snippet-firefox-extension.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!-- This snippet is used in firefox extension, see Makefile -->
|
||||
<base href="resource://pdf.js/web/" />
|
||||
<script type="text/javascript" id="PDFJS_SCRIPT_TAG">
|
||||
<!--
|
||||
// pdf.js is inlined here because resource:// urls won't work
|
||||
// for loading workers.
|
||||
/* PDFJSSCRIPT_INCLUDE_BUNDLE */
|
||||
-->
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
// This specifies the location of the pdf.js file.
|
||||
PDFJS.isFirefoxExtension = true;
|
||||
PDFJS.workerSrc = 'none';
|
||||
</script>
|
133
web/viewer.css
@ -9,7 +9,7 @@ body {
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* === Toolbar === */
|
||||
@ -65,16 +65,6 @@ body {
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
span#info {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@-moz-document regexp("http:.*debug=1.*") {
|
||||
span#info {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
/* === Sidebar === */
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
@ -89,7 +79,8 @@ span#info {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#sidebar:hover {
|
||||
#sidebar:hover,
|
||||
#sidebar.pinned {
|
||||
left: 0px;
|
||||
transition: left 0.25s ease-in-out 0s;
|
||||
-o-transition: left 0.25s ease-in-out 0s;
|
||||
@ -97,6 +88,26 @@ span#info {
|
||||
-webkit-transition: left 0.25s ease-in-out 0s;
|
||||
}
|
||||
|
||||
#pinIcon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 55px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: center no-repeat;
|
||||
background-image: url('images/pin-up.svg');
|
||||
background-size: 15px 15px;
|
||||
}
|
||||
|
||||
#pinIcon:hover {
|
||||
background-color: rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
#sidebar.pinned #pinIcon {
|
||||
background-image: url('images/pin-down.svg');
|
||||
background-size: 15px 15px;
|
||||
}
|
||||
|
||||
#sidebarBox {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
width: 300px;
|
||||
@ -116,7 +127,7 @@ span#info {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
top: 10px;
|
||||
top: 20px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
width: 280px;
|
||||
@ -147,7 +158,7 @@ span#info {
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
overflow: auto;
|
||||
top: 10px;
|
||||
top: 20px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
width: 280px;
|
||||
@ -273,6 +284,16 @@ canvas {
|
||||
-webkit-box-shadow: 0px 2px 10px #ff0;
|
||||
}
|
||||
|
||||
.loadingIcon {
|
||||
position: absolute;
|
||||
display: block;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: url('images/loading-icon.gif') center no-repeat;
|
||||
}
|
||||
|
||||
.textLayer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@ -280,6 +301,7 @@ canvas {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
color: #000;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.textLayer > div {
|
||||
@ -407,7 +429,84 @@ canvas {
|
||||
}
|
||||
}
|
||||
|
||||
#loading {
|
||||
margin: 100px 0;
|
||||
text-align: center;
|
||||
#loadingBox {
|
||||
margin: 100px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#loadingBar {
|
||||
background-color: #333;
|
||||
display: inline-block;
|
||||
border: 1px solid black;
|
||||
clear: both;
|
||||
margin:0px;
|
||||
line-height: 0;
|
||||
border-radius: 4px;
|
||||
width: 15em;
|
||||
height: 1.5em;
|
||||
}
|
||||
|
||||
#loadingBar .progress {
|
||||
background-color: green;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
|
||||
background: #b4e391;
|
||||
background: -moz-linear-gradient(top, #b4e391 0%, #61c419 50%, #b4e391 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b4e391), color-stop(50%,#61c419), color-stop(100%,#b4e391));
|
||||
background: -webkit-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);
|
||||
background: -o-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);
|
||||
background: -ms-linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);
|
||||
background: linear-gradient(top, #b4e391 0%,#61c419 50%,#b4e391 100%);
|
||||
|
||||
border-top-left-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#PDFBug {
|
||||
font-size: 10px;
|
||||
position: fixed;
|
||||
top: 35px;
|
||||
bottom: 5px;
|
||||
right: 2px;
|
||||
width: 300px;
|
||||
background: white;
|
||||
border: 1px solid #666;
|
||||
padding: 0;
|
||||
}
|
||||
#PDFBug .controls {
|
||||
border-bottom: 1px solid #666;
|
||||
padding: 3px;
|
||||
background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%);
|
||||
}
|
||||
#PDFBug .panels {
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
top: 27px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#PDFBug button.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
.debuggerShowText {
|
||||
background: yellow;
|
||||
color: blue;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.debuggerHideText:hover {
|
||||
background: yellow;
|
||||
opacity: 0.3;
|
||||
}
|
||||
#PDFBug .stats {
|
||||
font-size: 10px;
|
||||
white-space: pre;
|
||||
font-family: courier;
|
||||
}
|
||||
#PDFBug .stats .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
@ -2,34 +2,40 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Simple pdf.js page viewer</title>
|
||||
<!-- PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION -->
|
||||
|
||||
<link rel="stylesheet" href="viewer.css"/>
|
||||
|
||||
<script type="text/javascript" src="compatibility.js"></script>
|
||||
<script type="text/javascript" src="compatibility.js"></script> <!-- PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION -->
|
||||
|
||||
<!-- PDFJSSCRIPT_INCLUDE_BUILD -->
|
||||
<script type="text/javascript" src="../src/core.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/util.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/canvas.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/obj.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/function.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/charsets.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/cidmaps.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/colorspace.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/crypto.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/evaluator.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/fonts.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/glyphlist.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/image.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/metrics.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/parser.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/pattern.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/stream.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/worker.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../external/jpgjs/jpg.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/jpx.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../src/core.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/util.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/metadata.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/canvas.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/obj.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/function.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/charsets.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/cidmaps.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/colorspace.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/crypto.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/evaluator.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/fonts.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/glyphlist.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/image.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/metrics.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/parser.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/pattern.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/stream.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/worker.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../external/jpgjs/jpg.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/jpx.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="../src/bidi.js"></script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE_CORE -->
|
||||
<script type="text/javascript" src="debugger.js"></script>
|
||||
<script type="text/javascript" src="viewer.js"></script>
|
||||
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@ -91,13 +97,12 @@
|
||||
|
||||
<input id="fileInput" type="file" oncontextmenu="return false;"/>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div id="fileInputSeperator" class="separator"></div>
|
||||
|
||||
<a href="#" id="viewBookmark" title="Bookmark (or copy) current location">
|
||||
<img src="images/bookmark.svg" alt="Bookmark" align="top" height="16"/>
|
||||
</a>
|
||||
|
||||
<span id="info">--</span>
|
||||
</div>
|
||||
<div id="errorWrapper" hidden='true'>
|
||||
<div id="errorMessageLeft">
|
||||
@ -120,6 +125,7 @@
|
||||
|
||||
<div id="sidebar">
|
||||
<div id="sidebarBox">
|
||||
<div id="pinIcon" onClick="PDFView.pinSidebar()"></div>
|
||||
<div id="sidebarScrollView">
|
||||
<div id="sidebarView"></div>
|
||||
</div>
|
||||
@ -144,10 +150,13 @@
|
||||
<img src="images/edit-find.svg" align="top" height="16" alt="Search Document" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="loading">Loading... 0%</div>
|
||||
<div id="loadingBox">
|
||||
<div id="loading">Loading... 0%</div>
|
||||
<div id="loadingBar"><div class="progress"></div></div>
|
||||
</div>
|
||||
<div id="viewer"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
424
web/viewer.js
@ -6,6 +6,7 @@
|
||||
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||
var kDefaultScale = 'auto';
|
||||
var kDefaultScaleDelta = 1.1;
|
||||
var kUnknownScale = 0;
|
||||
var kCacheSize = 20;
|
||||
var kCssUnits = 96.0 / 72.0;
|
||||
var kScrollbarPadding = 40;
|
||||
@ -14,6 +15,15 @@ var kMaxScale = 4.0;
|
||||
var kImageDirectory = './images/';
|
||||
var kSettingsMemory = 20;
|
||||
|
||||
function getFileName(url) {
|
||||
var anchor = url.indexOf('#');
|
||||
var query = url.indexOf('?');
|
||||
var end = Math.min(
|
||||
anchor > 0 ? anchor : url.length,
|
||||
query > 0 ? query : url.length);
|
||||
return url.substring(url.lastIndexOf('/', end) + 1, end);
|
||||
}
|
||||
|
||||
var Cache = function cacheCache(size) {
|
||||
var data = [];
|
||||
this.push = function cachePush(view) {
|
||||
@ -26,6 +36,48 @@ var Cache = function cacheCache(size) {
|
||||
};
|
||||
};
|
||||
|
||||
var ProgressBar = (function ProgressBarClosure() {
|
||||
|
||||
function clamp(v, min, max) {
|
||||
return Math.min(Math.max(v, min), max);
|
||||
}
|
||||
|
||||
function ProgressBar(id, opts) {
|
||||
|
||||
// Fetch the sub-elements for later
|
||||
this.div = document.querySelector(id + ' .progress');
|
||||
|
||||
// Get options, with sensible defaults
|
||||
this.height = opts.height || 100;
|
||||
this.width = opts.width || 100;
|
||||
this.units = opts.units || '%';
|
||||
this.percent = opts.percent || 0;
|
||||
|
||||
// Initialize heights
|
||||
this.div.style.height = this.height + this.units;
|
||||
}
|
||||
|
||||
ProgressBar.prototype = {
|
||||
|
||||
updateBar: function ProgressBar_updateBar() {
|
||||
var progressSize = this.width * this._percent / 100;
|
||||
|
||||
this.div.style.width = progressSize + this.units;
|
||||
},
|
||||
|
||||
get percent() {
|
||||
return this._percent;
|
||||
},
|
||||
|
||||
set percent(val) {
|
||||
this._percent = clamp(val, 0, 100);
|
||||
this.updateBar();
|
||||
}
|
||||
};
|
||||
|
||||
return ProgressBar;
|
||||
})();
|
||||
|
||||
var RenderingQueue = (function RenderingQueueClosure() {
|
||||
function RenderingQueue() {
|
||||
this.items = [];
|
||||
@ -36,10 +88,6 @@ var RenderingQueue = (function RenderingQueueClosure() {
|
||||
if (!item.drawingRequired())
|
||||
return; // as no redraw required, no need for queueing.
|
||||
|
||||
if ('rendering' in item)
|
||||
return; // is already in the queue
|
||||
|
||||
item.rendering = true;
|
||||
this.items.push(item);
|
||||
if (this.items.length > 1)
|
||||
return; // not first item
|
||||
@ -48,7 +96,6 @@ var RenderingQueue = (function RenderingQueueClosure() {
|
||||
},
|
||||
continueExecution: function RenderingQueueContinueExecution() {
|
||||
var item = this.items.shift();
|
||||
delete item.rendering;
|
||||
|
||||
if (this.items.length == 0)
|
||||
return; // queue is empty
|
||||
@ -61,34 +108,54 @@ var RenderingQueue = (function RenderingQueueClosure() {
|
||||
return RenderingQueue;
|
||||
})();
|
||||
|
||||
var FirefoxCom = (function FirefoxComClosure() {
|
||||
return {
|
||||
/**
|
||||
* Creates an event that hopefully the extension is listening for and will
|
||||
* synchronously respond to.
|
||||
* @param {String} action The action to trigger.
|
||||
* @param {String} data Optional data to send.
|
||||
* @return {*} The response.
|
||||
*/
|
||||
request: function(action, data) {
|
||||
var request = document.createTextNode('');
|
||||
request.setUserData('action', action, null);
|
||||
request.setUserData('data', data, null);
|
||||
document.documentElement.appendChild(request);
|
||||
|
||||
var sender = document.createEvent('Events');
|
||||
sender.initEvent('pdf.js.message', true, false);
|
||||
request.dispatchEvent(sender);
|
||||
var response = request.getUserData('response');
|
||||
document.documentElement.removeChild(request);
|
||||
return response;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// Settings Manager - This is a utility for saving settings
|
||||
// First we see if localStorage is available, FF bug #495747
|
||||
// First we see if localStorage is available
|
||||
// If not, we use FUEL in FF
|
||||
var Settings = (function SettingsClosure() {
|
||||
var isLocalStorageEnabled = (function localStorageEnabledTest() {
|
||||
// Feature test as per http://diveintohtml5.info/storage.html
|
||||
// The additional localStorage call is to get around a FF quirk, see
|
||||
// bug #495747 in bugzilla
|
||||
try {
|
||||
localStorage;
|
||||
return 'localStorage' in window && window['localStorage'] !== null &&
|
||||
localStorage;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})();
|
||||
var extPrefix = 'extensions.uriloader@pdf.js';
|
||||
var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled;
|
||||
var inPrivateBrowsing = false;
|
||||
if (isExtension) {
|
||||
var pbs = Components.classes['@mozilla.org/privatebrowsing;1']
|
||||
.getService(Components.interfaces.nsIPrivateBrowsingService);
|
||||
inPrivateBrowsing = pbs.privateBrowsingEnabled;
|
||||
}
|
||||
|
||||
var isFirefoxExtension = PDFJS.isFirefoxExtension;
|
||||
|
||||
function Settings(fingerprint) {
|
||||
var database = null;
|
||||
var index;
|
||||
if (inPrivateBrowsing)
|
||||
return false;
|
||||
else if (isExtension)
|
||||
database = Application.prefs.getValue(extPrefix + '.database', '{}');
|
||||
if (isFirefoxExtension)
|
||||
database = FirefoxCom.request('getDatabase', null) || '{}';
|
||||
else if (isLocalStorageEnabled)
|
||||
database = localStorage.getItem('database') || '{}';
|
||||
else
|
||||
@ -110,31 +177,27 @@ var Settings = (function SettingsClosure() {
|
||||
index = database.files.push({fingerprint: fingerprint}) - 1;
|
||||
this.file = database.files[index];
|
||||
this.database = database;
|
||||
if (isExtension)
|
||||
Application.prefs.setValue(extPrefix + '.database',
|
||||
JSON.stringify(database));
|
||||
else if (isLocalStorageEnabled)
|
||||
localStorage.setItem('database', JSON.stringify(database));
|
||||
}
|
||||
|
||||
Settings.prototype = {
|
||||
set: function settingsSet(name, val) {
|
||||
if (inPrivateBrowsing)
|
||||
if (!('file' in this))
|
||||
return false;
|
||||
|
||||
var file = this.file;
|
||||
file[name] = val;
|
||||
if (isExtension)
|
||||
Application.prefs.setValue(extPrefix + '.database',
|
||||
JSON.stringify(this.database));
|
||||
var database = JSON.stringify(this.database);
|
||||
if (isFirefoxExtension)
|
||||
FirefoxCom.request('setDatabase', database);
|
||||
else if (isLocalStorageEnabled)
|
||||
localStorage.setItem('database', JSON.stringify(this.database));
|
||||
localStorage.setItem('database', database);
|
||||
},
|
||||
|
||||
get: function settingsGet(name, defaultValue) {
|
||||
if (inPrivateBrowsing)
|
||||
if (!('file' in this))
|
||||
return defaultValue;
|
||||
else
|
||||
return this.file[name] || defaultValue;
|
||||
|
||||
return this.file[name] || defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
@ -148,7 +211,7 @@ var currentPageNumber = 1;
|
||||
var PDFView = {
|
||||
pages: [],
|
||||
thumbnails: [],
|
||||
currentScale: 0,
|
||||
currentScale: kUnknownScale,
|
||||
currentScaleValue: null,
|
||||
initialBookmark: document.location.hash.substring(1),
|
||||
|
||||
@ -203,12 +266,12 @@ var PDFView = {
|
||||
|
||||
zoomIn: function pdfViewZoomIn() {
|
||||
var newScale = Math.min(kMaxScale, this.currentScale * kDefaultScaleDelta);
|
||||
this.setScale(newScale, true);
|
||||
this.parseScale(newScale, true);
|
||||
},
|
||||
|
||||
zoomOut: function pdfViewZoomOut() {
|
||||
var newScale = Math.max(kMinScale, this.currentScale / kDefaultScaleDelta);
|
||||
this.setScale(newScale, true);
|
||||
this.parseScale(newScale, true);
|
||||
},
|
||||
|
||||
set page(val) {
|
||||
@ -222,6 +285,7 @@ var PDFView = {
|
||||
return;
|
||||
}
|
||||
|
||||
pages[val - 1].updateStats();
|
||||
currentPageNumber = val;
|
||||
var event = document.createEvent('UIEvents');
|
||||
event.initUIEvent('pagechange', false, false, window, 0);
|
||||
@ -245,7 +309,13 @@ var PDFView = {
|
||||
},
|
||||
|
||||
open: function pdfViewOpen(url, scale) {
|
||||
document.title = this.url = url;
|
||||
this.url = url;
|
||||
|
||||
document.title = decodeURIComponent(getFileName(url)) || url;
|
||||
|
||||
if (!PDFView.loadingBar) {
|
||||
PDFView.loadingBar = new ProgressBar('#loadingBar', {});
|
||||
}
|
||||
|
||||
var self = this;
|
||||
PDFJS.getPdf(
|
||||
@ -257,7 +327,7 @@ var PDFView = {
|
||||
},
|
||||
error: function getPdfError(e) {
|
||||
var loadingIndicator = document.getElementById('loading');
|
||||
loadingIndicator.innerHTML = 'Error';
|
||||
loadingIndicator.textContent = 'Error';
|
||||
var moreInfo = {
|
||||
message: 'Unexpected server response of ' + e.target.status + '.'
|
||||
};
|
||||
@ -272,7 +342,13 @@ var PDFView = {
|
||||
},
|
||||
|
||||
download: function pdfViewDownload() {
|
||||
window.open(this.url + '#pdfjs.action=download', '_parent');
|
||||
var url = this.url.split('#')[0];
|
||||
if (PDFJS.isFirefoxExtension) {
|
||||
FirefoxCom.request('download', url);
|
||||
} else {
|
||||
url += '#pdfjs.action=download', '_parent';
|
||||
window.open(url, '_parent');
|
||||
}
|
||||
},
|
||||
|
||||
navigateTo: function pdfViewNavigateTo(dest) {
|
||||
@ -293,16 +369,17 @@ var PDFView = {
|
||||
|
||||
getDestinationHash: function pdfViewGetDestinationHash(dest) {
|
||||
if (typeof dest === 'string')
|
||||
return '#' + escape(dest);
|
||||
return PDFView.getAnchorUrl('#' + escape(dest));
|
||||
if (dest instanceof Array) {
|
||||
var destRef = dest[0]; // see navigateTo method for dest format
|
||||
var pageNumber = destRef instanceof Object ?
|
||||
this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
|
||||
(destRef + 1);
|
||||
if (pageNumber) {
|
||||
var pdfOpenParams = '#page=' + pageNumber;
|
||||
var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber);
|
||||
var destKind = dest[1];
|
||||
if ('name' in destKind && destKind.name == 'XYZ') {
|
||||
if (typeof destKind === 'object' && 'name' in destKind &&
|
||||
destKind.name == 'XYZ') {
|
||||
var scale = (dest[4] || this.currentScale);
|
||||
pdfOpenParams += '&zoom=' + (scale * 100);
|
||||
if (dest[2] || dest[3]) {
|
||||
@ -315,6 +392,17 @@ var PDFView = {
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* For the firefox extension we prefix the full url on anchor links so they
|
||||
* don't come up as resource:// urls and so open in new tab/window works.
|
||||
* @param {String} anchor The anchor hash include the #.
|
||||
*/
|
||||
getAnchorUrl: function getAnchorUrl(anchor) {
|
||||
if (PDFJS.isFirefoxExtension)
|
||||
return this.url.split('#')[0] + anchor;
|
||||
return anchor;
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the error box.
|
||||
* @param {String} message A message that is human readable.
|
||||
@ -327,7 +415,7 @@ var PDFView = {
|
||||
errorWrapper.removeAttribute('hidden');
|
||||
|
||||
var errorMessage = document.getElementById('errorMessage');
|
||||
errorMessage.innerHTML = message;
|
||||
errorMessage.textContent = message;
|
||||
|
||||
var closeButton = document.getElementById('errorClose');
|
||||
closeButton.onclick = function() {
|
||||
@ -353,8 +441,14 @@ var PDFView = {
|
||||
|
||||
if (moreInfo) {
|
||||
errorMoreInfo.value += 'Message: ' + moreInfo.message;
|
||||
if (moreInfo.stack)
|
||||
if (moreInfo.stack) {
|
||||
errorMoreInfo.value += '\n' + 'Stack: ' + moreInfo.stack;
|
||||
} else {
|
||||
if (moreInfo.filename)
|
||||
errorMoreInfo.value += '\n' + 'File: ' + moreInfo.filename;
|
||||
if (moreInfo.lineNumber)
|
||||
errorMoreInfo.value += '\n' + 'Line: ' + moreInfo.lineNumber;
|
||||
}
|
||||
}
|
||||
errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1;
|
||||
},
|
||||
@ -362,7 +456,9 @@ var PDFView = {
|
||||
progress: function pdfViewProgress(level) {
|
||||
var percent = Math.round(level * 100);
|
||||
var loadingIndicator = document.getElementById('loading');
|
||||
loadingIndicator.innerHTML = 'Loading... ' + percent + '%';
|
||||
loadingIndicator.textContent = 'Loading... ' + percent + '%';
|
||||
|
||||
PDFView.loadingBar.percent = percent;
|
||||
},
|
||||
|
||||
load: function pdfViewLoad(data, scale) {
|
||||
@ -377,8 +473,8 @@ var PDFView = {
|
||||
var errorWrapper = document.getElementById('errorWrapper');
|
||||
errorWrapper.setAttribute('hidden', 'true');
|
||||
|
||||
var loadingIndicator = document.getElementById('loading');
|
||||
loadingIndicator.setAttribute('hidden', 'true');
|
||||
var loadingBox = document.getElementById('loadingBox');
|
||||
loadingBox.setAttribute('hidden', 'true');
|
||||
|
||||
var sidebar = document.getElementById('sidebarView');
|
||||
sidebar.parentNode.scrollTop = 0;
|
||||
@ -402,7 +498,7 @@ var PDFView = {
|
||||
var pagesCount = pdf.numPages;
|
||||
var id = pdf.fingerprint;
|
||||
var storedHash = null;
|
||||
document.getElementById('numPages').innerHTML = pagesCount;
|
||||
document.getElementById('numPages').textContent = pagesCount;
|
||||
document.getElementById('pageNumber').max = pagesCount;
|
||||
PDFView.documentFingerprint = id;
|
||||
var store = PDFView.store = new Settings(id);
|
||||
@ -452,11 +548,35 @@ var PDFView = {
|
||||
}
|
||||
else if (storedHash)
|
||||
this.setHash(storedHash);
|
||||
else {
|
||||
this.parseScale(scale || kDefaultScale, true);
|
||||
else if (scale) {
|
||||
this.parseScale(scale, true);
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
if (PDFView.currentScale === kUnknownScale) {
|
||||
// Scale was not initialized: invalid bookmark or scale was not specified.
|
||||
// Setting the default one.
|
||||
this.parseScale(kDefaultScale, true);
|
||||
}
|
||||
|
||||
this.metadata = null;
|
||||
var metadata = pdf.catalog.metadata;
|
||||
var info = this.documentInfo = pdf.info;
|
||||
var pdfTitle;
|
||||
|
||||
if (metadata) {
|
||||
this.metadata = metadata = new PDFJS.Metadata(metadata);
|
||||
|
||||
if (metadata.has('dc:title'))
|
||||
pdfTitle = metadata.get('dc:title');
|
||||
}
|
||||
|
||||
if (!pdfTitle && info && info['Title'])
|
||||
pdfTitle = info['Title'];
|
||||
|
||||
if (pdfTitle)
|
||||
document.title = pdfTitle + ' - ' + document.title;
|
||||
|
||||
// loosing pdf reference here, starting text indexing in 500ms
|
||||
setTimeout((function loadStartTextExtraction() {
|
||||
this.startTextExtraction(pdf);
|
||||
@ -525,13 +645,7 @@ var PDFView = {
|
||||
return;
|
||||
|
||||
if (hash.indexOf('=') >= 0) {
|
||||
// parsing query string
|
||||
var paramsPairs = hash.split('&');
|
||||
var params = {};
|
||||
for (var i = 0; i < paramsPairs.length; ++i) {
|
||||
var paramPair = paramsPairs[i].split('=');
|
||||
params[paramPair[0]] = paramPair[1];
|
||||
}
|
||||
var params = PDFView.parseQueryString(hash);
|
||||
// borrowing syntax from "Parameters for Opening PDF Files"
|
||||
if ('nameddest' in params) {
|
||||
PDFView.navigateTo(params.nameddest);
|
||||
@ -600,6 +714,10 @@ var PDFView = {
|
||||
}
|
||||
},
|
||||
|
||||
pinSidebar: function pdfViewPinSidebar() {
|
||||
document.getElementById('sidebar').classList.toggle('pinned');
|
||||
},
|
||||
|
||||
getVisiblePages: function pdfViewGetVisiblePages() {
|
||||
var pages = this.pages;
|
||||
var kBottomMargin = 10;
|
||||
@ -652,6 +770,19 @@ var PDFView = {
|
||||
}
|
||||
|
||||
return visibleThumbs;
|
||||
},
|
||||
|
||||
// Helper function to parse query string (e.g. ?param1=value&parm2=...).
|
||||
parseQueryString: function pdfViewParseQueryString(query) {
|
||||
var parts = query.split('&');
|
||||
var params = {};
|
||||
for (var i = 0, ii = parts.length; i < parts.length; ++i) {
|
||||
var param = parts[i].split('=');
|
||||
var key = param[0];
|
||||
var value = param.length > 1 ? param[1] : null;
|
||||
params[unescape(key)] = unescape(value);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
};
|
||||
|
||||
@ -686,6 +817,10 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
div.removeAttribute('data-loaded');
|
||||
|
||||
delete this.canvas;
|
||||
|
||||
this.loadingIconDiv = document.createElement('div');
|
||||
this.loadingIconDiv.className = 'loadingIcon';
|
||||
div.appendChild(this.loadingIconDiv);
|
||||
};
|
||||
|
||||
function setupAnnotations(content, scale) {
|
||||
@ -723,7 +858,15 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
if (!item.content) {
|
||||
content.setAttribute('hidden', true);
|
||||
} else {
|
||||
text.innerHTML = item.content.replace('\n', '<br />');
|
||||
var e = document.createElement('span');
|
||||
var lines = item.content.split('\n');
|
||||
for (var i = 0, ii = lines.length; i < ii; ++i) {
|
||||
var line = lines[i];
|
||||
e.appendChild(document.createTextNode(line));
|
||||
if (i < (ii - 1))
|
||||
e.appendChild(document.createElement('br'));
|
||||
}
|
||||
text.appendChild(e);
|
||||
image.addEventListener('mouseover', function annotationImageOver() {
|
||||
this.nextSibling.removeAttribute('hidden');
|
||||
}, false);
|
||||
@ -817,6 +960,8 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
|
||||
if (scale && scale !== PDFView.currentScale)
|
||||
PDFView.parseScale(scale, true);
|
||||
else if (PDFView.currentScale === kUnknownScale)
|
||||
PDFView.parseScale(kDefaultScale, true);
|
||||
|
||||
setTimeout(function pageViewScrollIntoViewRelayout() {
|
||||
// letting page to re-layout before scrolling
|
||||
@ -840,7 +985,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
};
|
||||
|
||||
this.drawingRequired = function() {
|
||||
return !div.hasChildNodes();
|
||||
return !div.querySelector('canvas');
|
||||
};
|
||||
|
||||
this.draw = function pageviewDraw(callback) {
|
||||
@ -875,35 +1020,42 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
ctx.restore();
|
||||
ctx.translate(-this.x * scale, -this.y * scale);
|
||||
|
||||
stats.begin = Date.now();
|
||||
this.content.startRendering(ctx,
|
||||
(function pageViewDrawCallback(error) {
|
||||
if (error)
|
||||
PDFView.error('An error occurred while rendering the page.', error);
|
||||
this.updateStats();
|
||||
if (this.onAfterDraw)
|
||||
this.onAfterDraw();
|
||||
// Rendering area
|
||||
|
||||
cache.push(this);
|
||||
callback();
|
||||
}).bind(this), textLayer
|
||||
);
|
||||
var self = this;
|
||||
this.content.startRendering(ctx, function pageViewDrawCallback(error) {
|
||||
if (self.loadingIconDiv) {
|
||||
div.removeChild(self.loadingIconDiv);
|
||||
delete self.loadingIconDiv;
|
||||
}
|
||||
|
||||
if (error)
|
||||
PDFView.error('An error occurred while rendering the page.', error);
|
||||
|
||||
self.stats = content.stats;
|
||||
self.updateStats();
|
||||
if (self.onAfterDraw)
|
||||
self.onAfterDraw();
|
||||
|
||||
cache.push(self);
|
||||
callback();
|
||||
}, textLayer);
|
||||
|
||||
setupAnnotations(this.content, this.scale);
|
||||
div.setAttribute('data-loaded', true);
|
||||
};
|
||||
|
||||
this.updateStats = function pageViewUpdateStats() {
|
||||
var t1 = stats.compile, t2 = stats.fonts, t3 = stats.render;
|
||||
var str = 'Time to compile/fonts/render: ' +
|
||||
(t1 - stats.begin) + '/' + (t2 - t1) + '/' + (t3 - t2) + ' ms';
|
||||
document.getElementById('info').innerHTML = str;
|
||||
if (PDFJS.pdfBug && Stats.enabled) {
|
||||
var stats = this.stats;
|
||||
Stats.add(this.id, stats);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = '#' + id;
|
||||
anchor.href = PDFView.getAnchorUrl('#page=' + id);
|
||||
anchor.onclick = function stopNivigation() {
|
||||
PDFView.page = id;
|
||||
return false;
|
||||
@ -976,7 +1128,7 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
||||
};
|
||||
|
||||
this.setImage = function thumbnailViewSetImage(img) {
|
||||
if (this.hasImage)
|
||||
if (this.hasImage || !img)
|
||||
return;
|
||||
|
||||
var ctx = getPageDrawContext();
|
||||
@ -1023,6 +1175,57 @@ var DocumentOutlineView = function documentOutlineView(outline) {
|
||||
}
|
||||
};
|
||||
|
||||
// optimised CSS custom property getter/setter
|
||||
var CustomStyle = (function CustomStyleClosure() {
|
||||
|
||||
// As noted on: http://www.zachstronaut.com/posts/2009/02/17/
|
||||
// animate-css-transforms-firefox-webkit.html
|
||||
// in some versions of IE9 it is critical that ms appear in this list
|
||||
// before Moz
|
||||
var prefixes = ['ms', 'Moz', 'Webkit', 'O'];
|
||||
var _cache = { };
|
||||
|
||||
function CustomStyle() {
|
||||
}
|
||||
|
||||
CustomStyle.getProp = function get(propName, element) {
|
||||
// check cache only when no element is given
|
||||
if (arguments.length == 1 && typeof _cache[propName] == 'string') {
|
||||
return _cache[propName];
|
||||
}
|
||||
|
||||
element = element || document.documentElement;
|
||||
var style = element.style, prefixed, uPropName;
|
||||
|
||||
// test standard property first
|
||||
if (typeof style[propName] == 'string') {
|
||||
return (_cache[propName] = propName);
|
||||
}
|
||||
|
||||
// capitalize
|
||||
uPropName = propName.charAt(0).toUpperCase() + propName.slice(1);
|
||||
|
||||
// test vendor specific properties
|
||||
for (var i = 0, l = prefixes.length; i < l; i++) {
|
||||
prefixed = prefixes[i] + uPropName;
|
||||
if (typeof style[prefixed] == 'string') {
|
||||
return (_cache[propName] = prefixed);
|
||||
}
|
||||
}
|
||||
|
||||
//if all fails then set to undefined
|
||||
return (_cache[propName] = 'undefined');
|
||||
}
|
||||
|
||||
CustomStyle.setProp = function set(propName, element, str) {
|
||||
var prop = this.getProp(propName);
|
||||
if (prop != 'undefined')
|
||||
element.style[prop] = str;
|
||||
}
|
||||
|
||||
return CustomStyle;
|
||||
})();
|
||||
|
||||
var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
|
||||
this.textLayerDiv = textLayerDiv;
|
||||
|
||||
@ -1052,12 +1255,13 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
|
||||
textLayerDiv.appendChild(textDiv);
|
||||
|
||||
if (textDiv.dataset.textLength > 1) { // avoid div by zero
|
||||
// Adjust div width (via letterSpacing) to match canvas text
|
||||
// Adjust div width to match canvas text
|
||||
// Due to the .offsetWidth calls, this is slow
|
||||
// This needs to come after appending to the DOM
|
||||
textDiv.style.letterSpacing =
|
||||
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
|
||||
(textDiv.dataset.textLength - 1)) + 'px';
|
||||
var textScale = textDiv.dataset.canvasWidth / textDiv.offsetWidth;
|
||||
CustomStyle.setProp('transform' , textDiv,
|
||||
'scale(' + textScale + ', 1)');
|
||||
CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%');
|
||||
}
|
||||
} // textLength > 0
|
||||
}
|
||||
@ -1092,46 +1296,57 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
|
||||
// vScale and hScale already contain the scaling to pixel units
|
||||
var fontHeight = fontSize * text.geom.vScale;
|
||||
textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
|
||||
textDiv.dataset.fontName = fontName;
|
||||
|
||||
textDiv.style.fontSize = fontHeight + 'px';
|
||||
textDiv.style.fontFamily = fontName || 'sans-serif';
|
||||
textDiv.style.left = text.geom.x + 'px';
|
||||
textDiv.style.top = (text.geom.y - fontHeight) + 'px';
|
||||
textDiv.textContent = text.str;
|
||||
textDiv.textContent = PDFJS.bidi(text, -1);
|
||||
textDiv.dir = text.direction;
|
||||
textDiv.dataset.textLength = text.length;
|
||||
this.textDivs.push(textDiv);
|
||||
};
|
||||
};
|
||||
|
||||
window.addEventListener('load', function webViewerLoad(evt) {
|
||||
var params = document.location.search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var param = params[i].split('=');
|
||||
params[unescape(param[0])] = unescape(param[1]);
|
||||
var params = PDFView.parseQueryString(document.location.search.substring(1));
|
||||
|
||||
var file = PDFJS.isFirefoxExtension ?
|
||||
window.location.toString() : params.file || kDefaultURL;
|
||||
PDFView.open(file, 0);
|
||||
|
||||
if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader ||
|
||||
!window.FileList || !window.Blob) {
|
||||
document.getElementById('fileInput').setAttribute('hidden', 'true');
|
||||
document.getElementById('fileInputSeperator')
|
||||
.setAttribute('hidden', 'true');
|
||||
} else {
|
||||
document.getElementById('fileInput').value = null;
|
||||
}
|
||||
|
||||
var scale = ('scale' in params) ? params.scale : 0;
|
||||
PDFView.open(params.file || kDefaultURL, parseFloat(scale));
|
||||
// Special debugging flags in the hash section of the URL.
|
||||
var hash = document.location.hash.substring(1);
|
||||
var hashParams = PDFView.parseQueryString(hash);
|
||||
|
||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
||||
document.getElementById('fileInput').setAttribute('hidden', 'true');
|
||||
else
|
||||
document.getElementById('fileInput').value = null;
|
||||
if ('disableWorker' in hashParams)
|
||||
PDFJS.disableWorker = (hashParams['disableWorker'] === 'true');
|
||||
|
||||
if ('disableWorker' in params)
|
||||
PDFJS.disableWorker = (params['disableWorker'] === 'true');
|
||||
if ('disableTextLayer' in hashParams)
|
||||
PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true');
|
||||
|
||||
if ('disableTextLayer' in params)
|
||||
PDFJS.disableTextLayer = (params['disableTextLayer'] === 'true');
|
||||
if ('pdfBug' in hashParams &&
|
||||
(!PDFJS.isFirefoxExtension || FirefoxCom.request('pdfBugEnabled'))) {
|
||||
PDFJS.pdfBug = true;
|
||||
var pdfBug = hashParams['pdfBug'];
|
||||
var enabled = pdfBug.split(',');
|
||||
PDFBug.enable(enabled);
|
||||
PDFBug.init();
|
||||
}
|
||||
|
||||
var sidebarScrollView = document.getElementById('sidebarScrollView');
|
||||
sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true);
|
||||
}, true);
|
||||
|
||||
window.addEventListener('unload', function webViewerUnload(evt) {
|
||||
window.scrollTo(0, 0);
|
||||
}, true);
|
||||
|
||||
/**
|
||||
* Render the next not yet visible page already such that it is
|
||||
* hopefully ready once the user scrolls to it.
|
||||
@ -1201,15 +1416,14 @@ function updateViewarea() {
|
||||
store.set('zoom', normalizedScaleValue);
|
||||
store.set('scrollLeft', Math.round(topLeft.x));
|
||||
store.set('scrollTop', Math.round(topLeft.y));
|
||||
|
||||
document.getElementById('viewBookmark').href = pdfOpenParams;
|
||||
var href = PDFView.getAnchorUrl(pdfOpenParams);
|
||||
document.getElementById('viewBookmark').href = href;
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', function webViewerScroll(evt) {
|
||||
updateViewarea();
|
||||
}, true);
|
||||
|
||||
|
||||
var thumbnailTimer;
|
||||
|
||||
function updateThumbViewArea() {
|
||||
@ -1342,7 +1556,7 @@ window.addEventListener('keydown', function keydown(evt) {
|
||||
handled = true;
|
||||
break;
|
||||
case 48: // '0'
|
||||
PDFView.setScale(kDefaultScale, true);
|
||||
PDFView.parseScale(kDefaultScale, true);
|
||||
handled = true;
|
||||
break;
|
||||
case 37: // left arrow
|
||||
|