Merge upstream.
4
.gitignore
vendored
@ -1,7 +1,5 @@
|
|||||||
*~
|
*~
|
||||||
pdf.pdf
|
|
||||||
intelisa.pdf
|
|
||||||
openweb_tm-PRINT.pdf
|
|
||||||
local.mk
|
local.mk
|
||||||
build/
|
build/
|
||||||
|
tags
|
||||||
|
|
||||||
|
2
LICENSE
@ -10,6 +10,8 @@
|
|||||||
Kalervo Kujala
|
Kalervo Kujala
|
||||||
Adil Allawi <@ironymark>
|
Adil Allawi <@ironymark>
|
||||||
Jakob Miland <saebekassebil@gmail.com>
|
Jakob Miland <saebekassebil@gmail.com>
|
||||||
|
Artur Adib <aadib@mozilla.com>
|
||||||
|
Brendan Dahl <bdahl@mozilla.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
copy of this software and associated documentation files (the "Software"),
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
112
Makefile
@ -3,9 +3,12 @@ BUILD_DIR := build
|
|||||||
BUILD_TARGET := $(BUILD_DIR)/pdf.js
|
BUILD_TARGET := $(BUILD_DIR)/pdf.js
|
||||||
DEFAULT_BROWSERS := resources/browser_manifests/browser_manifest.json
|
DEFAULT_BROWSERS := resources/browser_manifests/browser_manifest.json
|
||||||
DEFAULT_TESTS := test_manifest.json
|
DEFAULT_TESTS := test_manifest.json
|
||||||
|
DEFAULT_PYTHON := python2.7
|
||||||
|
|
||||||
EXTENSION_SRC := ./extensions/
|
EXTENSION_SRC := ./extensions/
|
||||||
|
EXTENSION_BASE_VERSION := 4bb289ec499013de66eb421737a4dbb4a9273eda
|
||||||
FIREFOX_EXTENSION_NAME := pdf.js.xpi
|
FIREFOX_EXTENSION_NAME := pdf.js.xpi
|
||||||
|
FIREFOX_AMO_EXTENSION_NAME := pdf.js.amo.xpi
|
||||||
CHROME_EXTENSION_NAME := pdf.js.crx
|
CHROME_EXTENSION_NAME := pdf.js.crx
|
||||||
|
|
||||||
all: bundle
|
all: bundle
|
||||||
@ -34,6 +37,7 @@ PDF_JS_FILES = \
|
|||||||
stream.js \
|
stream.js \
|
||||||
worker.js \
|
worker.js \
|
||||||
../external/jpgjs/jpg.js \
|
../external/jpgjs/jpg.js \
|
||||||
|
jpx.js \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# make server
|
# make server
|
||||||
@ -41,8 +45,12 @@ PDF_JS_FILES = \
|
|||||||
# This target starts a local web server at localhost:8888. This can be
|
# This target starts a local web server at localhost:8888. This can be
|
||||||
# used for testing all browsers.
|
# used for testing all browsers.
|
||||||
server:
|
server:
|
||||||
@cd test; python test.py --port=8888;
|
@cd test; $(DEFAULT_PYTHON) test.py --port=8888;
|
||||||
|
|
||||||
|
# make test
|
||||||
|
#
|
||||||
|
# This target runs all the tests excluding the unit-test. This can be used for
|
||||||
|
# testing all browsers.
|
||||||
test: shell-test browser-test
|
test: shell-test browser-test
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -65,10 +73,17 @@ bundle: | $(BUILD_DIR)
|
|||||||
cat $(PDF_JS_FILES) > all_files.tmp; \
|
cat $(PDF_JS_FILES) > all_files.tmp; \
|
||||||
sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \
|
sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \
|
||||||
sed -i.bak "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%h" -n 1`/" ../$(BUILD_TARGET); \
|
sed -i.bak "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%h" -n 1`/" ../$(BUILD_TARGET); \
|
||||||
rm -f ../$(BUILD_TARGET).bak
|
rm -f ../$(BUILD_TARGET).bak; \
|
||||||
rm -f *.tmp; \
|
rm -f *.tmp; \
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# make unit-test
|
||||||
|
#
|
||||||
|
# This target runs in-browser unit tests with js-test-driver and jasmine unit
|
||||||
|
# test framework.
|
||||||
|
unit-test:
|
||||||
|
@cd test/unit/ ; make ;
|
||||||
|
|
||||||
# make browser-test
|
# make browser-test
|
||||||
#
|
#
|
||||||
# This target runs in-browser tests using two primary arguments: a
|
# This target runs in-browser tests using two primary arguments: a
|
||||||
@ -93,7 +108,7 @@ browser-test:
|
|||||||
fi;
|
fi;
|
||||||
|
|
||||||
cd test; \
|
cd test; \
|
||||||
python test.py --reftest \
|
$(DEFAULT_PYTHON) test.py --reftest \
|
||||||
--browserManifestFile=$(PDF_BROWSERS) \
|
--browserManifestFile=$(PDF_BROWSERS) \
|
||||||
--manifestFile=$(PDF_TESTS)
|
--manifestFile=$(PDF_TESTS)
|
||||||
|
|
||||||
@ -147,7 +162,10 @@ web: | production extension compiler pages-repo
|
|||||||
@cp $(BUILD_TARGET) $(GH_PAGES)/$(BUILD_TARGET)
|
@cp $(BUILD_TARGET) $(GH_PAGES)/$(BUILD_TARGET)
|
||||||
@cp -R web/* $(GH_PAGES)/web
|
@cp -R web/* $(GH_PAGES)/web
|
||||||
@cp web/images/* $(GH_PAGES)/web/images
|
@cp web/images/* $(GH_PAGES)/web/images
|
||||||
@cp $(EXTENSION_SRC)/firefox/*.xpi $(GH_PAGES)/$(EXTENSION_SRC)/firefox/
|
@cp $(FIREFOX_BUILD_DIR)/$(FIREFOX_EXTENSION_NAME) \
|
||||||
|
$(FIREFOX_BUILD_DIR)/$(FIREFOX_AMO_EXTENSION_NAME) \
|
||||||
|
$(FIREFOX_BUILD_DIR)/update.rdf \
|
||||||
|
$(GH_PAGES)/$(EXTENSION_SRC)/firefox/
|
||||||
@cp $(GH_PAGES)/web/index.html.template $(GH_PAGES)/index.html;
|
@cp $(GH_PAGES)/web/index.html.template $(GH_PAGES)/index.html;
|
||||||
@mv -f $(GH_PAGES)/web/viewer-production.html $(GH_PAGES)/web/viewer.html;
|
@mv -f $(GH_PAGES)/web/viewer-production.html $(GH_PAGES)/web/viewer.html;
|
||||||
@cd $(GH_PAGES); git add -A;
|
@cd $(GH_PAGES); git add -A;
|
||||||
@ -190,35 +208,83 @@ pages-repo: | $(BUILD_DIR)
|
|||||||
# This target produce a restartless firefox extension containing a
|
# This target produce a restartless firefox extension containing a
|
||||||
# copy of the pdf.js source.
|
# copy of the pdf.js source.
|
||||||
CONTENT_DIR := content
|
CONTENT_DIR := content
|
||||||
FIREFOX_CONTENT_DIR := $(EXTENSION_SRC)/firefox/$(CONTENT_DIR)/
|
BUILD_NUMBER := `git log --format=oneline $(EXTENSION_BASE_VERSION).. | wc -l | awk '{print $$1}'`
|
||||||
CHROME_CONTENT_DIR := $(EXTENSION_SRC)/chrome/$(CONTENT_DIR)/
|
EXTENSION_WEB_FILES = \
|
||||||
PDF_WEB_FILES = \
|
|
||||||
web/images \
|
web/images \
|
||||||
web/compatibility.js \
|
|
||||||
web/viewer.css \
|
web/viewer.css \
|
||||||
web/viewer.js \
|
web/viewer.js \
|
||||||
|
web/viewer.html \
|
||||||
web/viewer-production.html \
|
web/viewer-production.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
FIREFOX_BUILD_DIR := $(BUILD_DIR)/firefox
|
||||||
|
FIREFOX_BUILD_CONTENT := $(FIREFOX_BUILD_DIR)/$(CONTENT_DIR)/
|
||||||
|
FIREFOX_CONTENT_DIR := $(EXTENSION_SRC)/firefox/$(CONTENT_DIR)/
|
||||||
|
FIREFOX_EXTENSION_FILES_TO_COPY = \
|
||||||
|
*.js \
|
||||||
|
*.rdf \
|
||||||
|
components \
|
||||||
|
$(NULL)
|
||||||
|
FIREFOX_EXTENSION_FILES = \
|
||||||
|
content \
|
||||||
|
*.js \
|
||||||
|
install.rdf \
|
||||||
|
components \
|
||||||
|
content \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
CHROME_BUILD_DIR := $(BUILD_DIR)/chrome
|
||||||
|
CHROME_CONTENT_DIR := $(EXTENSION_SRC)/chrome/$(CONTENT_DIR)/
|
||||||
|
CHROME_BUILD_CONTENT := $(CHROME_BUILD_DIR)/$(CONTENT_DIR)/
|
||||||
|
CHROME_EXTENSION_FILES = \
|
||||||
|
extensions/chrome/*.json \
|
||||||
|
extensions/chrome/*.html \
|
||||||
|
$(NULL)
|
||||||
extension: | production
|
extension: | production
|
||||||
|
# Clear out everything in the firefox extension build directory
|
||||||
|
@rm -Rf $(FIREFOX_BUILD_DIR)
|
||||||
|
@mkdir -p $(FIREFOX_BUILD_CONTENT)
|
||||||
|
@mkdir -p $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)
|
||||||
|
@mkdir -p $(FIREFOX_BUILD_CONTENT)/web
|
||||||
|
@cd extensions/firefox; cp -r $(FIREFOX_EXTENSION_FILES_TO_COPY) ../../$(FIREFOX_BUILD_DIR)/
|
||||||
# Copy a standalone version of pdf.js inside the content directory
|
# Copy a standalone version of pdf.js inside the content directory
|
||||||
@rm -Rf $(FIREFOX_CONTENT_DIR)
|
@cp $(BUILD_TARGET) $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/
|
||||||
@mkdir -p $(FIREFOX_CONTENT_DIR)/$(BUILD_DIR)
|
@cp -r $(EXTENSION_WEB_FILES) $(FIREFOX_BUILD_CONTENT)/web/
|
||||||
@mkdir -p $(FIREFOX_CONTENT_DIR)/web
|
@rm $(FIREFOX_BUILD_CONTENT)/web/viewer-production.html
|
||||||
@cp $(BUILD_TARGET) $(FIREFOX_CONTENT_DIR)/$(BUILD_DIR)
|
# Copy over the firefox extension snippet so we can inline pdf.js in it
|
||||||
@cp -r $(PDF_WEB_FILES) $(FIREFOX_CONTENT_DIR)/web/
|
@cp web/viewer-snippet-firefox-extension.html $(FIREFOX_BUILD_CONTENT)/web/
|
||||||
@mv -f $(FIREFOX_CONTENT_DIR)/web/viewer-production.html $(FIREFOX_CONTENT_DIR)/web/viewer.html
|
# Modify the viewer so it does all the extension only stuff.
|
||||||
|
@cd $(FIREFOX_BUILD_CONTENT)/web; \
|
||||||
|
sed -i.bak '/PDFJSSCRIPT_INCLUDE_BUNDLE/ r ../build/pdf.js' viewer-snippet-firefox-extension.html; \
|
||||||
|
sed -i.bak '/PDFJSSCRIPT_REMOVE/d' viewer.html; \
|
||||||
|
sed -i.bak '/PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION/d' viewer.html; \
|
||||||
|
sed -i.bak '/PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION/ r viewer-snippet-firefox-extension.html' viewer.html; \
|
||||||
|
rm -f *.bak;
|
||||||
|
# We don't need pdf.js anymore since its inlined
|
||||||
|
@rm -Rf $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/;
|
||||||
|
# Update the build version number
|
||||||
|
@sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/install.rdf
|
||||||
|
@sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/update.rdf
|
||||||
|
@rm -f $(FIREFOX_BUILD_DIR)/*.bak
|
||||||
# Create the xpi
|
# Create the xpi
|
||||||
@cd $(EXTENSION_SRC)/firefox; zip -r $(FIREFOX_EXTENSION_NAME) *
|
@cd $(FIREFOX_BUILD_DIR); zip -r $(FIREFOX_EXTENSION_NAME) $(FIREFOX_EXTENSION_FILES)
|
||||||
@echo "extension created: " $(FIREFOX_EXTENSION_NAME)
|
@echo "extension created: " $(FIREFOX_EXTENSION_NAME)
|
||||||
|
# Build the amo extension too (remove the updateUrl)
|
||||||
|
@sed -i.bak "/updateURL/d" $(FIREFOX_BUILD_DIR)/install.rdf
|
||||||
|
@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)
|
||||||
|
|
||||||
# Copy a standalone version of pdf.js inside the extension directory
|
# Clear out everything in the chrome extension build directory
|
||||||
@rm -Rf $(CHROME_CONTENT_DIR)
|
@rm -Rf $(CHROME_BUILD_DIR)
|
||||||
@mkdir -p $(CHROME_CONTENT_DIR)/$(BUILD_DIR)
|
@mkdir -p $(CHROME_BUILD_CONTENT)
|
||||||
@mkdir -p $(CHROME_CONTENT_DIR)/web
|
@mkdir -p $(CHROME_BUILD_CONTENT)/$(BUILD_DIR)
|
||||||
@cp $(BUILD_TARGET) $(CHROME_CONTENT_DIR)/$(BUILD_DIR)
|
@mkdir -p $(CHROME_BUILD_CONTENT)/web
|
||||||
@cp -r $(PDF_WEB_FILES) $(CHROME_CONTENT_DIR)/web/
|
@cp -R $(CHROME_EXTENSION_FILES) $(CHROME_BUILD_DIR)/
|
||||||
@mv -f $(CHROME_CONTENT_DIR)/web/viewer-production.html $(CHROME_CONTENT_DIR)/web/viewer.html
|
# Copy a standalone version of pdf.js inside the content directory
|
||||||
|
@cp $(BUILD_TARGET) $(CHROME_BUILD_CONTENT)/$(BUILD_DIR)/
|
||||||
|
@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
|
# Create the crx
|
||||||
#TODO
|
#TODO
|
||||||
|
36
README.md
@ -1,9 +1,6 @@
|
|||||||
# pdf.js
|
# PDF.JS
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
pdf.js is an HTML5 technology experiment that explores building a faithful
|
pdf.js is an HTML5 technology experiment that explores building a faithful
|
||||||
and efficient Portable Document Format (PDF) renderer without native code
|
and efficient Portable Document Format (PDF) renderer without native code
|
||||||
assistance.
|
assistance.
|
||||||
@ -14,9 +11,9 @@ rendering PDFs, and eventually release a PDF reader extension powered by
|
|||||||
pdf.js. Integration with Firefox is a possibility if the experiment proves
|
pdf.js. Integration with Firefox is a possibility if the experiment proves
|
||||||
successful.
|
successful.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Getting started
|
||||||
## Getting started
|
|
||||||
|
|
||||||
### Online demo
|
### Online demo
|
||||||
|
|
||||||
@ -29,11 +26,15 @@ using the pdf.js API.
|
|||||||
|
|
||||||
### Extension
|
### Extension
|
||||||
|
|
||||||
An up-to-date 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
|
||||||
|
|
||||||
(The above link is updated upon every merge to our master branch).
|
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`.
|
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`
|
Then open Chrome with the flag `--enable-experimental-extension-apis`, go to `Tools > Extension`
|
||||||
@ -68,12 +69,12 @@ In order to bundle all `src/` files into a final `pdf.js`, issue:
|
|||||||
This will generate the file `build/pdf.js` that can be included in your final project. (WARNING: That's a large file! Consider minifying it).
|
This will generate the file `build/pdf.js` that can be included in your final project. (WARNING: That's a large file! Consider minifying it).
|
||||||
|
|
||||||
|
|
||||||
## Learning
|
# Learning
|
||||||
|
|
||||||
Here are some initial pointers to help contributors get off the ground.
|
Here are some initial pointers to help contributors get off the ground.
|
||||||
Additional resources are available in a separate section below.
|
Additional resources are available in a separate section below.
|
||||||
|
|
||||||
#### Hello world
|
### Hello world
|
||||||
|
|
||||||
For a "hello world" example, take a look at:
|
For a "hello world" example, take a look at:
|
||||||
|
|
||||||
@ -82,7 +83,7 @@ For a "hello world" example, take a look at:
|
|||||||
This example illustrates the bare minimum ingredients for integrating pdf.js
|
This example illustrates the bare minimum ingredients for integrating pdf.js
|
||||||
in a custom project.
|
in a custom project.
|
||||||
|
|
||||||
#### Introductory video
|
### Introductory video
|
||||||
|
|
||||||
Check out the presentation by our contributor Julian Viereck on the inner
|
Check out the presentation by our contributor Julian Viereck on the inner
|
||||||
workings of PDF and pdf.js:
|
workings of PDF and pdf.js:
|
||||||
@ -92,7 +93,7 @@ workings of PDF and pdf.js:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
# Contributing
|
||||||
|
|
||||||
pdf.js is a community-driven project, so contributors are always welcome.
|
pdf.js is a community-driven project, so contributors are always welcome.
|
||||||
Simply fork our repo and contribute away. Good starting places for picking
|
Simply fork our repo and contribute away. Good starting places for picking
|
||||||
@ -122,7 +123,7 @@ You can add your name to it! :)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Running the tests
|
# 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
|
whether it's able to successfully parse PDFs, as well as compare its output
|
||||||
@ -148,7 +149,7 @@ images. The test type `load` simply tests whether the file loads without
|
|||||||
raising any errors.
|
raising any errors.
|
||||||
|
|
||||||
|
|
||||||
## Running tests through our bot
|
### Running tests through our bot
|
||||||
|
|
||||||
If you are a reviewer, you can use our remote bot to issue comprehensive tests
|
If you are a reviewer, you can use our remote bot to issue comprehensive tests
|
||||||
against reference images before merging pull requests.
|
against reference images before merging pull requests.
|
||||||
@ -158,7 +159,7 @@ See the bot repo for details:
|
|||||||
+ https://github.com/mozilla/pdf.js-bot
|
+ https://github.com/mozilla/pdf.js-bot
|
||||||
|
|
||||||
|
|
||||||
## Additional resources
|
# Additional resources
|
||||||
|
|
||||||
Gallery of user projects and modifications:
|
Gallery of user projects and modifications:
|
||||||
|
|
||||||
@ -188,7 +189,7 @@ Follow us on twitter: @pdfjs
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## PDF-related resources
|
### PDF-related resources
|
||||||
|
|
||||||
A really basic overview of PDF is described here:
|
A really basic overview of PDF is described here:
|
||||||
|
|
||||||
@ -205,4 +206,3 @@ a "PDF Reference" from Adobe:
|
|||||||
|
|
||||||
Recommended chapters to read: "2. Overview", "3.4 File Structure",
|
Recommended chapters to read: "2. Overview", "3.4 File Structure",
|
||||||
"4.1 Graphics Objects" that lists the PDF commands.
|
"4.1 Graphics Objects" that lists the PDF commands.
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<script type="text/javascript" src="../../src/stream.js"></script>
|
<script type="text/javascript" src="../../src/stream.js"></script>
|
||||||
<script type="text/javascript" src="../../src/worker.js"></script>
|
<script type="text/javascript" src="../../src/worker.js"></script>
|
||||||
<script type="text/javascript" src="../../external/jpgjs/jpg.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">
|
<script type="text/javascript">
|
||||||
// Specify the main script used to create a new PDF.JS web worker.
|
// Specify the main script used to create a new PDF.JS web worker.
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<script type="text/javascript" src="../../src/stream.js"></script>
|
<script type="text/javascript" src="../../src/stream.js"></script>
|
||||||
<script type="text/javascript" src="../../src/worker.js"></script>
|
<script type="text/javascript" src="../../src/worker.js"></script>
|
||||||
<script type="text/javascript" src="../../external/jpgjs/jpg.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">
|
<script type="text/javascript">
|
||||||
// Specify the main script used to create a new PDF.JS web worker.
|
// Specify the main script used to create a new PDF.JS web worker.
|
||||||
|
86
extensions/firefox/bootstrap.js
vendored
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const RESOURCE_NAME = 'pdf.js';
|
||||||
|
const EXT_PREFIX = 'extensions.uriloader@pdf.js';
|
||||||
|
|
||||||
let Cc = Components.classes;
|
let Cc = Components.classes;
|
||||||
let Ci = Components.interfaces;
|
let Ci = Components.interfaces;
|
||||||
let Cm = Components.manager;
|
let Cm = Components.manager;
|
||||||
@ -14,33 +17,88 @@ function log(str) {
|
|||||||
dump(str + '\n');
|
dump(str + '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
function startup(aData, aReason) {
|
// Register/unregister a class as a component.
|
||||||
let manifestPath = 'chrome.manifest';
|
let Factory = {
|
||||||
let manifest = Cc['@mozilla.org/file/local;1']
|
registrar: null,
|
||||||
.createInstance(Ci.nsILocalFile);
|
aClass: null,
|
||||||
try {
|
register: function(aClass) {
|
||||||
manifest.initWithPath(aData.installPath.path);
|
if (this.aClass) {
|
||||||
manifest.append(manifestPath);
|
log('Cannot register more than one class');
|
||||||
Cm.QueryInterface(Ci.nsIComponentRegistrar).autoRegister(manifest);
|
return;
|
||||||
Services.prefs.setBoolPref('extensions.pdf.js.active', true);
|
}
|
||||||
} catch (e) {
|
this.registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||||
log(e);
|
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 aliasFile = Cc['@mozilla.org/file/local;1']
|
||||||
|
.createInstance(Ci.nsILocalFile);
|
||||||
|
var componentPath = aData.installPath.clone();
|
||||||
|
componentPath.append('content');
|
||||||
|
aliasFile.initWithPath(componentPath.path);
|
||||||
|
var aliasURI = ioService.newFileURI(aliasFile);
|
||||||
|
resProt.setSubstitution(RESOURCE_NAME, aliasURI);
|
||||||
|
|
||||||
|
// Load the component and register it.
|
||||||
|
pdfStreamConverterUrl = aData.resourceURI.spec +
|
||||||
|
'components/PdfStreamConverter.js';
|
||||||
|
Cu.import(pdfStreamConverterUrl);
|
||||||
|
Factory.register(PdfStreamConverter);
|
||||||
|
Services.prefs.setBoolPref('extensions.pdf.js.active', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function shutdown(aData, aReason) {
|
function shutdown(aData, aReason) {
|
||||||
if (Services.prefs.getBoolPref('extensions.pdf.js.active'))
|
if (Services.prefs.getBoolPref('extensions.pdf.js.active'))
|
||||||
Services.prefs.setBoolPref('extensions.pdf.js.active', false);
|
Services.prefs.setBoolPref('extensions.pdf.js.active', false);
|
||||||
|
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
|
||||||
|
if (pdfStreamConverterUrl) {
|
||||||
|
Cu.unload(pdfStreamConverterUrl);
|
||||||
|
pdfStreamConverterUrl = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function install(aData, aReason) {
|
function install(aData, aReason) {
|
||||||
let url = 'chrome://pdf.js/content/web/viewer.html?file=%s';
|
|
||||||
Services.prefs.setCharPref('extensions.pdf.js.url', url);
|
|
||||||
Services.prefs.setBoolPref('extensions.pdf.js.active', false);
|
Services.prefs.setBoolPref('extensions.pdf.js.active', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function uninstall(aData, aReason) {
|
function uninstall(aData, aReason) {
|
||||||
Services.prefs.clearUserPref('extensions.pdf.js.url');
|
|
||||||
Services.prefs.clearUserPref('extensions.pdf.js.active');
|
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}
|
|
||||||
|
|
190
extensions/firefox/components/PdfStreamConverter.js
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
/* -*- 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');
|
||||||
|
|
||||||
|
function log(aMsg) {
|
||||||
|
let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
|
||||||
|
Services.console.logStringMessage(msg);
|
||||||
|
dump(msg + '\n');
|
||||||
|
}
|
||||||
|
function getWindow(top, id) {
|
||||||
|
return top.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils)
|
||||||
|
.getOuterWindowWithId(id);
|
||||||
|
}
|
||||||
|
function windowID(win) {
|
||||||
|
return win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils)
|
||||||
|
.outerWindowID;
|
||||||
|
}
|
||||||
|
function topWindow(win) {
|
||||||
|
return win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIWebNavigation)
|
||||||
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||||
|
.rootTreeItem
|
||||||
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindow);
|
||||||
|
}
|
||||||
|
let application = Cc['@mozilla.org/fuel/application;1']
|
||||||
|
.getService(Ci.fuelIApplication);
|
||||||
|
let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
|
||||||
|
.getService(Ci.nsIPrivateBrowsingService);
|
||||||
|
let inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled;
|
||||||
|
|
||||||
|
// All the priviledged actions.
|
||||||
|
function ChromeActions() {
|
||||||
|
this.inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled;
|
||||||
|
}
|
||||||
|
ChromeActions.prototype = {
|
||||||
|
download: function(data) {
|
||||||
|
Services.wm.getMostRecentWindow('navigator:browser').saveURL(data);
|
||||||
|
},
|
||||||
|
setDatabase: function(data) {
|
||||||
|
if (this.inPrivateBrowswing)
|
||||||
|
return;
|
||||||
|
// 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', '{}');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if (!Services.prefs.getBoolPref('extensions.pdf.js.active'))
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Keep the URL the same so the browser sees it as the same.
|
||||||
|
channel.originalURI = aRequest.URI;
|
||||||
|
channel.asyncOpen(this.listener, aContext);
|
||||||
|
|
||||||
|
// Setup a global listener waiting for the next DOM to be created and verfiy
|
||||||
|
// that its the one we want by its URL. When the correct DOM is found create
|
||||||
|
// an event listener on that window for the pdf.js events that require
|
||||||
|
// chrome priviledges. Code snippet from John Galt.
|
||||||
|
let window = aRequest.loadGroup.groupObserver
|
||||||
|
.QueryInterface(Ci.nsIWebProgress)
|
||||||
|
.DOMWindow;
|
||||||
|
let top = topWindow(window);
|
||||||
|
let id = windowID(window);
|
||||||
|
window = null;
|
||||||
|
|
||||||
|
top.addEventListener('DOMWindowCreated', function onDOMWinCreated(event) {
|
||||||
|
let doc = event.originalTarget;
|
||||||
|
let win = doc.defaultView;
|
||||||
|
|
||||||
|
if (id == windowID(win)) {
|
||||||
|
top.removeEventListener('DOMWindowCreated', onDOMWinCreated, true);
|
||||||
|
if (!doc.documentURIObject.equals(aRequest.URI))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let requestListener = new RequestListener(new ChromeActions);
|
||||||
|
win.addEventListener(PDFJS_EVENT_ID, function(event) {
|
||||||
|
requestListener.receive(event);
|
||||||
|
}, false, true);
|
||||||
|
} else if (!getWindow(top, id)) {
|
||||||
|
top.removeEventListener('DOMWindowCreated', onDOMWinCreated, true);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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]);
|
|
||||||
|
|
@ -6,20 +6,22 @@
|
|||||||
<Description about="urn:mozilla:install-manifest">
|
<Description about="urn:mozilla:install-manifest">
|
||||||
<em:id>uriloader@pdf.js</em:id>
|
<em:id>uriloader@pdf.js</em:id>
|
||||||
<em:name>pdf.js</em:name>
|
<em:name>pdf.js</em:name>
|
||||||
<em:version>0.1.0</em:version>
|
<em:version>0.2.PDFJSSCRIPT_BUILD</em:version>
|
||||||
<em:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL>
|
<em:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL>
|
||||||
<em:targetApplication>
|
<em:targetApplication>
|
||||||
<Description>
|
<Description>
|
||||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||||
<em:minVersion>6.0</em:minVersion>
|
<em:minVersion>6.0</em:minVersion>
|
||||||
<em:maxVersion>11.0a1</em:maxVersion>
|
<em:maxVersion>13.0a1</em:maxVersion>
|
||||||
</Description>
|
</Description>
|
||||||
</em:targetApplication>
|
</em:targetApplication>
|
||||||
<em:bootstrap>true</em:bootstrap>
|
<em:bootstrap>true</em:bootstrap>
|
||||||
<em:unpack>true</em:unpack>
|
<em:unpack>true</em:unpack>
|
||||||
<em:creator>Vivien Nicolas</em:creator>
|
<em:creator>Mozilla Labs</em:creator>
|
||||||
<em:description>pdf.js uri loader</em:description>
|
<em:description>pdf.js uri loader</em:description>
|
||||||
<em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL>
|
<em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL>
|
||||||
<em:type>2</em:type>
|
<em:type>2</em:type>
|
||||||
|
<!-- Use the raw link for updates so we we can use SSL. -->
|
||||||
|
<em:updateURL>https://github.com/mozilla/pdf.js/raw/gh-pages/extensions/firefox/update.rdf</em:updateURL>
|
||||||
</Description>
|
</Description>
|
||||||
</RDF>
|
</RDF>
|
||||||
|
26
extensions/firefox/update.rdf
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||||
|
|
||||||
|
<RDF:Description about="urn:mozilla:extension:uriloader@pdf.js">
|
||||||
|
<em:updates>
|
||||||
|
<RDF:Seq>
|
||||||
|
<RDF:li>
|
||||||
|
<RDF:Description>
|
||||||
|
<em:version>0.2.PDFJSSCRIPT_BUILD</em:version>
|
||||||
|
<em:targetApplication>
|
||||||
|
<RDF:Description>
|
||||||
|
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||||
|
<em:minVersion>6.0</em:minVersion>
|
||||||
|
<em:maxVersion>11.*</em:maxVersion>
|
||||||
|
<!-- Use the raw link for updates so we we can use SSL. -->
|
||||||
|
<em:updateLink>https://github.com/mozilla/pdf.js/raw/gh-pages/extensions/firefox/pdf.js.xpi</em:updateLink>
|
||||||
|
</RDF:Description>
|
||||||
|
</em:targetApplication>
|
||||||
|
</RDF:Description>
|
||||||
|
</RDF:li>
|
||||||
|
</RDF:Seq>
|
||||||
|
</em:updates>
|
||||||
|
</RDF:Description>
|
||||||
|
</RDF:RDF>
|
676
external/jasmine/jasmine-html.js
vendored
@ -1,676 +0,0 @@
|
|||||||
jasmine.HtmlReporterHelpers = {};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
|
||||||
var el = document.createElement(type);
|
|
||||||
|
|
||||||
for (var i = 2; i < arguments.length; i++) {
|
|
||||||
var child = arguments[i];
|
|
||||||
|
|
||||||
if (typeof child === 'string') {
|
|
||||||
el.appendChild(document.createTextNode(child));
|
|
||||||
} else {
|
|
||||||
if (child) {
|
|
||||||
el.appendChild(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var attr in attrs) {
|
|
||||||
if (attr == "className") {
|
|
||||||
el[attr] = attrs[attr];
|
|
||||||
} else {
|
|
||||||
el.setAttribute(attr, attrs[attr]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
|
||||||
var results = child.results();
|
|
||||||
var status = results.passed() ? 'passed' : 'failed';
|
|
||||||
if (results.skipped) {
|
|
||||||
status = 'skipped';
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
|
||||||
var parentDiv = this.dom.summary;
|
|
||||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
|
||||||
var parent = child[parentSuite];
|
|
||||||
|
|
||||||
if (parent) {
|
|
||||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
|
||||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
|
||||||
}
|
|
||||||
parentDiv = this.views.suites[parent.id].element;
|
|
||||||
}
|
|
||||||
|
|
||||||
parentDiv.appendChild(childElement);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
|
||||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
|
||||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter = function(_doc) {
|
|
||||||
var self = this;
|
|
||||||
var doc = _doc || window.document;
|
|
||||||
|
|
||||||
var reporterView;
|
|
||||||
|
|
||||||
var dom = {};
|
|
||||||
|
|
||||||
// Jasmine Reporter Public Interface
|
|
||||||
self.logRunningSpecs = false;
|
|
||||||
|
|
||||||
self.reportRunnerStarting = function(runner) {
|
|
||||||
var specs = runner.specs() || [];
|
|
||||||
|
|
||||||
if (specs.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
createReporterDom(runner.env.versionString());
|
|
||||||
doc.body.appendChild(dom.reporter);
|
|
||||||
|
|
||||||
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
|
||||||
reporterView.addSpecs(specs, self.specFilter);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.reportRunnerResults = function(runner) {
|
|
||||||
reporterView.complete();
|
|
||||||
};
|
|
||||||
|
|
||||||
self.reportSuiteResults = function(suite) {
|
|
||||||
reporterView.suiteComplete(suite);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.reportSpecStarting = function(spec) {
|
|
||||||
if (self.logRunningSpecs) {
|
|
||||||
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.reportSpecResults = function(spec) {
|
|
||||||
reporterView.specComplete(spec);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.log = function() {
|
|
||||||
var console = jasmine.getGlobal().console;
|
|
||||||
if (console && console.log) {
|
|
||||||
if (console.log.apply) {
|
|
||||||
console.log.apply(console, arguments);
|
|
||||||
} else {
|
|
||||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.specFilter = function(spec) {
|
|
||||||
if (!focusedSpecName()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
return self;
|
|
||||||
|
|
||||||
function focusedSpecName() {
|
|
||||||
var specName;
|
|
||||||
|
|
||||||
(function memoizeFocusedSpec() {
|
|
||||||
if (specName) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var paramMap = [];
|
|
||||||
var params = doc.location.search.substring(1).split('&');
|
|
||||||
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
var p = params[i].split('=');
|
|
||||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
specName = paramMap.spec;
|
|
||||||
})();
|
|
||||||
|
|
||||||
return specName;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createReporterDom(version) {
|
|
||||||
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
|
||||||
dom.banner = self.createDom('div', { className: 'banner' },
|
|
||||||
self.createDom('span', { className: 'title' }, "Jasmine "),
|
|
||||||
self.createDom('span', { className: 'version' }, version)),
|
|
||||||
|
|
||||||
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
|
||||||
dom.alert = self.createDom('div', {className: 'alert'}),
|
|
||||||
dom.results = self.createDom('div', {className: 'results'},
|
|
||||||
dom.summary = self.createDom('div', { className: 'summary' }),
|
|
||||||
dom.details = self.createDom('div', { id: 'details' }))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporterHelpers = {};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
|
||||||
var el = document.createElement(type);
|
|
||||||
|
|
||||||
for (var i = 2; i < arguments.length; i++) {
|
|
||||||
var child = arguments[i];
|
|
||||||
|
|
||||||
if (typeof child === 'string') {
|
|
||||||
el.appendChild(document.createTextNode(child));
|
|
||||||
} else {
|
|
||||||
if (child) {
|
|
||||||
el.appendChild(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var attr in attrs) {
|
|
||||||
if (attr == "className") {
|
|
||||||
el[attr] = attrs[attr];
|
|
||||||
} else {
|
|
||||||
el.setAttribute(attr, attrs[attr]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
|
||||||
var results = child.results();
|
|
||||||
var status = results.passed() ? 'passed' : 'failed';
|
|
||||||
if (results.skipped) {
|
|
||||||
status = 'skipped';
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
|
||||||
var parentDiv = this.dom.summary;
|
|
||||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
|
||||||
var parent = child[parentSuite];
|
|
||||||
|
|
||||||
if (parent) {
|
|
||||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
|
||||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
|
||||||
}
|
|
||||||
parentDiv = this.views.suites[parent.id].element;
|
|
||||||
}
|
|
||||||
|
|
||||||
parentDiv.appendChild(childElement);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
|
||||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
|
||||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.ReporterView = function(dom) {
|
|
||||||
this.startedAt = new Date();
|
|
||||||
this.runningSpecCount = 0;
|
|
||||||
this.completeSpecCount = 0;
|
|
||||||
this.passedCount = 0;
|
|
||||||
this.failedCount = 0;
|
|
||||||
this.skippedCount = 0;
|
|
||||||
|
|
||||||
this.createResultsMenu = function() {
|
|
||||||
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
|
||||||
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
|
||||||
' | ',
|
|
||||||
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
|
||||||
|
|
||||||
this.summaryMenuItem.onclick = function() {
|
|
||||||
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
|
||||||
};
|
|
||||||
|
|
||||||
this.detailsMenuItem.onclick = function() {
|
|
||||||
showDetails();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
this.addSpecs = function(specs, specFilter) {
|
|
||||||
this.totalSpecCount = specs.length;
|
|
||||||
|
|
||||||
this.views = {
|
|
||||||
specs: {},
|
|
||||||
suites: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i < specs.length; i++) {
|
|
||||||
var spec = specs[i];
|
|
||||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
|
||||||
if (specFilter(spec)) {
|
|
||||||
this.runningSpecCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.specComplete = function(spec) {
|
|
||||||
this.completeSpecCount++;
|
|
||||||
|
|
||||||
if (isUndefined(this.views.specs[spec.id])) {
|
|
||||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
|
||||||
}
|
|
||||||
|
|
||||||
var specView = this.views.specs[spec.id];
|
|
||||||
|
|
||||||
switch (specView.status()) {
|
|
||||||
case 'passed':
|
|
||||||
this.passedCount++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'failed':
|
|
||||||
this.failedCount++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'skipped':
|
|
||||||
this.skippedCount++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
specView.refresh();
|
|
||||||
this.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.suiteComplete = function(suite) {
|
|
||||||
var suiteView = this.views.suites[suite.id];
|
|
||||||
if (isUndefined(suiteView)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
suiteView.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.refresh = function() {
|
|
||||||
|
|
||||||
if (isUndefined(this.resultsMenu)) {
|
|
||||||
this.createResultsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// currently running UI
|
|
||||||
if (isUndefined(this.runningAlert)) {
|
|
||||||
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
|
|
||||||
dom.alert.appendChild(this.runningAlert);
|
|
||||||
}
|
|
||||||
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
|
||||||
|
|
||||||
// skipped specs UI
|
|
||||||
if (isUndefined(this.skippedAlert)) {
|
|
||||||
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
|
||||||
|
|
||||||
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
|
||||||
dom.alert.appendChild(this.skippedAlert);
|
|
||||||
}
|
|
||||||
|
|
||||||
// passing specs UI
|
|
||||||
if (isUndefined(this.passedAlert)) {
|
|
||||||
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
|
|
||||||
}
|
|
||||||
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
|
||||||
|
|
||||||
// failing specs UI
|
|
||||||
if (isUndefined(this.failedAlert)) {
|
|
||||||
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
|
||||||
}
|
|
||||||
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
|
||||||
|
|
||||||
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
|
||||||
dom.alert.appendChild(this.failedAlert);
|
|
||||||
dom.alert.appendChild(this.resultsMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
// summary info
|
|
||||||
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
|
||||||
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
|
||||||
};
|
|
||||||
|
|
||||||
this.complete = function() {
|
|
||||||
dom.alert.removeChild(this.runningAlert);
|
|
||||||
|
|
||||||
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
|
||||||
|
|
||||||
if (this.failedCount === 0) {
|
|
||||||
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
|
||||||
} else {
|
|
||||||
showDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
|
|
||||||
function showDetails() {
|
|
||||||
if (dom.reporter.className.search(/showDetails/) === -1) {
|
|
||||||
dom.reporter.className += " showDetails";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isUndefined(obj) {
|
|
||||||
return typeof obj === 'undefined';
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDefined(obj) {
|
|
||||||
return !isUndefined(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
function specPluralizedFor(count) {
|
|
||||||
var str = count + " spec";
|
|
||||||
if (count > 1) {
|
|
||||||
str += "s"
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
|
||||||
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
|
||||||
this.spec = spec;
|
|
||||||
this.dom = dom;
|
|
||||||
this.views = views;
|
|
||||||
|
|
||||||
this.symbol = this.createDom('li', { className: 'pending' });
|
|
||||||
this.dom.symbolSummary.appendChild(this.symbol);
|
|
||||||
|
|
||||||
this.summary = this.createDom('div', { className: 'specSummary' },
|
|
||||||
this.createDom('a', {
|
|
||||||
className: 'description',
|
|
||||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
|
||||||
title: this.spec.getFullName()
|
|
||||||
}, this.spec.description)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.detail = this.createDom('div', { className: 'specDetail' },
|
|
||||||
this.createDom('a', {
|
|
||||||
className: 'description',
|
|
||||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
|
||||||
title: this.spec.getFullName()
|
|
||||||
}, this.spec.getFullName())
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
|
||||||
return this.getSpecStatus(this.spec);
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
|
||||||
this.symbol.className = this.status();
|
|
||||||
|
|
||||||
switch (this.status()) {
|
|
||||||
case 'skipped':
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'passed':
|
|
||||||
this.appendSummaryToSuiteDiv();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'failed':
|
|
||||||
this.appendSummaryToSuiteDiv();
|
|
||||||
this.appendFailureDetail();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
|
||||||
this.summary.className += ' ' + this.status();
|
|
||||||
this.appendToSummary(this.spec, this.summary);
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
|
||||||
this.detail.className += ' ' + this.status();
|
|
||||||
|
|
||||||
var resultItems = this.spec.results().getItems();
|
|
||||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
|
||||||
|
|
||||||
for (var i = 0; i < resultItems.length; i++) {
|
|
||||||
var result = resultItems[i];
|
|
||||||
|
|
||||||
if (result.type == 'log') {
|
|
||||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
|
||||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
|
||||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
|
||||||
|
|
||||||
if (result.trace.stack) {
|
|
||||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messagesDiv.childNodes.length > 0) {
|
|
||||||
this.detail.appendChild(messagesDiv);
|
|
||||||
this.dom.details.appendChild(this.detail);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
|
||||||
this.suite = suite;
|
|
||||||
this.dom = dom;
|
|
||||||
this.views = views;
|
|
||||||
|
|
||||||
this.element = this.createDom('div', { className: 'suite' },
|
|
||||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.appendToSummary(this.suite, this.element);
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
|
||||||
return this.getSpecStatus(this.suite);
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
|
||||||
this.element.className += " " + this.status();
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
|
||||||
|
|
||||||
/* @deprecated Use jasmine.HtmlReporter instead
|
|
||||||
*/
|
|
||||||
jasmine.TrivialReporter = function(doc) {
|
|
||||||
this.document = doc || document;
|
|
||||||
this.suiteDivs = {};
|
|
||||||
this.logRunningSpecs = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
|
||||||
var el = document.createElement(type);
|
|
||||||
|
|
||||||
for (var i = 2; i < arguments.length; i++) {
|
|
||||||
var child = arguments[i];
|
|
||||||
|
|
||||||
if (typeof child === 'string') {
|
|
||||||
el.appendChild(document.createTextNode(child));
|
|
||||||
} else {
|
|
||||||
if (child) { el.appendChild(child); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var attr in attrs) {
|
|
||||||
if (attr == "className") {
|
|
||||||
el[attr] = attrs[attr];
|
|
||||||
} else {
|
|
||||||
el.setAttribute(attr, attrs[attr]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return el;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
|
||||||
var showPassed, showSkipped;
|
|
||||||
|
|
||||||
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
|
||||||
this.createDom('div', { className: 'banner' },
|
|
||||||
this.createDom('div', { className: 'logo' },
|
|
||||||
this.createDom('span', { className: 'title' }, "Jasmine"),
|
|
||||||
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
|
||||||
this.createDom('div', { className: 'options' },
|
|
||||||
"Show ",
|
|
||||||
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
|
||||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
|
||||||
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
|
||||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
|
||||||
)
|
|
||||||
),
|
|
||||||
|
|
||||||
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
|
||||||
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
|
||||||
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
|
||||||
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.document.body.appendChild(this.outerDiv);
|
|
||||||
|
|
||||||
var suites = runner.suites();
|
|
||||||
for (var i = 0; i < suites.length; i++) {
|
|
||||||
var suite = suites[i];
|
|
||||||
var suiteDiv = this.createDom('div', { className: 'suite' },
|
|
||||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
|
||||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
|
||||||
this.suiteDivs[suite.id] = suiteDiv;
|
|
||||||
var parentDiv = this.outerDiv;
|
|
||||||
if (suite.parentSuite) {
|
|
||||||
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
|
||||||
}
|
|
||||||
parentDiv.appendChild(suiteDiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.startedAt = new Date();
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
showPassed.onclick = function(evt) {
|
|
||||||
if (showPassed.checked) {
|
|
||||||
self.outerDiv.className += ' show-passed';
|
|
||||||
} else {
|
|
||||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
showSkipped.onclick = function(evt) {
|
|
||||||
if (showSkipped.checked) {
|
|
||||||
self.outerDiv.className += ' show-skipped';
|
|
||||||
} else {
|
|
||||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
|
||||||
var results = runner.results();
|
|
||||||
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
|
||||||
this.runnerDiv.setAttribute("class", className);
|
|
||||||
//do it twice for IE
|
|
||||||
this.runnerDiv.setAttribute("className", className);
|
|
||||||
var specs = runner.specs();
|
|
||||||
var specCount = 0;
|
|
||||||
for (var i = 0; i < specs.length; i++) {
|
|
||||||
if (this.specFilter(specs[i])) {
|
|
||||||
specCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
|
||||||
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
|
||||||
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
|
||||||
|
|
||||||
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
|
||||||
var results = suite.results();
|
|
||||||
var status = results.passed() ? 'passed' : 'failed';
|
|
||||||
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
|
||||||
status = 'skipped';
|
|
||||||
}
|
|
||||||
this.suiteDivs[suite.id].className += " " + status;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
|
||||||
if (this.logRunningSpecs) {
|
|
||||||
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
|
||||||
var results = spec.results();
|
|
||||||
var status = results.passed() ? 'passed' : 'failed';
|
|
||||||
if (results.skipped) {
|
|
||||||
status = 'skipped';
|
|
||||||
}
|
|
||||||
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
|
||||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
|
||||||
this.createDom('a', {
|
|
||||||
className: 'description',
|
|
||||||
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
|
||||||
title: spec.getFullName()
|
|
||||||
}, spec.description));
|
|
||||||
|
|
||||||
|
|
||||||
var resultItems = results.getItems();
|
|
||||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
|
||||||
for (var i = 0; i < resultItems.length; i++) {
|
|
||||||
var result = resultItems[i];
|
|
||||||
|
|
||||||
if (result.type == 'log') {
|
|
||||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
|
||||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
|
||||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
|
||||||
|
|
||||||
if (result.trace.stack) {
|
|
||||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messagesDiv.childNodes.length > 0) {
|
|
||||||
specDiv.appendChild(messagesDiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.log = function() {
|
|
||||||
var console = jasmine.getGlobal().console;
|
|
||||||
if (console && console.log) {
|
|
||||||
if (console.log.apply) {
|
|
||||||
console.log.apply(console, arguments);
|
|
||||||
} else {
|
|
||||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.getLocation = function() {
|
|
||||||
return this.document.location;
|
|
||||||
};
|
|
||||||
|
|
||||||
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
|
||||||
var paramMap = {};
|
|
||||||
var params = this.getLocation().search.substring(1).split('&');
|
|
||||||
for (var i = 0; i < params.length; i++) {
|
|
||||||
var p = params[i].split('=');
|
|
||||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!paramMap.spec) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
|
||||||
};
|
|
81
external/jasmine/jasmine.css
vendored
@ -1,81 +0,0 @@
|
|||||||
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
|
|
||||||
|
|
||||||
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
|
|
||||||
#HTMLReporter a { text-decoration: none; }
|
|
||||||
#HTMLReporter a:hover { text-decoration: underline; }
|
|
||||||
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
|
|
||||||
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
|
|
||||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
|
||||||
#HTMLReporter .version { color: #aaaaaa; }
|
|
||||||
#HTMLReporter .banner { margin-top: 14px; }
|
|
||||||
#HTMLReporter .duration { color: #aaaaaa; float: right; }
|
|
||||||
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
|
|
||||||
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
|
|
||||||
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
|
|
||||||
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
|
|
||||||
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
|
|
||||||
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
|
|
||||||
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
|
|
||||||
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
|
|
||||||
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
|
|
||||||
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
|
|
||||||
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
|
|
||||||
#HTMLReporter .runningAlert { background-color: #666666; }
|
|
||||||
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
|
|
||||||
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
|
|
||||||
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
|
|
||||||
#HTMLReporter .passingAlert { background-color: #a6b779; }
|
|
||||||
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
|
|
||||||
#HTMLReporter .failingAlert { background-color: #cf867e; }
|
|
||||||
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
|
|
||||||
#HTMLReporter .results { margin-top: 14px; }
|
|
||||||
#HTMLReporter #details { display: none; }
|
|
||||||
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
|
|
||||||
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
|
|
||||||
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
|
|
||||||
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
|
|
||||||
#HTMLReporter.showDetails .summary { display: none; }
|
|
||||||
#HTMLReporter.showDetails #details { display: block; }
|
|
||||||
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
|
|
||||||
#HTMLReporter .summary { margin-top: 14px; }
|
|
||||||
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
|
|
||||||
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
|
|
||||||
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
|
|
||||||
#HTMLReporter .description + .suite { margin-top: 0; }
|
|
||||||
#HTMLReporter .suite { margin-top: 14px; }
|
|
||||||
#HTMLReporter .suite a { color: #333333; }
|
|
||||||
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
|
|
||||||
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
|
|
||||||
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
|
|
||||||
#HTMLReporter .resultMessage span.result { display: block; }
|
|
||||||
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
|
|
||||||
|
|
||||||
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
|
|
||||||
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
|
|
||||||
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
|
|
||||||
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
|
|
||||||
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
|
|
||||||
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
|
|
||||||
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
|
|
||||||
#TrivialReporter .runner.running { background-color: yellow; }
|
|
||||||
#TrivialReporter .options { text-align: right; font-size: .8em; }
|
|
||||||
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
|
|
||||||
#TrivialReporter .suite .suite { margin: 5px; }
|
|
||||||
#TrivialReporter .suite.passed { background-color: #dfd; }
|
|
||||||
#TrivialReporter .suite.failed { background-color: #fdd; }
|
|
||||||
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
|
|
||||||
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
|
|
||||||
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
|
|
||||||
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
|
|
||||||
#TrivialReporter .spec.skipped { background-color: #bbb; }
|
|
||||||
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
|
|
||||||
#TrivialReporter .passed { background-color: #cfc; display: none; }
|
|
||||||
#TrivialReporter .failed { background-color: #fbb; }
|
|
||||||
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
|
|
||||||
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
|
|
||||||
#TrivialReporter .resultMessage .mismatch { color: black; }
|
|
||||||
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
|
|
||||||
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
|
|
||||||
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
|
|
||||||
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
|
|
||||||
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
|
|
BIN
external/jasmine/jasmine_favicon.png
vendored
Before Width: | Height: | Size: 905 B |
198
external/jasmineAdapter/JasmineAdapter.js
vendored
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Jasmine JsTestDriver Adapter.
|
||||||
|
* @author misko@hevery.com (Misko Hevery)
|
||||||
|
*/
|
||||||
|
(function(window) {
|
||||||
|
var rootDescribes = new Describes(window);
|
||||||
|
var describePath = [];
|
||||||
|
rootDescribes.collectMode();
|
||||||
|
|
||||||
|
var JASMINE_TYPE = 'jasmine test case';
|
||||||
|
TestCase('Jasmine Adapter Tests', null, JASMINE_TYPE);
|
||||||
|
|
||||||
|
var jasminePlugin = {
|
||||||
|
name:'jasmine',
|
||||||
|
|
||||||
|
getTestRunsConfigurationFor: function(testCaseInfos, expressions, testRunsConfiguration) {
|
||||||
|
for (var i = 0; i < testCaseInfos.length; i++) {
|
||||||
|
if (testCaseInfos[i].getType() == JASMINE_TYPE) {
|
||||||
|
testRunsConfiguration.push(new jstestdriver.TestRunConfiguration(testCaseInfos[i], []));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
runTestConfiguration: function(testRunConfiguration, onTestDone, onTestRunConfigurationComplete){
|
||||||
|
if (testRunConfiguration.getTestCaseInfo().getType() != JASMINE_TYPE) return false;
|
||||||
|
|
||||||
|
var jasmineEnv = jasmine.currentEnv_ = new jasmine.Env();
|
||||||
|
rootDescribes.playback();
|
||||||
|
var specLog = jstestdriver.console.log_ = [];
|
||||||
|
var start;
|
||||||
|
jasmineEnv.specFilter = function(spec) {
|
||||||
|
return rootDescribes.isExclusive(spec);
|
||||||
|
};
|
||||||
|
jasmineEnv.reporter = {
|
||||||
|
log: function(str){
|
||||||
|
specLog.push(str);
|
||||||
|
},
|
||||||
|
|
||||||
|
reportRunnerStarting: function(runner) { },
|
||||||
|
|
||||||
|
reportSpecStarting: function(spec) {
|
||||||
|
specLog = jstestdriver.console.log_ = [];
|
||||||
|
start = new Date().getTime();
|
||||||
|
},
|
||||||
|
|
||||||
|
reportSpecResults: function(spec) {
|
||||||
|
var suite = spec.suite;
|
||||||
|
var results = spec.results();
|
||||||
|
if (results.skipped) return;
|
||||||
|
var end = new Date().getTime();
|
||||||
|
var messages = [];
|
||||||
|
var resultItems = results.getItems();
|
||||||
|
var state = 'passed';
|
||||||
|
for ( var i = 0; i < resultItems.length; i++) {
|
||||||
|
if (!resultItems[i].passed()) {
|
||||||
|
state = resultItems[i].message.match(/AssertionError:/) ? 'error' : 'failed';
|
||||||
|
messages.push({
|
||||||
|
message: resultItems[i].toString(),
|
||||||
|
name: resultItems[i].trace.name,
|
||||||
|
stack: formatStack(resultItems[i].trace.stack)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onTestDone(
|
||||||
|
new jstestdriver.TestResult(
|
||||||
|
suite.getFullName(),
|
||||||
|
spec.description,
|
||||||
|
state,
|
||||||
|
jstestdriver.angular.toJson(messages),
|
||||||
|
specLog.join('\n'),
|
||||||
|
end - start));
|
||||||
|
},
|
||||||
|
|
||||||
|
reportSuiteResults: function(suite) {},
|
||||||
|
|
||||||
|
reportRunnerResults: function(runner) {
|
||||||
|
onTestRunConfigurationComplete();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jasmineEnv.execute();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
onTestsFinish: function(){
|
||||||
|
jasmine.currentEnv_ = null;
|
||||||
|
rootDescribes.collectMode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
jstestdriver.pluginRegistrar.register(jasminePlugin);
|
||||||
|
|
||||||
|
function formatStack(stack) {
|
||||||
|
var lines = (stack||'').split(/\r?\n/);
|
||||||
|
var frames = [];
|
||||||
|
for (i = 0; i < lines.length; i++) {
|
||||||
|
if (!lines[i].match(/\/jasmine[\.-]/)) {
|
||||||
|
frames.push(lines[i].replace(/https?:\/\/\w+(:\d+)?\/test\//, '').replace(/^\s*/, ' '));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return frames.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function noop(){}
|
||||||
|
function Describes(window){
|
||||||
|
var describes = {};
|
||||||
|
var beforeEachs = {};
|
||||||
|
var afterEachs = {};
|
||||||
|
// Here we store:
|
||||||
|
// 0: everyone runs
|
||||||
|
// 1: run everything under ddescribe
|
||||||
|
// 2: run only iits (ignore ddescribe)
|
||||||
|
var exclusive = 0;
|
||||||
|
var collectMode = true;
|
||||||
|
intercept('describe', describes);
|
||||||
|
intercept('xdescribe', describes);
|
||||||
|
intercept('beforeEach', beforeEachs);
|
||||||
|
intercept('afterEach', afterEachs);
|
||||||
|
|
||||||
|
function intercept(functionName, collection){
|
||||||
|
window[functionName] = function(desc, fn){
|
||||||
|
if (collectMode) {
|
||||||
|
collection[desc] = function(){
|
||||||
|
jasmine.getEnv()[functionName](desc, fn);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
jasmine.getEnv()[functionName](desc, fn);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
window.ddescribe = function(name, fn){
|
||||||
|
if (exclusive < 1) {
|
||||||
|
exclusive = 1; // run ddescribe only
|
||||||
|
}
|
||||||
|
window.describe(name, function(){
|
||||||
|
var oldIt = window.it;
|
||||||
|
window.it = function(name, fn){
|
||||||
|
fn.exclusive = 1; // run anything under ddescribe
|
||||||
|
jasmine.getEnv().it(name, fn);
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
fn.call(this);
|
||||||
|
} finally {
|
||||||
|
window.it = oldIt;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
window.iit = function(name, fn){
|
||||||
|
exclusive = fn.exclusive = 2; // run only iits
|
||||||
|
jasmine.getEnv().it(name, fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.collectMode = function() {
|
||||||
|
collectMode = true;
|
||||||
|
exclusive = 0; // run everything
|
||||||
|
};
|
||||||
|
this.playback = function(){
|
||||||
|
collectMode = false;
|
||||||
|
playback(beforeEachs);
|
||||||
|
playback(afterEachs);
|
||||||
|
playback(describes);
|
||||||
|
|
||||||
|
function playback(set) {
|
||||||
|
for ( var name in set) {
|
||||||
|
set[name]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isExclusive = function(spec) {
|
||||||
|
if (exclusive) {
|
||||||
|
var blocks = spec.queue.blocks;
|
||||||
|
for ( var i = 0; i < blocks.length; i++) {
|
||||||
|
if (blocks[i].func.exclusive >= exclusive) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
})(window);
|
||||||
|
|
||||||
|
// Patch Jasmine for proper stack traces
|
||||||
|
jasmine.Spec.prototype.fail = function (e) {
|
||||||
|
var expectationResult = new jasmine.ExpectationResult({
|
||||||
|
passed: false,
|
||||||
|
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||||
|
});
|
||||||
|
// PATCH
|
||||||
|
if (e) {
|
||||||
|
expectationResult.trace = e;
|
||||||
|
}
|
||||||
|
this.results_.addResult(expectationResult);
|
||||||
|
};
|
||||||
|
|
23
external/jasmineAdapter/MIT.LICENSE
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Copyright (c) 2010
|
||||||
|
Misko Hevery <misko@hevery.com>
|
||||||
|
Olmo Maldonado <me@ibolmo.com>
|
||||||
|
Christoph Pojer <christoph.pojer@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
BIN
external/jsTestDriver/JsTestDriver-1.3.3d.jar
vendored
Normal file
202
external/jsTestDriver/LICENSE-2.0.txt
vendored
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
@ -23,6 +23,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() {
|
|||||||
this.alphaIsShape = false;
|
this.alphaIsShape = false;
|
||||||
this.fontSize = 0;
|
this.fontSize = 0;
|
||||||
this.textMatrix = IDENTITY_MATRIX;
|
this.textMatrix = IDENTITY_MATRIX;
|
||||||
|
this.fontMatrix = IDENTITY_MATRIX;
|
||||||
this.leading = 0;
|
this.leading = 0;
|
||||||
// Current point (in user coordinates)
|
// Current point (in user coordinates)
|
||||||
this.x = 0;
|
this.x = 0;
|
||||||
@ -48,6 +49,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() {
|
|||||||
// Note: fill alpha applies to all non-stroking operations
|
// Note: fill alpha applies to all non-stroking operations
|
||||||
this.fillAlpha = 1;
|
this.fillAlpha = 1;
|
||||||
this.strokeAlpha = 1;
|
this.strokeAlpha = 1;
|
||||||
|
this.lineWidth = 1;
|
||||||
|
|
||||||
this.old = old;
|
this.old = old;
|
||||||
}
|
}
|
||||||
@ -329,6 +331,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
|
|
||||||
// Graphics state
|
// Graphics state
|
||||||
setLineWidth: function canvasGraphicsSetLineWidth(width) {
|
setLineWidth: function canvasGraphicsSetLineWidth(width) {
|
||||||
|
this.current.lineWidth = width;
|
||||||
this.ctx.lineWidth = width;
|
this.ctx.lineWidth = width;
|
||||||
},
|
},
|
||||||
setLineCap: function canvasGraphicsSetLineCap(style) {
|
setLineCap: function canvasGraphicsSetLineCap(style) {
|
||||||
@ -442,6 +445,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
consumePath = typeof consumePath !== 'undefined' ? consumePath : true;
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var strokeColor = this.current.strokeColor;
|
var strokeColor = this.current.strokeColor;
|
||||||
|
if (this.current.lineWidth === 0)
|
||||||
|
ctx.lineWidth = this.getSinglePixelWidth();
|
||||||
// For stroke we want to temporarily change the global alpha to the
|
// For stroke we want to temporarily change the global alpha to the
|
||||||
// stroking alpha.
|
// stroking alpha.
|
||||||
ctx.globalAlpha = this.current.strokeAlpha;
|
ctx.globalAlpha = this.current.strokeAlpha;
|
||||||
@ -542,12 +547,32 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
setFont: function canvasGraphicsSetFont(fontRefName, size) {
|
setFont: function canvasGraphicsSetFont(fontRefName, size) {
|
||||||
var fontObj = this.objs.get(fontRefName).fontObj;
|
var fontObj = this.objs.get(fontRefName).fontObj;
|
||||||
|
var current = this.current;
|
||||||
|
|
||||||
if (!fontObj) {
|
if (!fontObj)
|
||||||
throw 'Can\'t find font for ' + fontRefName;
|
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.font = fontObj;
|
||||||
this.current.fontSize = size;
|
this.current.fontSize = size;
|
||||||
@ -557,7 +582,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
(fontObj.bold ? 'bold' : 'normal');
|
(fontObj.bold ? 'bold' : 'normal');
|
||||||
|
|
||||||
var italic = fontObj.italic ? 'italic' : 'normal';
|
var italic = fontObj.italic ? 'italic' : 'normal';
|
||||||
var serif = fontObj.serif ? 'serif' : 'sans-serif';
|
var serif = fontObj.isSerifFont ? 'serif' : 'sans-serif';
|
||||||
var typeface = '"' + name + '", ' + serif;
|
var typeface = '"' + name + '", ' + serif;
|
||||||
var rule = italic + ' ' + bold + ' ' + size + 'px ' + typeface;
|
var rule = italic + ' ' + bold + ' ' + size + 'px ' + typeface;
|
||||||
this.ctx.font = rule;
|
this.ctx.font = rule;
|
||||||
@ -591,7 +616,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var current = this.current;
|
var current = this.current;
|
||||||
var textHScale = current.textHScale;
|
var textHScale = current.textHScale;
|
||||||
var fontMatrix = current.font.fontMatrix || IDENTITY_MATRIX;
|
var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
|
||||||
|
|
||||||
ctx.transform.apply(ctx, current.textMatrix);
|
ctx.transform.apply(ctx, current.textMatrix);
|
||||||
ctx.scale(1, -1);
|
ctx.scale(1, -1);
|
||||||
@ -625,7 +650,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
var charSpacing = current.charSpacing;
|
var charSpacing = current.charSpacing;
|
||||||
var wordSpacing = current.wordSpacing;
|
var wordSpacing = current.wordSpacing;
|
||||||
var textHScale = current.textHScale;
|
var textHScale = current.textHScale;
|
||||||
var fontMatrix = font.fontMatrix || IDENTITY_MATRIX;
|
var fontMatrix = current.fontMatrix || IDENTITY_MATRIX;
|
||||||
var textHScale2 = textHScale * fontMatrix[0];
|
var textHScale2 = textHScale * fontMatrix[0];
|
||||||
var glyphsLength = glyphs.length;
|
var glyphsLength = glyphs.length;
|
||||||
var textLayer = this.textLayer;
|
var textLayer = this.textLayer;
|
||||||
@ -640,7 +665,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
ctx.translate(current.x, current.y);
|
ctx.translate(current.x, current.y);
|
||||||
|
|
||||||
ctx.scale(textHScale, 1);
|
ctx.scale(textHScale, 1);
|
||||||
ctx.lineWidth /= current.textMatrix[0];
|
|
||||||
|
|
||||||
if (textSelection) {
|
if (textSelection) {
|
||||||
this.save();
|
this.save();
|
||||||
@ -664,7 +688,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
this.restore();
|
this.restore();
|
||||||
|
|
||||||
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
|
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);
|
ctx.translate(width, 0);
|
||||||
current.x += width * textHScale;
|
current.x += width * textHScale;
|
||||||
@ -677,49 +702,58 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
} else {
|
} else {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
this.applyTextTransforms();
|
this.applyTextTransforms();
|
||||||
ctx.lineWidth /= current.textMatrix[0] * fontMatrix[0];
|
|
||||||
|
var lineWidth = current.lineWidth;
|
||||||
|
var scale = Math.abs(current.textMatrix[0] * fontMatrix[0]);
|
||||||
|
if (scale == 0 || lineWidth == 0)
|
||||||
|
lineWidth = this.getSinglePixelWidth();
|
||||||
|
else
|
||||||
|
lineWidth /= scale;
|
||||||
|
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
|
||||||
if (textSelection)
|
if (textSelection)
|
||||||
text.geom = this.getTextGeometry();
|
text.geom = this.getTextGeometry();
|
||||||
|
|
||||||
var width = 0;
|
var x = 0;
|
||||||
for (var i = 0; i < glyphsLength; ++i) {
|
for (var i = 0; i < glyphsLength; ++i) {
|
||||||
var glyph = glyphs[i];
|
var glyph = glyphs[i];
|
||||||
if (glyph === null) {
|
if (glyph === null) {
|
||||||
// word break
|
// word break
|
||||||
width += wordSpacing;
|
x += Util.sign(current.fontMatrix[0]) * wordSpacing;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var char = glyph.fontChar;
|
var char = glyph.fontChar;
|
||||||
var charWidth = glyph.width * fontSize * 0.001 + charSpacing;
|
var charWidth = glyph.width * fontSize * 0.001 +
|
||||||
|
Util.sign(current.fontMatrix[0]) * charSpacing;
|
||||||
|
|
||||||
switch (textRenderingMode) {
|
switch (textRenderingMode) {
|
||||||
default: // other unsupported rendering modes
|
default: // other unsupported rendering modes
|
||||||
case TextRenderingMode.FILL:
|
case TextRenderingMode.FILL:
|
||||||
case TextRenderingMode.FILL_ADD_TO_PATH:
|
case TextRenderingMode.FILL_ADD_TO_PATH:
|
||||||
ctx.fillText(char, width, 0);
|
ctx.fillText(char, x, 0);
|
||||||
break;
|
break;
|
||||||
case TextRenderingMode.STROKE:
|
case TextRenderingMode.STROKE:
|
||||||
case TextRenderingMode.STROKE_ADD_TO_PATH:
|
case TextRenderingMode.STROKE_ADD_TO_PATH:
|
||||||
ctx.strokeText(char, width, 0);
|
ctx.strokeText(char, x, 0);
|
||||||
break;
|
break;
|
||||||
case TextRenderingMode.FILL_STROKE:
|
case TextRenderingMode.FILL_STROKE:
|
||||||
case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
|
case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
|
||||||
ctx.fillText(char, width, 0);
|
ctx.fillText(char, x, 0);
|
||||||
ctx.strokeText(char, width, 0);
|
ctx.strokeText(char, x, 0);
|
||||||
break;
|
break;
|
||||||
case TextRenderingMode.INVISIBLE:
|
case TextRenderingMode.INVISIBLE:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
width += charWidth;
|
x += charWidth;
|
||||||
|
|
||||||
text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
|
text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
|
||||||
text.length++;
|
text.length++;
|
||||||
text.canvasWidth += charWidth;
|
text.canvasWidth += charWidth;
|
||||||
}
|
}
|
||||||
current.x += width * textHScale2;
|
current.x += x * textHScale2;
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,7 +769,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
var fontSize = current.fontSize;
|
var fontSize = current.fontSize;
|
||||||
var textHScale = current.textHScale;
|
var textHScale = current.textHScale;
|
||||||
if (!font.coded)
|
if (!font.coded)
|
||||||
textHScale *= (font.fontMatrix || IDENTITY_MATRIX)[0];
|
textHScale *= (current.fontMatrix || IDENTITY_MATRIX)[0];
|
||||||
var arrLength = arr.length;
|
var arrLength = arr.length;
|
||||||
var textLayer = this.textLayer;
|
var textLayer = this.textLayer;
|
||||||
var text = {str: '', length: 0, canvasWidth: 0, geom: {}};
|
var text = {str: '', length: 0, canvasWidth: 0, geom: {}};
|
||||||
@ -832,8 +866,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
setStrokeColor: function canvasGraphicsSetStrokeColor(/*...*/) {
|
setStrokeColor: function canvasGraphicsSetStrokeColor(/*...*/) {
|
||||||
var cs = this.current.strokeColorSpace;
|
var cs = this.current.strokeColorSpace;
|
||||||
var color = cs.getRgb(arguments);
|
var rgbColor = cs.getRgb(arguments);
|
||||||
var color = Util.makeCssRgb.apply(null, cs.getRgb(arguments));
|
var color = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
|
||||||
this.ctx.strokeStyle = color;
|
this.ctx.strokeStyle = color;
|
||||||
this.current.strokeColor = color;
|
this.current.strokeColor = color;
|
||||||
},
|
},
|
||||||
@ -855,7 +889,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
} else if (IR[0] == 'RadialAxial' || IR[0] == 'Dummy') {
|
} else if (IR[0] == 'RadialAxial' || IR[0] == 'Dummy') {
|
||||||
var pattern = Pattern.shadingFromIR(this.ctx, IR);
|
var pattern = Pattern.shadingFromIR(this.ctx, IR);
|
||||||
} else {
|
} else {
|
||||||
throw 'Unkown IR type';
|
error('Unkown IR type ' + IR[0]);
|
||||||
}
|
}
|
||||||
return pattern;
|
return pattern;
|
||||||
},
|
},
|
||||||
@ -870,7 +904,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
setFillColor: function canvasGraphicsSetFillColor(/*...*/) {
|
setFillColor: function canvasGraphicsSetFillColor(/*...*/) {
|
||||||
var cs = this.current.fillColorSpace;
|
var cs = this.current.fillColorSpace;
|
||||||
var color = Util.makeCssRgb.apply(null, cs.getRgb(arguments));
|
var rgbColor = cs.getRgb(arguments);
|
||||||
|
var color = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
|
||||||
this.ctx.fillStyle = color;
|
this.ctx.fillStyle = color;
|
||||||
this.current.fillColor = color;
|
this.current.fillColor = color;
|
||||||
},
|
},
|
||||||
@ -1141,6 +1176,10 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
},
|
},
|
||||||
restoreFillRule: function canvasGraphicsRestoreFillRule(rule) {
|
restoreFillRule: function canvasGraphicsRestoreFillRule(rule) {
|
||||||
this.ctx.mozFillRule = rule;
|
this.ctx.mozFillRule = rule;
|
||||||
|
},
|
||||||
|
getSinglePixelWidth: function getSinglePixelWidth(scale) {
|
||||||
|
var inverse = this.ctx.mozCurrentTransformInverse;
|
||||||
|
return Math.abs(inverse[0] + inverse[2]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,6 +57,11 @@ var ColorSpace = (function ColorSpaceClosure() {
|
|||||||
|
|
||||||
return new AlternateCS(numComps, ColorSpace.fromIR(alt),
|
return new AlternateCS(numComps, ColorSpace.fromIR(alt),
|
||||||
PDFFunction.fromIR(tintFnIR));
|
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:
|
default:
|
||||||
error('Unkown name ' + name);
|
error('Unkown name ' + name);
|
||||||
}
|
}
|
||||||
@ -146,6 +151,8 @@ var ColorSpace = (function ColorSpaceClosure() {
|
|||||||
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
|
var tintFnIR = PDFFunction.getIR(xref, xref.fetchIfRef(cs[3]));
|
||||||
return ['AlternateCS', numComps, alt, tintFnIR];
|
return ['AlternateCS', numComps, alt, tintFnIR];
|
||||||
case 'Lab':
|
case 'Lab':
|
||||||
|
var params = cs[1].map;
|
||||||
|
return ['LabCS', params];
|
||||||
default:
|
default:
|
||||||
error('unimplemented color space object "' + mode + '"');
|
error('unimplemented color space object "' + mode + '"');
|
||||||
}
|
}
|
||||||
@ -369,55 +376,16 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() {
|
|||||||
DeviceCmykCS.prototype = {
|
DeviceCmykCS.prototype = {
|
||||||
getRgb: function cmykcs_getRgb(color) {
|
getRgb: function cmykcs_getRgb(color) {
|
||||||
var c = color[0], m = color[1], y = color[2], k = color[3];
|
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;
|
// CMYK -> CMY: http://www.easyrgb.com/index.php?X=MATH&H=14#text14
|
||||||
// this is a matrix multiplication, unrolled for performance
|
c = (c * (1 - k) + k);
|
||||||
// code is taken from the poppler implementation
|
m = (m * (1 - k) + k);
|
||||||
x = c1 * m1 * y1 * k1; // 0 0 0 0
|
y = (y * (1 - k) + k);
|
||||||
r = g = b = x;
|
|
||||||
x = c1 * m1 * y1 * k; // 0 0 0 1
|
// CMY -> RGB: http://www.easyrgb.com/index.php?X=MATH&H=12#text12
|
||||||
r += 0.1373 * x;
|
var r = (1 - c);
|
||||||
g += 0.1216 * x;
|
var g = (1 - m);
|
||||||
b += 0.1255 * x;
|
var b = (1 - y);
|
||||||
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;
|
|
||||||
|
|
||||||
return [r, g, b];
|
return [r, g, b];
|
||||||
},
|
},
|
||||||
@ -448,3 +416,116 @@ var DeviceCmykCS = (function DeviceCmykCSClosure() {
|
|||||||
return DeviceCmykCS;
|
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;
|
||||||
|
})();
|
||||||
|
40
src/core.js
@ -33,7 +33,9 @@ function getPdf(arg, callback) {
|
|||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', params.url);
|
xhr.open('GET', params.url);
|
||||||
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
|
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)
|
if ('progress' in params)
|
||||||
xhr.onprogress = params.progress || undefined;
|
xhr.onprogress = params.progress || undefined;
|
||||||
@ -193,6 +195,9 @@ var Page = (function PageClosure() {
|
|||||||
for (i = 0; i < n; ++i)
|
for (i = 0; i < n; ++i)
|
||||||
content[i] = xref.fetchIfRef(content[i]);
|
content[i] = xref.fetchIfRef(content[i]);
|
||||||
content = new StreamsSequenceStream(content);
|
content = new StreamsSequenceStream(content);
|
||||||
|
} else if (!content) {
|
||||||
|
// replacing non-existent page content with empty one
|
||||||
|
content = new Stream(new Uint8Array(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
var pe = this.pe = new PartialEvaluator(
|
var pe = this.pe = new PartialEvaluator(
|
||||||
@ -409,14 +414,14 @@ var Page = (function PageClosure() {
|
|||||||
if (callback)
|
if (callback)
|
||||||
callback(e);
|
callback(e);
|
||||||
else
|
else
|
||||||
throw e;
|
error(e);
|
||||||
}
|
}
|
||||||
}.bind(this),
|
}.bind(this),
|
||||||
function pageDisplayReadPromiseError(reason) {
|
function pageDisplayReadPromiseError(reason) {
|
||||||
if (callback)
|
if (callback)
|
||||||
callback(reason);
|
callback(reason);
|
||||||
else
|
else
|
||||||
throw reason;
|
error(reason);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -619,17 +624,26 @@ var PDFDoc = (function PDFDocClosure() {
|
|||||||
if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
|
if (!globalScope.PDFJS.disableWorker && typeof Worker !== 'undefined') {
|
||||||
var workerSrc = PDFJS.workerSrc;
|
var workerSrc = PDFJS.workerSrc;
|
||||||
if (typeof workerSrc === 'undefined') {
|
if (typeof workerSrc === 'undefined') {
|
||||||
throw 'No PDFJS.workerSrc specified';
|
error('No PDFJS.workerSrc specified');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Some versions of FF can't create a worker on localhost, see:
|
var worker;
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
if (PDFJS.isFirefoxExtension) {
|
||||||
var worker = new Worker(workerSrc);
|
// 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);
|
var messageHandler = new MessageHandler('main', worker);
|
||||||
// Tell the worker the file it was created from.
|
|
||||||
messageHandler.send('workerSrc', workerSrc);
|
|
||||||
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
||||||
if (supportTypedArray) {
|
if (supportTypedArray) {
|
||||||
this.worker = worker;
|
this.worker = worker;
|
||||||
@ -645,7 +659,9 @@ var PDFDoc = (function PDFDocClosure() {
|
|||||||
// serializing the typed array.
|
// serializing the typed array.
|
||||||
messageHandler.send('test', testObj);
|
messageHandler.send('test', testObj);
|
||||||
return;
|
return;
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
warn('The worker has been disabled.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Either workers are disabled, not supported or have thrown an exception.
|
// Either workers are disabled, not supported or have thrown an exception.
|
||||||
// Thus, we fallback to a faked worker.
|
// Thus, we fallback to a faked worker.
|
||||||
@ -716,7 +732,7 @@ var PDFDoc = (function PDFDocClosure() {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw 'Got unkown object type ' + type;
|
error('Got unkown object type ' + type);
|
||||||
}
|
}
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
@ -737,7 +753,7 @@ var PDFDoc = (function PDFDocClosure() {
|
|||||||
if (page.displayReadyPromise)
|
if (page.displayReadyPromise)
|
||||||
page.displayReadyPromise.reject(data.error);
|
page.displayReadyPromise.reject(data.error);
|
||||||
else
|
else
|
||||||
throw data.error;
|
error(data.error);
|
||||||
}, this);
|
}, this);
|
||||||
|
|
||||||
messageHandler.on('jpeg_decode', function(data, promise) {
|
messageHandler.on('jpeg_decode', function(data, promise) {
|
||||||
|
@ -159,6 +159,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
// a Stream in the main thread.
|
// a Stream in the main thread.
|
||||||
if (translated.file)
|
if (translated.file)
|
||||||
translated.file = translated.file.getBytes();
|
translated.file = translated.file.getBytes();
|
||||||
|
if (translated.properties.file) {
|
||||||
|
translated.properties.file =
|
||||||
|
translated.properties.file.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
handler.send('obj', [
|
handler.send('obj', [
|
||||||
loadedName,
|
loadedName,
|
||||||
@ -292,8 +296,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
// Create an IR of the pattern code.
|
// Create an IR of the pattern code.
|
||||||
var depIdx = dependencyArray.length;
|
var depIdx = dependencyArray.length;
|
||||||
var queueObj = {};
|
var queueObj = {};
|
||||||
var codeIR = this.getIRQueue(pattern, dict.get('Resources'),
|
var codeIR = this.getIRQueue(pattern, dict.get('Resources') ||
|
||||||
queueObj, dependencyArray);
|
resources, queueObj, dependencyArray);
|
||||||
|
|
||||||
// Add the dependencies that are required to execute the
|
// Add the dependencies that are required to execute the
|
||||||
// codeIR.
|
// codeIR.
|
||||||
@ -336,8 +340,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
// This adds the IRQueue of the xObj to the current queue.
|
// This adds the IRQueue of the xObj to the current queue.
|
||||||
var depIdx = dependencyArray.length;
|
var depIdx = dependencyArray.length;
|
||||||
|
|
||||||
this.getIRQueue(xobj, xobj.dict.get('Resources'), queue,
|
this.getIRQueue(xobj, xobj.dict.get('Resources') || resources,
|
||||||
dependencyArray);
|
queue, dependencyArray);
|
||||||
|
|
||||||
// Add the dependencies that are required to execute the
|
// Add the dependencies that are required to execute the
|
||||||
// codeIR.
|
// codeIR.
|
||||||
@ -481,8 +485,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
properties.cidToGidMap = this.readCidToGidMap(cidToGidMap);
|
properties.cidToGidMap = this.readCidToGidMap(cidToGidMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var flags = properties.flags;
|
||||||
var differences = [];
|
var differences = [];
|
||||||
var baseEncoding = Encodings.StandardEncoding;
|
var baseEncoding = !!(flags & FontFlags.Symbolic) ?
|
||||||
|
Encodings.symbolsEncoding : Encodings.StandardEncoding;
|
||||||
var hasEncoding = dict.has('Encoding');
|
var hasEncoding = dict.has('Encoding');
|
||||||
if (hasEncoding) {
|
if (hasEncoding) {
|
||||||
var encoding = xref.fetchIfRef(dict.get('Encoding'));
|
var encoding = xref.fetchIfRef(dict.get('Encoding'));
|
||||||
@ -758,10 +764,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
baseFontName = baseFontName.name.replace(/[,_]/g, '-');
|
baseFontName = baseFontName.name.replace(/[,_]/g, '-');
|
||||||
var metrics = this.getBaseFontMetrics(baseFontName);
|
var metrics = this.getBaseFontMetrics(baseFontName);
|
||||||
|
|
||||||
|
// Simulating descriptor flags attribute
|
||||||
|
var fontNameWoStyle = baseFontName.split('-')[0];
|
||||||
|
var flags = (serifFonts[fontNameWoStyle] ||
|
||||||
|
(fontNameWoStyle.search(/serif/gi) != -1) ? FontFlags.Serif : 0) |
|
||||||
|
(symbolsFonts[fontNameWoStyle] ? FontFlags.Symbolic :
|
||||||
|
FontFlags.Nonsymbolic);
|
||||||
|
|
||||||
var properties = {
|
var properties = {
|
||||||
type: type.name,
|
type: type.name,
|
||||||
widths: metrics.widths,
|
widths: metrics.widths,
|
||||||
defaultWidth: metrics.defaultWidth,
|
defaultWidth: metrics.defaultWidth,
|
||||||
|
flags: flags,
|
||||||
firstChar: 0,
|
firstChar: 0,
|
||||||
lastChar: maxCharIndex
|
lastChar: maxCharIndex
|
||||||
};
|
};
|
||||||
@ -773,11 +787,10 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
|
|||||||
properties: properties
|
properties: properties
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// According to the spec if 'FontDescriptor' is declared, 'FirstChar',
|
// 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.
|
// 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
|
// TODO Fill the width array depending on which of the base font this is
|
||||||
// a variant.
|
// a variant.
|
||||||
|
155
src/fonts.js
@ -19,6 +19,18 @@ var kPDFGlyphSpaceUnits = 1000;
|
|||||||
// Until hinting is fully supported this constant can be used
|
// Until hinting is fully supported this constant can be used
|
||||||
var kHintingEnabled = false;
|
var kHintingEnabled = false;
|
||||||
|
|
||||||
|
var FontFlags = {
|
||||||
|
FixedPitch: 1,
|
||||||
|
Serif: 2,
|
||||||
|
Symbolic: 4,
|
||||||
|
Script: 8,
|
||||||
|
Nonsymbolic: 32,
|
||||||
|
Italic: 64,
|
||||||
|
AllCap: 65536,
|
||||||
|
SmallCap: 131072,
|
||||||
|
ForceBold: 262144
|
||||||
|
};
|
||||||
|
|
||||||
var Encodings = {
|
var Encodings = {
|
||||||
get ExpertEncoding() {
|
get ExpertEncoding() {
|
||||||
return shadow(this, 'ExpertEncoding', ['', '', '', '', '', '', '', '', '',
|
return shadow(this, 'ExpertEncoding', ['', '', '', '', '', '', '', '', '',
|
||||||
@ -160,19 +172,20 @@ var Encodings = {
|
|||||||
'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
|
'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
|
||||||
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
||||||
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||||
'braceleft', 'bar', 'braceright', 'asciitilde', '', '', 'exclamdown',
|
'braceleft', 'bar', 'braceright', 'asciitilde', '', '', '', '', '', '',
|
||||||
'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency',
|
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||||
'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft',
|
'', '', '', '', '', '', '', '', '', '', 'exclamdown', 'cent', 'sterling',
|
||||||
'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger', 'daggerdbl',
|
'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle',
|
||||||
'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase',
|
'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi',
|
||||||
'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis',
|
'fl', '', 'endash', 'dagger', 'daggerdbl', 'periodcentered', '',
|
||||||
'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex',
|
'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright',
|
||||||
'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring',
|
'guillemotright', 'ellipsis', 'perthousand', '', 'questiondown', '',
|
||||||
'cedilla', '', 'hungarumlaut', 'ogonek', 'caron', 'emdash', '', '', '',
|
'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent',
|
||||||
'', '', '', '', '', '', '', '', '', '', '', '', '', 'AE', '',
|
'dieresis', '', 'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron',
|
||||||
'ordfeminine', '', '', '', '', 'Lslash', 'Oslash', 'OE', 'ordmasculine',
|
'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
|
||||||
'', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '', 'lslash',
|
'AE', '', 'ordfeminine', '', '', '', '', 'Lslash', 'Oslash', 'OE',
|
||||||
'oslash', 'oe', 'germandbls'
|
'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '',
|
||||||
|
'lslash', 'oslash', 'oe', 'germandbls'
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
get WinAnsiEncoding() {
|
get WinAnsiEncoding() {
|
||||||
@ -339,6 +352,21 @@ var stdFontMap = {
|
|||||||
'TimesNewRomanPSMT-Italic': 'Times-Italic'
|
'TimesNewRomanPSMT-Italic': 'Times-Italic'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the map of the non-standard fonts that might be included as a standard
|
||||||
|
* fonts without glyph data.
|
||||||
|
*/
|
||||||
|
var nonStdFontMap = {
|
||||||
|
'ComicSansMS': 'Comic Sans MS',
|
||||||
|
'ComicSansMS-Bold': 'Comic Sans MS-Bold',
|
||||||
|
'ComicSansMS-BoldItalic': 'Comic Sans MS-BoldItalic',
|
||||||
|
'ComicSansMS-Italic': 'Comic Sans MS-Italic',
|
||||||
|
'LucidaConsole': 'Courier',
|
||||||
|
'LucidaConsole-Bold': 'Courier-Bold',
|
||||||
|
'LucidaConsole-BoldItalic': 'Courier-BoldOblique',
|
||||||
|
'LucidaConsole-Italic': 'Courier-Oblique'
|
||||||
|
};
|
||||||
|
|
||||||
var serifFonts = {
|
var serifFonts = {
|
||||||
'Adobe Jenson': true, 'Adobe Text': true, 'Albertus': true,
|
'Adobe Jenson': true, 'Adobe Text': true, 'Albertus': true,
|
||||||
'Aldus': true, 'Alexandria': true, 'Algerian': true,
|
'Aldus': true, 'Alexandria': true, 'Algerian': true,
|
||||||
@ -386,6 +414,23 @@ var serifFonts = {
|
|||||||
'Wide Latin': true, 'Windsor': true, 'XITS': true
|
'Wide Latin': true, 'Windsor': true, 'XITS': true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var symbolsFonts = {
|
||||||
|
'Dingbats': true, 'Symbol': true, 'ZapfDingbats': true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Some characters, e.g. copyrightserif, mapped to the private use area and
|
||||||
|
// might not be displayed using standard fonts. Mapping/hacking well-known chars
|
||||||
|
// to the similar equivalents in the normal characters range.
|
||||||
|
function mapPrivateUseChars(code) {
|
||||||
|
switch (code) {
|
||||||
|
case 0xF8E9: // copyrightsans
|
||||||
|
case 0xF6D9: // copyrightserif
|
||||||
|
return 0x00A9; // copyright
|
||||||
|
default:
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var FontLoader = {
|
var FontLoader = {
|
||||||
listeningForFontLoad: false,
|
listeningForFontLoad: false,
|
||||||
|
|
||||||
@ -528,7 +573,8 @@ var FontLoader = {
|
|||||||
src += ' window.onload = function fontLoaderOnload() {\n';
|
src += ' window.onload = function fontLoaderOnload() {\n';
|
||||||
src += ' parent.postMessage(JSON.stringify(fontNames), "*");\n';
|
src += ' parent.postMessage(JSON.stringify(fontNames), "*");\n';
|
||||||
src += ' }';
|
src += ' }';
|
||||||
src += '</script></head><body>';
|
// Hack so the end script tag isn't counted if this is inline JS.
|
||||||
|
src += '</scr' + 'ipt></head><body>';
|
||||||
for (var i = 0, ii = names.length; i < ii; ++i) {
|
for (var i = 0, ii = names.length; i < ii; ++i) {
|
||||||
src += '<p style="font-family:\'' + names[i] + '\'">Hi</p>';
|
src += '<p style="font-family:\'' + names[i] + '\'">Hi</p>';
|
||||||
}
|
}
|
||||||
@ -742,7 +788,8 @@ var Font = (function FontClosure() {
|
|||||||
var names = name.split('+');
|
var names = name.split('+');
|
||||||
names = names.length > 1 ? names[1] : names[0];
|
names = names.length > 1 ? names[1] : names[0];
|
||||||
names = names.split(/[-,_]/g)[0];
|
names = names.split(/[-,_]/g)[0];
|
||||||
this.serif = serifFonts[names] || (name.search(/serif/gi) != -1);
|
this.isSerifFont = !!(properties.flags & FontFlags.Serif);
|
||||||
|
this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
|
||||||
|
|
||||||
var type = properties.type;
|
var type = properties.type;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@ -750,7 +797,7 @@ var Font = (function FontClosure() {
|
|||||||
// If the font is to be ignored, register it like an already loaded font
|
// If the font is to be ignored, register it like an already loaded font
|
||||||
// to avoid the cost of waiting for it be be loaded by the platform.
|
// to avoid the cost of waiting for it be be loaded by the platform.
|
||||||
if (properties.ignore) {
|
if (properties.ignore) {
|
||||||
this.loadedName = this.serif ? 'serif' : 'sans-serif';
|
this.loadedName = this.isSerifFont ? 'serif' : 'sans-serif';
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -780,7 +827,7 @@ var Font = (function FontClosure() {
|
|||||||
// The file data is not specified. Trying to fix the font name
|
// The file data is not specified. Trying to fix the font name
|
||||||
// to be used with the canvas.font.
|
// to be used with the canvas.font.
|
||||||
var fontName = name.replace(/[,_]/g, '-');
|
var fontName = name.replace(/[,_]/g, '-');
|
||||||
fontName = stdFontMap[fontName] || fontName;
|
fontName = stdFontMap[fontName] || nonStdFontMap[fontName] || fontName;
|
||||||
|
|
||||||
this.bold = (fontName.search(/bold/gi) != -1);
|
this.bold = (fontName.search(/bold/gi) != -1);
|
||||||
this.italic = (fontName.search(/oblique/gi) != -1) ||
|
this.italic = (fontName.search(/oblique/gi) != -1) ||
|
||||||
@ -830,7 +877,6 @@ var Font = (function FontClosure() {
|
|||||||
this.widthMultiplier = !properties.fontMatrix ? 1.0 :
|
this.widthMultiplier = !properties.fontMatrix ? 1.0 :
|
||||||
1.0 / properties.fontMatrix[0];
|
1.0 / properties.fontMatrix[0];
|
||||||
this.encoding = properties.baseEncoding;
|
this.encoding = properties.baseEncoding;
|
||||||
this.hasShortCmap = properties.hasShortCmap;
|
|
||||||
this.loadedName = getUniqueName();
|
this.loadedName = getUniqueName();
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
};
|
};
|
||||||
@ -1800,7 +1846,32 @@ var Font = (function FontClosure() {
|
|||||||
this.useToUnicode = true;
|
this.useToUnicode = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
properties.hasShortCmap = hasShortCmap;
|
|
||||||
|
if (hasShortCmap && this.hasEncoding && !this.isSymbolicFont) {
|
||||||
|
// Re-encode short map encoding to unicode -- that simplifies the
|
||||||
|
// resolution of MacRoman encoded glyphs logic for TrueType fonts:
|
||||||
|
// copying all characters to private use area, all mapping all known
|
||||||
|
// glyphs to the unicodes. The glyphs and ids arrays will grow.
|
||||||
|
var usedUnicodes = [];
|
||||||
|
for (var i = 0, ii = glyphs.length; i < ii; i++) {
|
||||||
|
var code = glyphs[i].unicode;
|
||||||
|
glyphs[i].unicode += kCmapGlyphOffset;
|
||||||
|
|
||||||
|
var glyphName = properties.baseEncoding[code];
|
||||||
|
if (glyphName in GlyphsUnicode) {
|
||||||
|
var unicode = GlyphsUnicode[glyphName];
|
||||||
|
if (unicode in usedUnicodes)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
usedUnicodes[unicode] = true;
|
||||||
|
glyphs.push({
|
||||||
|
unicode: unicode,
|
||||||
|
code: glyphs[i].code
|
||||||
|
});
|
||||||
|
ids.push(ids[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove glyph references outside range of avaialable glyphs
|
// remove glyph references outside range of avaialable glyphs
|
||||||
for (var i = 0, ii = ids.length; i < ii; i++) {
|
for (var i = 0, ii = ids.length; i < ii; i++) {
|
||||||
@ -2092,10 +2163,11 @@ var Font = (function FontClosure() {
|
|||||||
window.btoa(data) + ');');
|
window.btoa(data) + ');');
|
||||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
||||||
|
|
||||||
|
var styleElement = document.createElement('style');
|
||||||
document.documentElement.getElementsByTagName('head')[0].appendChild(
|
document.documentElement.getElementsByTagName('head')[0].appendChild(
|
||||||
document.createElement('style'));
|
styleElement);
|
||||||
|
|
||||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
var styleSheet = styleElement.sheet;
|
||||||
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
||||||
|
|
||||||
return rule;
|
return rule;
|
||||||
@ -2141,7 +2213,7 @@ var Font = (function FontClosure() {
|
|||||||
case 'CIDFontType0':
|
case 'CIDFontType0':
|
||||||
if (this.noUnicodeAdaptation) {
|
if (this.noUnicodeAdaptation) {
|
||||||
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
||||||
unicode = charcode;
|
unicode = mapPrivateUseChars(charcode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
unicode = this.toUnicode[charcode] || charcode;
|
unicode = this.toUnicode[charcode] || charcode;
|
||||||
@ -2149,7 +2221,7 @@ var Font = (function FontClosure() {
|
|||||||
case 'CIDFontType2':
|
case 'CIDFontType2':
|
||||||
if (this.noUnicodeAdaptation) {
|
if (this.noUnicodeAdaptation) {
|
||||||
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
||||||
unicode = charcode;
|
unicode = mapPrivateUseChars(charcode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
unicode = this.toUnicode[charcode] || charcode;
|
unicode = this.toUnicode[charcode] || charcode;
|
||||||
@ -2159,7 +2231,7 @@ var Font = (function FontClosure() {
|
|||||||
if (!isNum(width))
|
if (!isNum(width))
|
||||||
width = this.widths[glyphName];
|
width = this.widths[glyphName];
|
||||||
if (this.noUnicodeAdaptation) {
|
if (this.noUnicodeAdaptation) {
|
||||||
unicode = GlyphsUnicode[glyphName] || charcode;
|
unicode = mapPrivateUseChars(GlyphsUnicode[glyphName] || charcode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
unicode = this.glyphNameMap[glyphName] ||
|
unicode = this.glyphNameMap[glyphName] ||
|
||||||
@ -2184,19 +2256,14 @@ var Font = (function FontClosure() {
|
|||||||
unicode = GlyphsUnicode[glyphName] || charcode;
|
unicode = GlyphsUnicode[glyphName] || charcode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!this.hasEncoding) {
|
if (!this.hasEncoding || this.isSymbolicFont) {
|
||||||
unicode = this.useToUnicode ? this.toUnicode[charcode] : charcode;
|
unicode = this.useToUnicode ? this.toUnicode[charcode] : charcode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (this.hasShortCmap && false) {
|
|
||||||
var j = Encodings.MacRomanEncoding.indexOf(glyphName);
|
// MacRoman encoding address by re-encoding the cmap table
|
||||||
unicode = j >= 0 ? j :
|
unicode = glyphName in this.glyphNameMap ?
|
||||||
this.glyphNameMap[glyphName];
|
this.glyphNameMap[glyphName] : GlyphsUnicode[glyphName];
|
||||||
} else {
|
|
||||||
unicode = glyphName in GlyphsUnicode ?
|
|
||||||
GlyphsUnicode[glyphName] :
|
|
||||||
this.glyphNameMap[glyphName];
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
warn('Unsupported font type: ' + this.type);
|
warn('Unsupported font type: ' + this.type);
|
||||||
@ -2553,7 +2620,13 @@ var Type1Parser = function type1Parser() {
|
|||||||
while (str[index++] != ']')
|
while (str[index++] != ']')
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
var array = str.substr(start, count).split(' ');
|
str = str.substr(start, count);
|
||||||
|
|
||||||
|
str = str.trim();
|
||||||
|
// Remove adjacent spaces
|
||||||
|
str = str.replace(/\s+/g, ' ');
|
||||||
|
|
||||||
|
var array = str.split(' ');
|
||||||
for (var i = 0, ii = array.length; i < ii; i++)
|
for (var i = 0, ii = array.length; i < ii; i++)
|
||||||
array[i] = parseFloat(array[i] || 0);
|
array[i] = parseFloat(array[i] || 0);
|
||||||
return array;
|
return array;
|
||||||
@ -3299,15 +3372,9 @@ var Type2CFF = (function Type2CFFClosure() {
|
|||||||
inverseEncoding[encoding[charcode]] = charcode | 0;
|
inverseEncoding[encoding[charcode]] = charcode | 0;
|
||||||
for (var i = 0, ii = charsets.length; i < ii; i++) {
|
for (var i = 0, ii = charsets.length; i < ii; i++) {
|
||||||
var glyph = charsets[i];
|
var glyph = charsets[i];
|
||||||
if (glyph == '.notdef') {
|
if (glyph == '.notdef')
|
||||||
charstrings.push({
|
|
||||||
unicode: 0,
|
|
||||||
code: 0,
|
|
||||||
gid: i,
|
|
||||||
glyph: glyph
|
|
||||||
});
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
var code = inverseEncoding[i];
|
var code = inverseEncoding[i];
|
||||||
if (!code || isSpecialUnicode(code)) {
|
if (!code || isSpecialUnicode(code)) {
|
||||||
unassignedUnicodeItems.push(i);
|
unassignedUnicodeItems.push(i);
|
||||||
@ -3560,7 +3627,7 @@ var Type2CFF = (function Type2CFFClosure() {
|
|||||||
dict['cidOperatorPresent'] = true;
|
dict['cidOperatorPresent'] = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
TODO('interpret top dict key');
|
TODO('interpret top dict key: ' + key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dict;
|
return dict;
|
||||||
|
156
src/function.js
@ -125,109 +125,99 @@ var PDFFunction = (function PDFFunctionClosure() {
|
|||||||
else
|
else
|
||||||
decode = toMultiArray(decode);
|
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);
|
var samples = this.getSampleArray(size, outputSize, bps, str);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
CONSTRUCT_SAMPLED, inputSize, domain, encode, decode, samples, size,
|
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) {
|
constructSampledFromIR: function pdfFunctionConstructSampledFromIR(IR) {
|
||||||
var inputSize = IR[1];
|
// See chapter 3, page 109 of the PDF reference
|
||||||
var domain = IR[2];
|
function interpolate(x, xmin, xmax, ymin, ymax) {
|
||||||
var encode = IR[3];
|
return ymin + ((x - xmin) * ((ymax - ymin) / (xmax - xmin)));
|
||||||
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];
|
|
||||||
|
|
||||||
return function constructSampledFromIRResult(args) {
|
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 + ' != ' +
|
error('Incorrect number of arguments: ' + inputSize + ' != ' +
|
||||||
args.length);
|
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
|
var x = args;
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
// for each output, do m-linear interpolation
|
// Building the cube vertices: its part and sample index
|
||||||
for (i = 0; i < outputSize; ++i) {
|
// 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
|
var k = n, pos = 1;
|
||||||
for (j = 0; j < (1 << inputSize); ++j) {
|
// Map x_i to y_j for 0 <= i < m using the sampled function.
|
||||||
idx = i;
|
for (var i = 0; i < m; ++i) {
|
||||||
for (k = 0, t = j; k < inputSize; ++k, t >>= 1) {
|
// x_i' = min(max(x_i, Domain_2i), Domain_2i+1)
|
||||||
idx += idxMul[k] * (e[k][t & 1]);
|
var domain_2i = domain[i][0];
|
||||||
}
|
var domain_2i_1 = domain[i][1];
|
||||||
if (idx >= 0 && idx < nSamples) {
|
var xi = Math.min(Math.max(x[i], domain_2i), domain_2i_1);
|
||||||
sBuf[j] = samples[idx];
|
|
||||||
|
// 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 {
|
} else {
|
||||||
sBuf[j] = 0; // TODO Investigate if this is what Adobe does
|
cubeN[j] *= n0;
|
||||||
|
cubeVertex[j] += offset0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do m sets of interpolations
|
k *= size_i;
|
||||||
for (j = 0, t = (1 << inputSize); j < inputSize; ++j, t >>= 1) {
|
pos <<= 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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
1854
src/jpx.js
Normal file
159
src/obj.js
@ -287,74 +287,69 @@ var XRef = (function XRefClosure() {
|
|||||||
|
|
||||||
XRef.prototype = {
|
XRef.prototype = {
|
||||||
readXRefTable: function readXRefTable(parser) {
|
readXRefTable: function readXRefTable(parser) {
|
||||||
|
// Example of cross-reference table:
|
||||||
|
// xref
|
||||||
|
// 0 1 <-- subsection header (first obj #, obj count)
|
||||||
|
// 0000000000 65535 f <-- actual object (offset, generation #, f/n)
|
||||||
|
// 23 2 <-- subsection header ... and so on ...
|
||||||
|
// 0000025518 00002 n
|
||||||
|
// 0000025635 00000 n
|
||||||
|
// trailer
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Outer loop is over subsection headers
|
||||||
var obj;
|
var obj;
|
||||||
while (true) {
|
while (!isCmd(obj = parser.getObj(), 'trailer')) {
|
||||||
if (isCmd(obj = parser.getObj(), 'trailer'))
|
var first = obj,
|
||||||
break;
|
count = parser.getObj();
|
||||||
if (!isInt(obj))
|
|
||||||
error('Invalid XRef table');
|
if (!isInt(first) || !isInt(count))
|
||||||
var first = obj;
|
error('Invalid XRef table: wrong types in subsection header');
|
||||||
if (!isInt(obj = parser.getObj()))
|
|
||||||
error('Invalid XRef table');
|
// Inner loop is over objects themselves
|
||||||
var n = obj;
|
for (var i = 0; i < count; i++) {
|
||||||
if (first < 0 || n < 0 || (first + n) != ((first + n) | 0))
|
|
||||||
error('Invalid XRef table: ' + first + ', ' + n);
|
|
||||||
for (var i = first; i < first + n; ++i) {
|
|
||||||
var entry = {};
|
var entry = {};
|
||||||
if (!isInt(obj = parser.getObj()))
|
entry.offset = parser.getObj();
|
||||||
error('Invalid XRef table: ' + first + ', ' + n);
|
entry.gen = parser.getObj();
|
||||||
entry.offset = obj;
|
var type = parser.getObj();
|
||||||
if (!isInt(obj = parser.getObj()))
|
|
||||||
error('Invalid XRef table: ' + first + ', ' + n);
|
if (isCmd(type, 'f'))
|
||||||
entry.gen = obj;
|
|
||||||
obj = parser.getObj();
|
|
||||||
if (isCmd(obj, 'n')) {
|
|
||||||
entry.uncompressed = true;
|
|
||||||
} else if (isCmd(obj, 'f')) {
|
|
||||||
entry.free = true;
|
entry.free = true;
|
||||||
} else {
|
else if (isCmd(type, 'n'))
|
||||||
error('Invalid XRef table: ' + first + ', ' + n);
|
entry.uncompressed = true;
|
||||||
}
|
|
||||||
if (!this.entries[i]) {
|
// Validate entry obj
|
||||||
// In some buggy PDF files the xref table claims to start at 1
|
if (!isInt(entry.offset) || !isInt(entry.gen) ||
|
||||||
// instead of 0.
|
!(entry.free || entry.uncompressed)) {
|
||||||
if (i == 1 && first == 1 &&
|
error('Invalid entry in XRef subsection: ' + first + ', ' + count);
|
||||||
entry.offset == 0 && entry.gen == 65535 && entry.free) {
|
|
||||||
i = first = 0;
|
|
||||||
}
|
|
||||||
this.entries[i] = entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.entries[i + first])
|
||||||
|
this.entries[i + first] = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// read the trailer dictionary
|
// Sanity check: as per spec, first object must have these properties
|
||||||
var dict;
|
if (this.entries[0] &&
|
||||||
if (!isDict(dict = parser.getObj()))
|
!(this.entries[0].gen === 65535 && this.entries[0].free))
|
||||||
error('Invalid XRef table');
|
error('Invalid XRef table: unexpected first object');
|
||||||
|
|
||||||
// get the 'Prev' pointer
|
// Sanity check
|
||||||
var prev;
|
if (!isCmd(obj, 'trailer'))
|
||||||
obj = dict.get('Prev');
|
error('Invalid XRef table: could not find trailer dictionary');
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for 'XRefStm' key
|
// Read trailer dictionary, e.g.
|
||||||
if (isInt(obj = dict.get('XRefStm'))) {
|
// trailer
|
||||||
var pos = obj;
|
// << /Size 22
|
||||||
// ignore previously loaded xref streams (possible infinite recursion)
|
// /Root 20R
|
||||||
if (!(pos in this.xrefstms)) {
|
// /Info 10R
|
||||||
this.xrefstms[pos] = 1;
|
// /ID [ <81b14aafa313db63dbd6f981e49f94f4> ]
|
||||||
this.readXRef(pos);
|
// >>
|
||||||
}
|
// 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;
|
return dict;
|
||||||
},
|
},
|
||||||
@ -407,9 +402,6 @@ var XRef = (function XRefClosure() {
|
|||||||
}
|
}
|
||||||
range.splice(0, 2);
|
range.splice(0, 2);
|
||||||
}
|
}
|
||||||
var prev = streamParameters.get('Prev');
|
|
||||||
if (isInt(prev))
|
|
||||||
this.readXRef(prev);
|
|
||||||
return streamParameters;
|
return streamParameters;
|
||||||
},
|
},
|
||||||
indexObjects: function indexObjects() {
|
indexObjects: function indexObjects() {
|
||||||
@ -529,22 +521,47 @@ var XRef = (function XRefClosure() {
|
|||||||
try {
|
try {
|
||||||
var parser = new Parser(new Lexer(stream), true);
|
var parser = new Parser(new Lexer(stream), true);
|
||||||
var obj = parser.getObj();
|
var obj = parser.getObj();
|
||||||
|
var dict;
|
||||||
|
|
||||||
// parse an old-style xref table
|
// Get dictionary
|
||||||
if (isCmd(obj, 'xref'))
|
if (isCmd(obj, 'xref')) {
|
||||||
return this.readXRefTable(parser);
|
// Parse end-of-file XRef
|
||||||
|
dict = this.readXRefTable(parser);
|
||||||
|
|
||||||
// parse an xref stream
|
// Recursively get other XRefs 'XRefStm', if any
|
||||||
if (isInt(obj)) {
|
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()) ||
|
if (!isInt(parser.getObj()) ||
|
||||||
!isCmd(parser.getObj(), 'obj') ||
|
!isCmd(parser.getObj(), 'obj') ||
|
||||||
!isStream(obj = parser.getObj())) {
|
!isStream(obj = parser.getObj())) {
|
||||||
error('Invalid XRef stream');
|
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) {
|
} catch (e) {
|
||||||
log('Reading of the xref table/stream failed: ' + e);
|
log('(while reading XRef): ' + e);
|
||||||
}
|
}
|
||||||
|
|
||||||
warn('Indexing all PDF objects');
|
warn('Indexing all PDF objects');
|
||||||
@ -574,7 +591,7 @@ var XRef = (function XRefClosure() {
|
|||||||
var stream, parser;
|
var stream, parser;
|
||||||
if (e.uncompressed) {
|
if (e.uncompressed) {
|
||||||
if (e.gen != gen)
|
if (e.gen != gen)
|
||||||
throw ('inconsistent generation in XRef');
|
error('inconsistent generation in XRef');
|
||||||
stream = this.stream.makeSubStream(e.offset);
|
stream = this.stream.makeSubStream(e.offset);
|
||||||
parser = new Parser(new Lexer(stream), true, this);
|
parser = new Parser(new Lexer(stream), true, this);
|
||||||
var obj1 = parser.getObj();
|
var obj1 = parser.getObj();
|
||||||
@ -703,7 +720,7 @@ var PDFObjects = (function PDFObjectsClosure() {
|
|||||||
// If there isn't an object yet or the object isn't resolved, then the
|
// If there isn't an object yet or the object isn't resolved, then the
|
||||||
// data isn't ready yet!
|
// data isn't ready yet!
|
||||||
if (!obj || !obj.isResolved) {
|
if (!obj || !obj.isResolved) {
|
||||||
throw 'Requesting object that isn\'t resolved yet ' + objId;
|
error('Requesting object that isn\'t resolved yet ' + objId);
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return obj.data;
|
return obj.data;
|
||||||
|
@ -240,6 +240,10 @@ var Parser = (function ParserClosure() {
|
|||||||
var bytes = stream.getBytes(length);
|
var bytes = stream.getBytes(length);
|
||||||
return new JpegStream(bytes, stream.dict, this.xref);
|
return new JpegStream(bytes, stream.dict, this.xref);
|
||||||
}
|
}
|
||||||
|
if (name == 'JPXDecode' || name == 'JPX') {
|
||||||
|
var bytes = stream.getBytes(length);
|
||||||
|
return new JpxStream(bytes, stream.dict);
|
||||||
|
}
|
||||||
if (name == 'ASCII85Decode' || name == 'A85') {
|
if (name == 'ASCII85Decode' || name == 'A85') {
|
||||||
return new Ascii85Stream(stream);
|
return new Ascii85Stream(stream);
|
||||||
}
|
}
|
||||||
@ -249,6 +253,9 @@ var Parser = (function ParserClosure() {
|
|||||||
if (name == 'CCITTFaxDecode' || name == 'CCF') {
|
if (name == 'CCITTFaxDecode' || name == 'CCF') {
|
||||||
return new CCITTFaxStream(stream, params);
|
return new CCITTFaxStream(stream, params);
|
||||||
}
|
}
|
||||||
|
if (name == 'RunLengthDecode') {
|
||||||
|
return new RunLengthStream(stream);
|
||||||
|
}
|
||||||
warn('filter "' + name + '" not supported yet');
|
warn('filter "' + name + '" not supported yet');
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
@ -94,9 +94,9 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
|
|||||||
|
|
||||||
var colorStops = [];
|
var colorStops = [];
|
||||||
for (var i = t0; i <= t1; i += step) {
|
for (var i = t0; i <= t1; i += step) {
|
||||||
var color = fn([i]);
|
var rgbColor = cs.getRgb(fn([i]));
|
||||||
var rgbColor = Util.makeCssRgb.apply(this, cs.getRgb(color));
|
var cssColor = Util.makeCssRgb(rgbColor[0], rgbColor[1], rgbColor[2]);
|
||||||
colorStops.push([(i - t0) / diff, rgbColor]);
|
colorStops.push([(i - t0) / diff, cssColor]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.colorStops = colorStops;
|
this.colorStops = colorStops;
|
||||||
@ -234,9 +234,9 @@ var TilingPattern = (function TilingPatternClosure() {
|
|||||||
tmpCtx.strokeStyle = ctx.strokeStyle;
|
tmpCtx.strokeStyle = ctx.strokeStyle;
|
||||||
break;
|
break;
|
||||||
case PaintType.UNCOLORED:
|
case PaintType.UNCOLORED:
|
||||||
color = Util.makeCssRgb.apply(this, color);
|
var cssColor = Util.makeCssRgb(this, color[0], color[1], color[2]);
|
||||||
tmpCtx.fillStyle = color;
|
tmpCtx.fillStyle = cssColor;
|
||||||
tmpCtx.strokeStyle = color;
|
tmpCtx.strokeStyle = cssColor;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error('Unsupported paint type: ' + paintType);
|
error('Unsupported paint type: ' + paintType);
|
||||||
|
170
src/stream.js
@ -821,21 +821,25 @@ var JpegStream = (function JpegStreamClosure() {
|
|||||||
JpegStream.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) {
|
JpegStream.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) {
|
||||||
if (this.bufferLength)
|
if (this.bufferLength)
|
||||||
return;
|
return;
|
||||||
var jpegImage = new JpegImage();
|
try {
|
||||||
if (this.colorTransform != -1)
|
var jpegImage = new JpegImage();
|
||||||
jpegImage.colorTransform = this.colorTransform;
|
if (this.colorTransform != -1)
|
||||||
jpegImage.parse(this.bytes);
|
jpegImage.colorTransform = this.colorTransform;
|
||||||
var width = jpegImage.width;
|
jpegImage.parse(this.bytes);
|
||||||
var height = jpegImage.height;
|
var width = jpegImage.width;
|
||||||
var data = jpegImage.getData(width, height);
|
var height = jpegImage.height;
|
||||||
this.buffer = data;
|
var data = jpegImage.getData(width, height);
|
||||||
this.bufferLength = data.length;
|
this.buffer = data;
|
||||||
|
this.bufferLength = data.length;
|
||||||
|
} catch (e) {
|
||||||
|
error('JPEG error: ' + e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
JpegStream.prototype.getIR = function jpegStreamGetIR() {
|
JpegStream.prototype.getIR = function jpegStreamGetIR() {
|
||||||
return bytesToString(this.bytes);
|
return bytesToString(this.bytes);
|
||||||
};
|
};
|
||||||
JpegStream.prototype.getChar = function jpegStreamGetChar() {
|
JpegStream.prototype.getChar = function jpegStreamGetChar() {
|
||||||
error('internal error: getChar is not valid on JpegStream');
|
error('internal error: getChar is not valid on JpegStream');
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Checks if the image can be decoded and displayed by the browser without any
|
* Checks if the image can be decoded and displayed by the browser without any
|
||||||
@ -869,6 +873,107 @@ var JpegStream = (function JpegStreamClosure() {
|
|||||||
return JpegStream;
|
return JpegStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For JPEG 2000's we use a library to decode these images and
|
||||||
|
* the stream behaves like all the other DecodeStreams.
|
||||||
|
*/
|
||||||
|
var JpxStream = (function JpxStreamClosure() {
|
||||||
|
function JpxStream(bytes, dict) {
|
||||||
|
this.dict = dict;
|
||||||
|
this.bytes = bytes;
|
||||||
|
|
||||||
|
DecodeStream.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
JpxStream.prototype = Object.create(DecodeStream.prototype);
|
||||||
|
|
||||||
|
JpxStream.prototype.ensureBuffer = function jpxStreamEnsureBuffer(req) {
|
||||||
|
if (this.bufferLength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var jpxImage = new JpxImage();
|
||||||
|
jpxImage.parse(this.bytes);
|
||||||
|
|
||||||
|
var width = jpxImage.width;
|
||||||
|
var height = jpxImage.height;
|
||||||
|
var componentsCount = jpxImage.componentsCount;
|
||||||
|
if (componentsCount != 1 && componentsCount != 3 && componentsCount != 4)
|
||||||
|
error('JPX with ' + componentsCount + ' components is not supported');
|
||||||
|
|
||||||
|
var data = new Uint8Array(width * height * componentsCount);
|
||||||
|
|
||||||
|
for (var k = 0, kk = jpxImage.tiles.length; k < kk; k++) {
|
||||||
|
var tileCompoments = jpxImage.tiles[k];
|
||||||
|
var tileWidth = tileCompoments[0].width;
|
||||||
|
var tileHeight = tileCompoments[0].height;
|
||||||
|
var tileLeft = tileCompoments[0].left;
|
||||||
|
var tileTop = tileCompoments[0].top;
|
||||||
|
|
||||||
|
var dataPosition, sourcePosition, data0, data1, data2, data3, rowFeed;
|
||||||
|
switch (componentsCount) {
|
||||||
|
case 1:
|
||||||
|
data0 = tileCompoments[0].items;
|
||||||
|
|
||||||
|
dataPosition = width * tileTop + tileLeft;
|
||||||
|
rowFeed = width - tileWidth;
|
||||||
|
sourcePosition = 0;
|
||||||
|
for (var j = 0; j < tileHeight; j++) {
|
||||||
|
for (var i = 0; i < tileWidth; i++)
|
||||||
|
data[dataPosition++] = data0[sourcePosition++];
|
||||||
|
dataPosition += rowFeed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
data0 = tileCompoments[0].items;
|
||||||
|
data1 = tileCompoments[1].items;
|
||||||
|
data2 = tileCompoments[2].items;
|
||||||
|
|
||||||
|
dataPosition = (width * tileTop + tileLeft) * 3;
|
||||||
|
rowFeed = (width - tileWidth) * 3;
|
||||||
|
sourcePosition = 0;
|
||||||
|
for (var j = 0; j < tileHeight; j++) {
|
||||||
|
for (var i = 0; i < tileWidth; i++) {
|
||||||
|
data[dataPosition++] = data0[sourcePosition];
|
||||||
|
data[dataPosition++] = data1[sourcePosition];
|
||||||
|
data[dataPosition++] = data2[sourcePosition];
|
||||||
|
sourcePosition++;
|
||||||
|
}
|
||||||
|
dataPosition += rowFeed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
data0 = tileCompoments[0].items;
|
||||||
|
data1 = tileCompoments[1].items;
|
||||||
|
data2 = tileCompoments[2].items;
|
||||||
|
data3 = tileCompoments[3].items;
|
||||||
|
|
||||||
|
dataPosition = (width * tileTop + tileLeft) * 4;
|
||||||
|
rowFeed = (width - tileWidth) * 4;
|
||||||
|
sourcePosition = 0;
|
||||||
|
for (var j = 0; j < tileHeight; j++) {
|
||||||
|
for (var i = 0; i < tileWidth; i++) {
|
||||||
|
data[dataPosition++] = data0[sourcePosition];
|
||||||
|
data[dataPosition++] = data1[sourcePosition];
|
||||||
|
data[dataPosition++] = data2[sourcePosition];
|
||||||
|
data[dataPosition++] = data3[sourcePosition];
|
||||||
|
sourcePosition++;
|
||||||
|
}
|
||||||
|
dataPosition += rowFeed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.buffer = data;
|
||||||
|
this.bufferLength = data.length;
|
||||||
|
};
|
||||||
|
JpxStream.prototype.getChar = function jpxStreamGetChar() {
|
||||||
|
error('internal error: getChar is not valid on JpxStream');
|
||||||
|
};
|
||||||
|
|
||||||
|
return JpxStream;
|
||||||
|
})();
|
||||||
|
|
||||||
var DecryptStream = (function DecryptStreamClosure() {
|
var DecryptStream = (function DecryptStreamClosure() {
|
||||||
function DecryptStream(str, decrypt) {
|
function DecryptStream(str, decrypt) {
|
||||||
this.str = str;
|
this.str = str;
|
||||||
@ -1041,6 +1146,51 @@ var AsciiHexStream = (function AsciiHexStreamClosure() {
|
|||||||
return AsciiHexStream;
|
return AsciiHexStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
var RunLengthStream = (function RunLengthStreamClosure() {
|
||||||
|
function RunLengthStream(str) {
|
||||||
|
this.str = str;
|
||||||
|
this.dict = str.dict;
|
||||||
|
|
||||||
|
DecodeStream.call(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
RunLengthStream.prototype = Object.create(DecodeStream.prototype);
|
||||||
|
|
||||||
|
RunLengthStream.prototype.readBlock = function runLengthStreamReadBlock() {
|
||||||
|
// 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 -
|
||||||
|
// duplicate the second byte from the header (257 - n) times, n = 128 - end.
|
||||||
|
var repeatHeader = this.str.getBytes(2);
|
||||||
|
if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] == 128) {
|
||||||
|
this.eof = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bufferLength = this.bufferLength;
|
||||||
|
var n = repeatHeader[0];
|
||||||
|
if (n < 128) {
|
||||||
|
// copy n bytes
|
||||||
|
var buffer = this.ensureBuffer(bufferLength + n + 1);
|
||||||
|
buffer[bufferLength++] = repeatHeader[1];
|
||||||
|
if (n > 0) {
|
||||||
|
var source = this.str.getBytes(n);
|
||||||
|
buffer.set(source, bufferLength);
|
||||||
|
bufferLength += n;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n = 257 - n;
|
||||||
|
var b = repeatHeader[1];
|
||||||
|
var buffer = this.ensureBuffer(bufferLength + n + 1);
|
||||||
|
for (var i = 0; i < n; i++)
|
||||||
|
buffer[bufferLength++] = b;
|
||||||
|
}
|
||||||
|
this.bufferLength = bufferLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
return RunLengthStream;
|
||||||
|
})();
|
||||||
|
|
||||||
var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||||
|
|
||||||
var ccittEOL = -2;
|
var ccittEOL = -2;
|
||||||
|
38
src/util.js
@ -78,21 +78,43 @@ var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
|||||||
|
|
||||||
var Util = (function UtilClosure() {
|
var Util = (function UtilClosure() {
|
||||||
function Util() {}
|
function Util() {}
|
||||||
|
|
||||||
Util.makeCssRgb = function makergb(r, g, b) {
|
Util.makeCssRgb = function makergb(r, g, b) {
|
||||||
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
|
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
|
||||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||||
};
|
};
|
||||||
|
|
||||||
Util.makeCssCmyk = function makecmyk(c, m, y, k) {
|
Util.makeCssCmyk = function makecmyk(c, m, y, k) {
|
||||||
c = (new DeviceCmykCS()).getRgb([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;
|
var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0;
|
||||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// For 2d affine transforms
|
||||||
Util.applyTransform = function apply(p, m) {
|
Util.applyTransform = function apply(p, m) {
|
||||||
var xt = p[0] * m[0] + p[1] * m[2] + m[4];
|
var xt = p[0] * m[0] + p[1] * m[2] + m[4];
|
||||||
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
||||||
return [xt, yt];
|
return [xt, yt];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Apply a generic 3d matrix M on a 3-vector v:
|
||||||
|
// | a b c | | X |
|
||||||
|
// | d e f | x | Y |
|
||||||
|
// | g h i | | Z |
|
||||||
|
// M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
|
||||||
|
// with v as [X,Y,Z]
|
||||||
|
Util.apply3dTransform = function apply3d(m, v) {
|
||||||
|
return [
|
||||||
|
m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
|
||||||
|
m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
|
||||||
|
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.sign = function sign(num) {
|
||||||
|
return num < 0 ? -1 : 1;
|
||||||
|
};
|
||||||
|
|
||||||
return Util;
|
return Util;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -255,8 +277,8 @@ var Promise = (function PromiseClosure() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._data !== EMPTY_PROMISE) {
|
if (this._data !== EMPTY_PROMISE) {
|
||||||
throw 'Promise ' + this.name +
|
error('Promise ' + this.name +
|
||||||
': Cannot set the data of a promise twice';
|
': Cannot set the data of a promise twice');
|
||||||
}
|
}
|
||||||
this._data = value;
|
this._data = value;
|
||||||
this.hasData = true;
|
this.hasData = true;
|
||||||
@ -268,7 +290,7 @@ var Promise = (function PromiseClosure() {
|
|||||||
|
|
||||||
get data() {
|
get data() {
|
||||||
if (this._data === EMPTY_PROMISE) {
|
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;
|
return this._data;
|
||||||
},
|
},
|
||||||
@ -283,10 +305,10 @@ var Promise = (function PromiseClosure() {
|
|||||||
|
|
||||||
resolve: function promiseResolve(data) {
|
resolve: function promiseResolve(data) {
|
||||||
if (this.isResolved) {
|
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) {
|
if (this.isRejected) {
|
||||||
throw 'The Promise was already rejected ' + this.name;
|
error('The Promise was already rejected ' + this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isResolved = true;
|
this.isResolved = true;
|
||||||
@ -300,10 +322,10 @@ var Promise = (function PromiseClosure() {
|
|||||||
|
|
||||||
reject: function proimseReject(reason) {
|
reject: function proimseReject(reason) {
|
||||||
if (this.isRejected) {
|
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) {
|
if (this.isResolved) {
|
||||||
throw 'The Promise was already resolved ' + this.name;
|
error('The Promise was already resolved ' + this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isRejected = true;
|
this.isRejected = true;
|
||||||
@ -317,7 +339,7 @@ var Promise = (function PromiseClosure() {
|
|||||||
|
|
||||||
then: function promiseThen(callback, errback) {
|
then: function promiseThen(callback, errback) {
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
throw 'Requiring callback' + this.name;
|
error('Requiring callback' + this.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the promise is already resolved, call the callback directly.
|
// If the promise is already resolved, call the callback directly.
|
||||||
|
@ -26,7 +26,7 @@ function MessageHandler(name, comObj) {
|
|||||||
delete callbacks[callbackId];
|
delete callbacks[callbackId];
|
||||||
callback(data.data);
|
callback(data.data);
|
||||||
} else {
|
} else {
|
||||||
throw 'Cannot resolve callback ' + callbackId;
|
error('Cannot resolve callback ' + callbackId);
|
||||||
}
|
}
|
||||||
} else if (data.action in ah) {
|
} else if (data.action in ah) {
|
||||||
var action = ah[data.action];
|
var action = ah[data.action];
|
||||||
@ -44,7 +44,7 @@ function MessageHandler(name, comObj) {
|
|||||||
action[0].call(action[1], data.data);
|
action[0].call(action[1], data.data);
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
on: function messageHandlerOn(actionName, handler, scope) {
|
||||||
var ah = this.actionHandler;
|
var ah = this.actionHandler;
|
||||||
if (ah[actionName]) {
|
if (ah[actionName]) {
|
||||||
throw 'There is already an actionName called "' + actionName + '"';
|
error('There is already an actionName called "' + actionName + '"');
|
||||||
}
|
}
|
||||||
ah[actionName] = [handler, scope];
|
ah[actionName] = [handler, scope];
|
||||||
},
|
},
|
||||||
@ -85,13 +85,6 @@ var WorkerMessageHandler = {
|
|||||||
handler.send('test', data instanceof Uint8Array);
|
handler.send('test', data instanceof Uint8Array);
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.on('workerSrc', function wphSetupWorkerSrc(data) {
|
|
||||||
// In development, the `workerSrc` message is handled in the
|
|
||||||
// `worker_loader.js` file. In production the workerProcessHandler is
|
|
||||||
// called for this. This servers as a dummy to prevent calling an
|
|
||||||
// undefined action `workerSrc`.
|
|
||||||
});
|
|
||||||
|
|
||||||
handler.on('doc', function wphSetupDoc(data) {
|
handler.on('doc', function wphSetupDoc(data) {
|
||||||
// Create only the model of the PDFDoc, which is enough for
|
// Create only the model of the PDFDoc, which is enough for
|
||||||
// processing the content of the pdf.
|
// processing the content of the pdf.
|
||||||
@ -116,11 +109,27 @@ var WorkerMessageHandler = {
|
|||||||
// Pre compile the pdf page and fetch the fonts/images.
|
// Pre compile the pdf page and fetch the fonts/images.
|
||||||
IRQueue = page.getIRQueue(handler, dependency);
|
IRQueue = page.getIRQueue(handler, dependency);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
var minimumStackMessage =
|
||||||
|
'worker.js: while trying to getPage() and getIRQueue()';
|
||||||
|
|
||||||
// Turn the error into an obj that can be serialized
|
// Turn the error into an obj that can be serialized
|
||||||
e = {
|
if (typeof e === 'string') {
|
||||||
message: typeof e === 'object' ? e.message : e,
|
e = {
|
||||||
stack: typeof e === 'object' ? e.stack : null
|
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', {
|
handler.send('page_error', {
|
||||||
pageNum: pageNum,
|
pageNum: pageNum,
|
||||||
error: e
|
error: e
|
||||||
@ -215,6 +224,7 @@ var workerConsole = {
|
|||||||
action: 'console_error',
|
action: 'console_error',
|
||||||
data: args
|
data: args
|
||||||
});
|
});
|
||||||
|
throw 'pdf.js execution error';
|
||||||
},
|
},
|
||||||
|
|
||||||
time: function time(name) {
|
time: function time(name) {
|
||||||
@ -224,7 +234,7 @@ var workerConsole = {
|
|||||||
timeEnd: function timeEnd(name) {
|
timeEnd: function timeEnd(name) {
|
||||||
var time = consoleTimer[name];
|
var time = consoleTimer[name];
|
||||||
if (time == null) {
|
if (time == null) {
|
||||||
throw 'Unkown timer name ' + name;
|
error('Unkown timer name ' + name);
|
||||||
}
|
}
|
||||||
this.log('Timer:', name, Date.now() - time);
|
this.log('Timer:', name, Date.now() - time);
|
||||||
}
|
}
|
||||||
|
@ -3,51 +3,31 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function onMessageLoader(evt) {
|
// List of files to include;
|
||||||
// Reset the `onmessage` function as it was only set to call
|
var files = [
|
||||||
// this function the first time a message is passed to the worker
|
'core.js',
|
||||||
// but shouldn't get called anytime afterwards.
|
'util.js',
|
||||||
this.onmessage = null;
|
'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'
|
||||||
|
];
|
||||||
|
|
||||||
if (evt.data.action !== 'workerSrc') {
|
// Load all the files.
|
||||||
throw 'Worker expects first message to be `workerSrc`';
|
for (var i = 0; i < files.length; i++) {
|
||||||
}
|
importScripts(files[i]);
|
||||||
|
|
||||||
// Content of `PDFJS.workerSrc` as defined on the main thread.
|
|
||||||
var workerSrc = evt.data.data;
|
|
||||||
|
|
||||||
// Extract the directory that contains the source files to load.
|
|
||||||
// Assuming the source files have the same relative possition as the
|
|
||||||
// `workerSrc` file.
|
|
||||||
var dir = workerSrc.substring(0, workerSrc.lastIndexOf('/') + 1);
|
|
||||||
|
|
||||||
// List of files to include;
|
|
||||||
var 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'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Load all the files.
|
|
||||||
for (var i = 0; i < files.length; i++) {
|
|
||||||
importScripts(dir + files[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onmessage = onMessageLoader;
|
|
||||||
|
3
test/.gitignore
vendored
@ -0,0 +1,3 @@
|
|||||||
|
ref/
|
||||||
|
tmp/
|
||||||
|
|
@ -78,6 +78,14 @@ function cleanup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function exceptionToString(e) {
|
||||||
|
if (typeof e !== 'object')
|
||||||
|
return String(e);
|
||||||
|
if (!('message' in e))
|
||||||
|
return JSON.stringify(e);
|
||||||
|
return e.message + ('stack' in e ? ' at ' + e.stack.split('\n')[0] : '');
|
||||||
|
}
|
||||||
|
|
||||||
function nextTask() {
|
function nextTask() {
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
@ -95,7 +103,7 @@ function nextTask() {
|
|||||||
try {
|
try {
|
||||||
task.pdfDoc = new PDFJS.PDFDoc(data);
|
task.pdfDoc = new PDFJS.PDFDoc(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failure = 'load PDF doc : ' + e.toString();
|
failure = 'load PDF doc : ' + exceptionToString(e);
|
||||||
}
|
}
|
||||||
task.pageNum = task.firstPage || 1;
|
task.pageNum = task.firstPage || 1;
|
||||||
nextPage(task, failure);
|
nextPage(task, failure);
|
||||||
@ -185,7 +193,7 @@ function nextPage(task, loadError) {
|
|||||||
textLayerBuilder
|
textLayerBuilder
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failure = 'page setup : ' + e.toString();
|
failure = 'page setup : ' + exceptionToString(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
2
test/pdfs/.gitignore
vendored
@ -22,3 +22,5 @@
|
|||||||
!issue918.pdf
|
!issue918.pdf
|
||||||
!smaskdim.pdf
|
!smaskdim.pdf
|
||||||
!type4psfunc.pdf
|
!type4psfunc.pdf
|
||||||
|
!S2.pdf
|
||||||
|
!zerowidthline.pdf
|
||||||
|
2549
test/pdfs/S2.pdf
Normal file
@ -1 +1 @@
|
|||||||
http://www.intel.com/content/dam/doc/manual/64-ia-32-architectures-software-developer-vol-1-manual.pdf
|
http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-vol-1-manual.pdf
|
1
test/pdfs/issue1010.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://francois.laustriat.free.fr/test/caravan-d.pdf
|
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/issue1055.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://mcpherrin.ca/code/mozilla/engl208b.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/liveprogramming.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://blog.lassus.se/files/liveprogramming.pdf
|
BIN
test/pdfs/zerowidthline.pdf
Normal file
@ -9,7 +9,7 @@ USAGE_EXAMPLE = "%prog"
|
|||||||
# The local web server uses the git repo as the document root.
|
# The local web server uses the git repo as the document root.
|
||||||
DOC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
|
DOC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
|
||||||
|
|
||||||
ANAL = True
|
GIT_CLONE_CHECK = True
|
||||||
DEFAULT_MANIFEST_FILE = 'test_manifest.json'
|
DEFAULT_MANIFEST_FILE = 'test_manifest.json'
|
||||||
EQLOG_FILE = 'eq.log'
|
EQLOG_FILE = 'eq.log'
|
||||||
BROWSERLOG_FILE = 'browser.log'
|
BROWSERLOG_FILE = 'browser.log'
|
||||||
@ -344,7 +344,7 @@ def verifyPDFs(manifestList):
|
|||||||
|
|
||||||
def setUp(options):
|
def setUp(options):
|
||||||
# Only serve files from a pdf.js clone
|
# Only serve files from a pdf.js clone
|
||||||
assert not ANAL or os.path.isfile('../src/pdf.js') and os.path.isdir('../.git')
|
assert not GIT_CLONE_CHECK or os.path.isfile('../src/pdf.js') and os.path.isdir('../.git')
|
||||||
|
|
||||||
if options.masterMode and os.path.isdir(TMPDIR):
|
if options.masterMode and os.path.isdir(TMPDIR):
|
||||||
print 'Temporary snapshot dir tmp/ is still around.'
|
print 'Temporary snapshot dir tmp/ is still around.'
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
},
|
},
|
||||||
{ "id": "intelisa-eq",
|
{ "id": "intelisa-eq",
|
||||||
"file": "pdfs/intelisa.pdf",
|
"file": "pdfs/intelisa.pdf",
|
||||||
"md5": "f5712097d29287a97f1278839814f682",
|
"md5": "83032ccbfdc5a66269b1403971110abe",
|
||||||
"link": true,
|
"link": true,
|
||||||
"pageLimit": 100,
|
"pageLimit": 100,
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
@ -176,7 +176,6 @@
|
|||||||
"md5": "eb7b224107205db4fea9f7df0185f77d",
|
"md5": "eb7b224107205db4fea9f7df0185f77d",
|
||||||
"link": true,
|
"link": true,
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"skipPages": [12,31],
|
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
{ "id": "fips197",
|
{ "id": "fips197",
|
||||||
@ -389,11 +388,75 @@
|
|||||||
"link": true,
|
"link": true,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue1010",
|
||||||
|
"file": "pdfs/issue1010.pdf",
|
||||||
|
"md5": "f991ef093484a107fe9f59dff18fc155",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue1015",
|
{ "id": "issue1015",
|
||||||
"file": "pdfs/issue1015.pdf",
|
"file": "pdfs/issue1015.pdf",
|
||||||
"md5": "b61503d1b445742b665212866afb60e2",
|
"md5": "b61503d1b445742b665212866afb60e2",
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"link": true,
|
"link": true,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "issue1096",
|
||||||
|
"file": "pdfs/issue1096.pdf",
|
||||||
|
"md5": "7f75d2b4b93c78d401ff39e8c1b00612",
|
||||||
|
"rounds": 1,
|
||||||
|
"pageLimit": 10,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "issue1127",
|
||||||
|
"file": "pdfs/issue1127.pdf",
|
||||||
|
"md5": "4fb2be5ffefeafda4ba977de2a1bb4d8",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "liveprogramming",
|
||||||
|
"file": "pdfs/liveprogramming.pdf",
|
||||||
|
"md5": "7bd4dad1188232ef597d36fd72c33e52",
|
||||||
|
"rounds": 1,
|
||||||
|
"pageLimit": 3,
|
||||||
|
"link": true,
|
||||||
|
"type": "load"
|
||||||
|
},
|
||||||
|
{ "id": "S2-eq",
|
||||||
|
"file": "pdfs/S2.pdf",
|
||||||
|
"md5": "d0b6137846df6e0fe058f234a87fb588",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "issue1055",
|
||||||
|
"file": "pdfs/issue1055.pdf",
|
||||||
|
"md5": "3ba56c2e48dce81da8669b1b9cf98ff0",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "zerowidthline",
|
||||||
|
"file": "pdfs/zerowidthline.pdf",
|
||||||
|
"md5": "295d26e61a85635433f8e4b768953f60",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": false,
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
<script type="text/javascript" src="/src/stream.js"></script>
|
<script type="text/javascript" src="/src/stream.js"></script>
|
||||||
<script type="text/javascript" src="/src/worker.js"></script>
|
<script type="text/javascript" src="/src/worker.js"></script>
|
||||||
<script type="text/javascript" src="/external/jpgjs/jpg.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="driver.js"></script>
|
<script type="text/javascript" src="driver.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
116
test/unit/Makefile
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Create temporary profile directory name.
|
||||||
|
TEMP_PROFILE:=$(shell echo `pwd`)/test_reports/temp_profile
|
||||||
|
|
||||||
|
# These are the Firefox command line arguments.
|
||||||
|
FIREFOX_ARGS:=-no-remote -profile $(TEMP_PROFILE)
|
||||||
|
|
||||||
|
# These are the Chrome command line arguments.
|
||||||
|
CHROME_ARGS:=--user-data-dir=$(TEMP_PROFILE) --no-first-run --disable-sync
|
||||||
|
|
||||||
|
# Unit test uses the manifest from ref test to determine which browsers will
|
||||||
|
# be used for running the unit tests.
|
||||||
|
MANIFEST:=../resources/browser_manifests/browser_manifest.json
|
||||||
|
|
||||||
|
# This is a helper command to separate multiple browsers to their own lines
|
||||||
|
# for an easier sed operation.
|
||||||
|
SPLIT_LINES:=sed 's|,|,@|g' | tr '@' '\n'
|
||||||
|
|
||||||
|
# This is a helper command to join multiple lines together.
|
||||||
|
JOIN_LINES:=tr -d '\n'
|
||||||
|
|
||||||
|
# Fetch the paths to browsers that are going to be used in testing.
|
||||||
|
# For OS X the path to the binary needs to be added.
|
||||||
|
# Add the browser arguments for each browser.
|
||||||
|
# Create random profile directory for each browser.
|
||||||
|
BROWSERS_PATHS:=$(shell echo `\
|
||||||
|
sed -n 's|.*"path":\(.*\)|\1,|p' $(MANIFEST) | \
|
||||||
|
$(JOIN_LINES) \
|
||||||
|
`)
|
||||||
|
|
||||||
|
# The browser_manifest.json file has only the app directory for mac browsers.
|
||||||
|
# The absolute path to the browser binary needs to be used.
|
||||||
|
BROWSERS_PATHS_WITH_MAC_CORRECTION:=$(shell echo '$(BROWSERS_PATHS)' | \
|
||||||
|
$(SPLIT_LINES) | \
|
||||||
|
sed 's|\(Aurora\.app\)|\1/Contents/MacOS/firefox-bin|' | \
|
||||||
|
sed 's|\(Firefox.*\.app\)|\1/Contents/MacOS/firefox-bin|' | \
|
||||||
|
sed 's|\(Google Chrome\.app\)|\1/Contents/MacOS/Google Chrome|' | \
|
||||||
|
$(JOIN_LINES) \
|
||||||
|
)
|
||||||
|
|
||||||
|
# Replace " with @@@@ so that echoing do not destroy the quotation marks.
|
||||||
|
QUOTATION_MARK:=\"
|
||||||
|
SUBSTITUTE_FOR_QUOTATION_MARK:=@@@@
|
||||||
|
|
||||||
|
# Each of the browser can have their own separate arguments.
|
||||||
|
BROWSERS_WITH_ARGUMENTS:=$(shell echo '$(BROWSERS_PATHS_WITH_MAC_CORRECTION)' | \
|
||||||
|
$(SPLIT_LINES) | \
|
||||||
|
sed "s|\(irefox.*\)\($(QUOTATION_MARK)\),|\1;$(FIREFOX_ARGS)\2,|" | \
|
||||||
|
sed "s|\(hrome.*\)\($(QUOTATION_MARK)\),|\1;$(CHROME_ARGS)\2,|" | \
|
||||||
|
$(JOIN_LINES) \
|
||||||
|
)
|
||||||
|
|
||||||
|
# A temporary profile directory is needed for each of the browser. In this way
|
||||||
|
# a unit test run will not disturb the main browsing session of the user. The
|
||||||
|
# $RANDOM shell variable is used to generate non-conflicting temporary
|
||||||
|
# directories.
|
||||||
|
BROWSERS_WITH_UKNOWN_RANDOM_PROFILE_PATHS:=$(shell echo '$(BROWSERS_WITH_ARGUMENTS)' | \
|
||||||
|
$(SPLIT_LINES) | \
|
||||||
|
sed 's|\(temp_profile\)|\1_$$RANDOM$$RANDOM|' | \
|
||||||
|
sed "s|$(QUOTATION_MARK)|$(SUBSTITUTE_FOR_QUOTATION_MARK)|g" | \
|
||||||
|
$(JOIN_LINES) \
|
||||||
|
)
|
||||||
|
|
||||||
|
# Echo the variable so that the unknown random directories become known.
|
||||||
|
# Replace @@@@ with " so that jsTestDriver will work properly.
|
||||||
|
BROWSERS:=$(shell echo "$(BROWSERS_WITH_UKNOWN_RANDOM_PROFILE_PATHS)" | \
|
||||||
|
sed "s|$(SUBSTITUTE_FOR_QUOTATION_MARK)|$(QUOTATION_MARK)|g" \
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get the known random directories for browsers. This information will be used
|
||||||
|
# to create the profile directories beforehand. Create also the dummy temp
|
||||||
|
# profile directory so that the mkdir command would not fail for browsers that
|
||||||
|
# do not need it.
|
||||||
|
PROFILES:=$(TEMP_PROFILE) $(shell echo '$(BROWSERS)' | \
|
||||||
|
$(SPLIT_LINES) | \
|
||||||
|
sed -n "s|.*\( $(TEMP_PROFILE)_[0-9]\+\).*|\1|p" | \
|
||||||
|
$(JOIN_LINES) \
|
||||||
|
)
|
||||||
|
|
||||||
|
# This is the command to invoke the unit test.
|
||||||
|
PROG:=java \
|
||||||
|
-Xms512m \
|
||||||
|
-Xmx1024m \
|
||||||
|
-jar ../../external/jsTestDriver/JsTestDriver-1.3.3d.jar \
|
||||||
|
--config ./jsTestDriver.conf \
|
||||||
|
--reset \
|
||||||
|
--port 4224 \
|
||||||
|
--browser $(BROWSERS) \
|
||||||
|
--tests all \
|
||||||
|
--testOutput ./test_reports/
|
||||||
|
|
||||||
|
# This default rule runs the unit tests with the constructed command.
|
||||||
|
test:
|
||||||
|
@mkdir -p $(PROFILES)
|
||||||
|
$(PROG)
|
||||||
|
@rm -rf $(PROFILES)
|
||||||
|
|
||||||
|
# In case this Makefile needs to be debugged then this rule will provide all
|
||||||
|
# the information from intermediate steps.
|
||||||
|
debug:
|
||||||
|
@echo 'Debug browsers paths: $(BROWSERS_PATHS)'
|
||||||
|
@echo
|
||||||
|
@echo 'Debug browsers paths with mac correction: $(BROWSERS_PATHS_WITH_MAC_CORRECTION)'
|
||||||
|
@echo
|
||||||
|
@echo 'Debug browsers with arguments: $(BROWSERS_WITH_ARGUMENTS)'
|
||||||
|
@echo
|
||||||
|
@echo 'Debug browsers random profile paths: $(BROWSERS_WITH_UKNOWN_RANDOM_PROFILE_PATHS)'
|
||||||
|
@echo
|
||||||
|
@echo 'Debug browsers: $(BROWSERS)'
|
||||||
|
@echo
|
||||||
|
@echo 'Debug profiles: $(PROFILES)'
|
||||||
|
@echo
|
||||||
|
@echo 'Command to be run: $(PROG)'
|
||||||
|
@echo
|
||||||
|
|
||||||
|
.phony:: test
|
||||||
|
|
30
test/unit/jsTestDriver.conf
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
server: http://localhost:4224
|
||||||
|
|
||||||
|
load:
|
||||||
|
- ../../external/jasmine/jasmine.js
|
||||||
|
- ../../external/jasmineAdapter/JasmineAdapter.js
|
||||||
|
- ../../src/obj.js
|
||||||
|
- ../../src/core.js
|
||||||
|
- ../../src/util.js
|
||||||
|
- ../../src/canvas.js
|
||||||
|
- ../../src/obj.js
|
||||||
|
- ../../src/function.js
|
||||||
|
- ../../src/charsets.js
|
||||||
|
- ../../src/cidmaps.js
|
||||||
|
- ../../src/colorspace.js
|
||||||
|
- ../../src/crypto.js
|
||||||
|
- ../../src/evaluator.js
|
||||||
|
- ../../src/fonts.js
|
||||||
|
- ../../src/glyphlist.js
|
||||||
|
- ../../src/image.js
|
||||||
|
- ../../src/metrics.js
|
||||||
|
- ../../src/parser.js
|
||||||
|
- ../../src/pattern.js
|
||||||
|
- ../../src/stream.js
|
||||||
|
- ../../src/worker.js
|
||||||
|
- ../../external/jpgjs/jpg.js
|
||||||
|
- ../unit/obj_spec.js
|
||||||
|
- ../unit/function_spec.js
|
||||||
|
- ../unit/crypto_spec.js
|
||||||
|
- ../unit/stream_spec.js
|
||||||
|
|
3
test/unit/test_reports/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
TEST*
|
||||||
|
temp*
|
||||||
|
|
@ -1,72 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>pdf.js unit test</title>
|
|
||||||
|
|
||||||
<link rel="shortcut icon" type="image/png" href="../../external/jasmine/jasmine_favicon.png">
|
|
||||||
<link rel="stylesheet" type="text/css" href="../../external/jasmine/jasmine.css">
|
|
||||||
|
|
||||||
<script type="text/javascript" src="../../external/jasmine/jasmine.js"></script>
|
|
||||||
<script type="text/javascript" src="../../external/jasmine/jasmine-html.js"></script>
|
|
||||||
|
|
||||||
<!-- include spec files here... -->
|
|
||||||
<script type="text/javascript" src="obj_spec.js"></script>
|
|
||||||
<script type="text/javascript" src="function_spec.js"></script>
|
|
||||||
<script type="text/javascript" src="crypto_spec.js"></script>
|
|
||||||
<script type="text/javascript" src="stream_spec.js"></script>
|
|
||||||
|
|
||||||
<!-- include source files here... -->
|
|
||||||
<script type="text/javascript" src="../../src/core.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/util.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/canvas.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/obj.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/function.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/charsets.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/cidmaps.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/colorspace.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/crypto.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/evaluator.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/fonts.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/glyphlist.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/image.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/metrics.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/parser.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/pattern.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/stream.js"></script>
|
|
||||||
<script type="text/javascript" src="../../src/worker.js"></script>
|
|
||||||
<script type="text/javascript" src="../../external/jpgjs/jpg.js"></script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
(function pdfJsUnitTest() {
|
|
||||||
var jasmineEnv = jasmine.getEnv();
|
|
||||||
jasmineEnv.updateInterval = 1000;
|
|
||||||
|
|
||||||
var trivialReporter = new jasmine.TrivialReporter();
|
|
||||||
|
|
||||||
jasmineEnv.addReporter(trivialReporter);
|
|
||||||
|
|
||||||
jasmineEnv.specFilter = function pdfJsUnitTestSpecFilter(spec) {
|
|
||||||
return trivialReporter.specFilter(spec);
|
|
||||||
};
|
|
||||||
|
|
||||||
var currentWindowOnload = window.onload;
|
|
||||||
|
|
||||||
window.onload = function pdfJsUnitTestOnload() {
|
|
||||||
if (currentWindowOnload) {
|
|
||||||
currentWindowOnload();
|
|
||||||
}
|
|
||||||
execJasmine();
|
|
||||||
};
|
|
||||||
|
|
||||||
function execJasmine() {
|
|
||||||
jasmineEnv.execute();
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -224,3 +224,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// Check console compatability
|
||||||
|
(function checkConsoleCompatibility() {
|
||||||
|
if (typeof console == 'undefined') {
|
||||||
|
console = {log: function() {}};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
height="48.000000px"
|
height="48.000000px"
|
||||||
width="48.000000px"
|
width="48.000000px"
|
||||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||||
version="1.1">
|
version="1.1"
|
||||||
|
viewbox="0 0 48 48">
|
||||||
<defs
|
<defs
|
||||||
id="defs3">
|
id="defs3">
|
||||||
<inkscape:perspective
|
<inkscape:perspective
|
||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
@ -16,7 +16,8 @@
|
|||||||
id="svg2994"
|
id="svg2994"
|
||||||
height="48px"
|
height="48px"
|
||||||
width="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
|
<defs
|
||||||
id="defs3">
|
id="defs3">
|
||||||
<inkscape:perspective
|
<inkscape:perspective
|
||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
@ -16,7 +16,8 @@
|
|||||||
id="svg2913"
|
id="svg2913"
|
||||||
height="48px"
|
height="48px"
|
||||||
width="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
|
<defs
|
||||||
id="defs3">
|
id="defs3">
|
||||||
<inkscape:perspective
|
<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-filename="/home/jimmac/Desktop/wi-fi.png"
|
||||||
inkscape:export-xdpi="90.000000"
|
inkscape:export-xdpi="90.000000"
|
||||||
inkscape:export-ydpi="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
|
<defs
|
||||||
id="defs3">
|
id="defs3">
|
||||||
<inkscape:perspective
|
<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-filename="/home/jimmac/Desktop/wi-fi.png"
|
||||||
inkscape:export-xdpi="90.000000"
|
inkscape:export-xdpi="90.000000"
|
||||||
inkscape:export-ydpi="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
|
<defs
|
||||||
id="defs3">
|
id="defs3">
|
||||||
<inkscape:perspective
|
<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 |
@ -16,7 +16,8 @@
|
|||||||
inkscape:version="0.46"
|
inkscape:version="0.46"
|
||||||
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
|
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
|
||||||
sodipodi:docname="list-add.svg"
|
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
|
<defs
|
||||||
id="defs6433">
|
id="defs6433">
|
||||||
<inkscape:perspective
|
<inkscape:perspective
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -16,7 +16,8 @@
|
|||||||
inkscape:version="0.46"
|
inkscape:version="0.46"
|
||||||
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
|
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
|
||||||
sodipodi:docname="list-remove.svg"
|
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
|
<defs
|
||||||
id="defs6433">
|
id="defs6433">
|
||||||
<inkscape:perspective
|
<inkscape:perspective
|
||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 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>
|
@ -9,7 +9,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[hidden] {
|
[hidden] {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === Toolbar === */
|
/* === Toolbar === */
|
||||||
@ -241,6 +241,16 @@ canvas {
|
|||||||
-webkit-box-shadow: 0px 2px 10px #ff0;
|
-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 {
|
.textLayer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Simple pdf.js page viewer</title>
|
<title>Simple pdf.js page viewer</title>
|
||||||
|
<!-- PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="viewer.css"/>
|
<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 -->
|
<!-- PDFJSSCRIPT_INCLUDE_BUILD -->
|
||||||
<script type="text/javascript" src="../src/core.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
<script type="text/javascript" src="../src/core.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||||
@ -26,6 +28,7 @@
|
|||||||
<script type="text/javascript" src="../src/stream.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="../src/worker.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||||
<script type="text/javascript" src="../external/jpgjs/jpg.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">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||||
<script type="text/javascript" src="viewer.js"></script>
|
<script type="text/javascript" src="viewer.js"></script>
|
||||||
|
|
||||||
@ -90,7 +93,7 @@
|
|||||||
|
|
||||||
<input id="fileInput" type="file" oncontextmenu="return false;"/>
|
<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">
|
<a href="#" id="viewBookmark" title="Bookmark (or copy) current location">
|
||||||
<img src="images/bookmark.svg" alt="Bookmark" align="top" height="16"/>
|
<img src="images/bookmark.svg" alt="Bookmark" align="top" height="16"/>
|
||||||
|
247
web/viewer.js
@ -6,6 +6,7 @@
|
|||||||
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||||
var kDefaultScale = 'auto';
|
var kDefaultScale = 'auto';
|
||||||
var kDefaultScaleDelta = 1.1;
|
var kDefaultScaleDelta = 1.1;
|
||||||
|
var kUnknownScale = 0;
|
||||||
var kCacheSize = 20;
|
var kCacheSize = 20;
|
||||||
var kCssUnits = 96.0 / 72.0;
|
var kCssUnits = 96.0 / 72.0;
|
||||||
var kScrollbarPadding = 40;
|
var kScrollbarPadding = 40;
|
||||||
@ -61,34 +62,54 @@ var RenderingQueue = (function RenderingQueueClosure() {
|
|||||||
return RenderingQueue;
|
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
|
// 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
|
// If not, we use FUEL in FF
|
||||||
var Settings = (function SettingsClosure() {
|
var Settings = (function SettingsClosure() {
|
||||||
var isLocalStorageEnabled = (function localStorageEnabledTest() {
|
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 {
|
try {
|
||||||
localStorage;
|
return 'localStorage' in window && window['localStorage'] !== null &&
|
||||||
|
localStorage;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
})();
|
})();
|
||||||
var extPrefix = 'extensions.uriloader@pdf.js';
|
|
||||||
var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled;
|
var isFirefoxExtension = PDFJS.isFirefoxExtension;
|
||||||
var inPrivateBrowsing = false;
|
|
||||||
if (isExtension) {
|
|
||||||
var pbs = Components.classes['@mozilla.org/privatebrowsing;1']
|
|
||||||
.getService(Components.interfaces.nsIPrivateBrowsingService);
|
|
||||||
inPrivateBrowsing = pbs.privateBrowsingEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Settings(fingerprint) {
|
function Settings(fingerprint) {
|
||||||
var database = null;
|
var database = null;
|
||||||
var index;
|
var index;
|
||||||
if (inPrivateBrowsing)
|
if (isFirefoxExtension)
|
||||||
return false;
|
database = FirefoxCom.request('getDatabase', null) || '{}';
|
||||||
else if (isExtension)
|
|
||||||
database = Application.prefs.getValue(extPrefix + '.database', '{}');
|
|
||||||
else if (isLocalStorageEnabled)
|
else if (isLocalStorageEnabled)
|
||||||
database = localStorage.getItem('database') || '{}';
|
database = localStorage.getItem('database') || '{}';
|
||||||
else
|
else
|
||||||
@ -110,31 +131,27 @@ var Settings = (function SettingsClosure() {
|
|||||||
index = database.files.push({fingerprint: fingerprint}) - 1;
|
index = database.files.push({fingerprint: fingerprint}) - 1;
|
||||||
this.file = database.files[index];
|
this.file = database.files[index];
|
||||||
this.database = database;
|
this.database = database;
|
||||||
if (isExtension)
|
|
||||||
Application.prefs.setValue(extPrefix + '.database',
|
|
||||||
JSON.stringify(database));
|
|
||||||
else if (isLocalStorageEnabled)
|
|
||||||
localStorage.setItem('database', JSON.stringify(database));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Settings.prototype = {
|
Settings.prototype = {
|
||||||
set: function settingsSet(name, val) {
|
set: function settingsSet(name, val) {
|
||||||
if (inPrivateBrowsing)
|
if (!('file' in this))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var file = this.file;
|
var file = this.file;
|
||||||
file[name] = val;
|
file[name] = val;
|
||||||
if (isExtension)
|
var database = JSON.stringify(this.database);
|
||||||
Application.prefs.setValue(extPrefix + '.database',
|
if (isFirefoxExtension)
|
||||||
JSON.stringify(this.database));
|
FirefoxCom.request('setDatabase', database);
|
||||||
else if (isLocalStorageEnabled)
|
else if (isLocalStorageEnabled)
|
||||||
localStorage.setItem('database', JSON.stringify(this.database));
|
localStorage.setItem('database', database);
|
||||||
},
|
},
|
||||||
|
|
||||||
get: function settingsGet(name, defaultValue) {
|
get: function settingsGet(name, defaultValue) {
|
||||||
if (inPrivateBrowsing)
|
if (!('file' in this))
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
else
|
|
||||||
return this.file[name] || defaultValue;
|
return this.file[name] || defaultValue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -148,7 +165,7 @@ var currentPageNumber = 1;
|
|||||||
var PDFView = {
|
var PDFView = {
|
||||||
pages: [],
|
pages: [],
|
||||||
thumbnails: [],
|
thumbnails: [],
|
||||||
currentScale: 0,
|
currentScale: kUnknownScale,
|
||||||
currentScaleValue: null,
|
currentScaleValue: null,
|
||||||
initialBookmark: document.location.hash.substring(1),
|
initialBookmark: document.location.hash.substring(1),
|
||||||
|
|
||||||
@ -203,12 +220,12 @@ var PDFView = {
|
|||||||
|
|
||||||
zoomIn: function pdfViewZoomIn() {
|
zoomIn: function pdfViewZoomIn() {
|
||||||
var newScale = Math.min(kMaxScale, this.currentScale * kDefaultScaleDelta);
|
var newScale = Math.min(kMaxScale, this.currentScale * kDefaultScaleDelta);
|
||||||
this.setScale(newScale, true);
|
this.parseScale(newScale, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
zoomOut: function pdfViewZoomOut() {
|
zoomOut: function pdfViewZoomOut() {
|
||||||
var newScale = Math.max(kMinScale, this.currentScale / kDefaultScaleDelta);
|
var newScale = Math.max(kMinScale, this.currentScale / kDefaultScaleDelta);
|
||||||
this.setScale(newScale, true);
|
this.parseScale(newScale, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
set page(val) {
|
set page(val) {
|
||||||
@ -258,7 +275,7 @@ var PDFView = {
|
|||||||
},
|
},
|
||||||
error: function getPdfError(e) {
|
error: function getPdfError(e) {
|
||||||
var loadingIndicator = document.getElementById('loading');
|
var loadingIndicator = document.getElementById('loading');
|
||||||
loadingIndicator.innerHTML = 'Error';
|
loadingIndicator.textContent = 'Error';
|
||||||
var moreInfo = {
|
var moreInfo = {
|
||||||
message: 'Unexpected server response of ' + e.target.status + '.'
|
message: 'Unexpected server response of ' + e.target.status + '.'
|
||||||
};
|
};
|
||||||
@ -273,7 +290,13 @@ var PDFView = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
download: function pdfViewDownload() {
|
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) {
|
navigateTo: function pdfViewNavigateTo(dest) {
|
||||||
@ -294,14 +317,14 @@ var PDFView = {
|
|||||||
|
|
||||||
getDestinationHash: function pdfViewGetDestinationHash(dest) {
|
getDestinationHash: function pdfViewGetDestinationHash(dest) {
|
||||||
if (typeof dest === 'string')
|
if (typeof dest === 'string')
|
||||||
return '#' + escape(dest);
|
return PDFView.getAnchorUrl('#' + escape(dest));
|
||||||
if (dest instanceof Array) {
|
if (dest instanceof Array) {
|
||||||
var destRef = dest[0]; // see navigateTo method for dest format
|
var destRef = dest[0]; // see navigateTo method for dest format
|
||||||
var pageNumber = destRef instanceof Object ?
|
var pageNumber = destRef instanceof Object ?
|
||||||
this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
|
this.pagesRefMap[destRef.num + ' ' + destRef.gen + ' R'] :
|
||||||
(destRef + 1);
|
(destRef + 1);
|
||||||
if (pageNumber) {
|
if (pageNumber) {
|
||||||
var pdfOpenParams = '#page=' + pageNumber;
|
var pdfOpenParams = PDFView.getAnchorUrl('#page=' + pageNumber);
|
||||||
var destKind = dest[1];
|
var destKind = dest[1];
|
||||||
if ('name' in destKind && destKind.name == 'XYZ') {
|
if ('name' in destKind && destKind.name == 'XYZ') {
|
||||||
var scale = (dest[4] || this.currentScale);
|
var scale = (dest[4] || this.currentScale);
|
||||||
@ -316,6 +339,17 @@ var PDFView = {
|
|||||||
return '';
|
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.
|
* Show the error box.
|
||||||
* @param {String} message A message that is human readable.
|
* @param {String} message A message that is human readable.
|
||||||
@ -328,7 +362,7 @@ var PDFView = {
|
|||||||
errorWrapper.removeAttribute('hidden');
|
errorWrapper.removeAttribute('hidden');
|
||||||
|
|
||||||
var errorMessage = document.getElementById('errorMessage');
|
var errorMessage = document.getElementById('errorMessage');
|
||||||
errorMessage.innerHTML = message;
|
errorMessage.textContent = message;
|
||||||
|
|
||||||
var closeButton = document.getElementById('errorClose');
|
var closeButton = document.getElementById('errorClose');
|
||||||
closeButton.onclick = function() {
|
closeButton.onclick = function() {
|
||||||
@ -354,8 +388,14 @@ var PDFView = {
|
|||||||
|
|
||||||
if (moreInfo) {
|
if (moreInfo) {
|
||||||
errorMoreInfo.value += 'Message: ' + moreInfo.message;
|
errorMoreInfo.value += 'Message: ' + moreInfo.message;
|
||||||
if (moreInfo.stack)
|
if (moreInfo.stack) {
|
||||||
errorMoreInfo.value += '\n' + 'Stack: ' + 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;
|
errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1;
|
||||||
},
|
},
|
||||||
@ -363,7 +403,7 @@ var PDFView = {
|
|||||||
progress: function pdfViewProgress(level) {
|
progress: function pdfViewProgress(level) {
|
||||||
var percent = Math.round(level * 100);
|
var percent = Math.round(level * 100);
|
||||||
var loadingIndicator = document.getElementById('loading');
|
var loadingIndicator = document.getElementById('loading');
|
||||||
loadingIndicator.innerHTML = 'Loading... ' + percent + '%';
|
loadingIndicator.textContent = 'Loading... ' + percent + '%';
|
||||||
},
|
},
|
||||||
|
|
||||||
load: function pdfViewLoad(data, scale) {
|
load: function pdfViewLoad(data, scale) {
|
||||||
@ -403,7 +443,7 @@ var PDFView = {
|
|||||||
var pagesCount = pdf.numPages;
|
var pagesCount = pdf.numPages;
|
||||||
var id = pdf.fingerprint;
|
var id = pdf.fingerprint;
|
||||||
var storedHash = null;
|
var storedHash = null;
|
||||||
document.getElementById('numPages').innerHTML = pagesCount;
|
document.getElementById('numPages').textContent = pagesCount;
|
||||||
document.getElementById('pageNumber').max = pagesCount;
|
document.getElementById('pageNumber').max = pagesCount;
|
||||||
PDFView.documentFingerprint = id;
|
PDFView.documentFingerprint = id;
|
||||||
var store = PDFView.store = new Settings(id);
|
var store = PDFView.store = new Settings(id);
|
||||||
@ -453,10 +493,16 @@ var PDFView = {
|
|||||||
}
|
}
|
||||||
else if (storedHash)
|
else if (storedHash)
|
||||||
this.setHash(storedHash);
|
this.setHash(storedHash);
|
||||||
else {
|
else if (scale) {
|
||||||
this.parseScale(scale || kDefaultScale, true);
|
this.parseScale(scale, true);
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PDFView.currentScale === kUnknownScale) {
|
||||||
|
// Scale was not initialized: invalid bookmark or scale was not specified.
|
||||||
|
// Setting the default one.
|
||||||
|
this.parseScale(kDefaultScale, true);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setHash: function pdfViewSetHash(hash) {
|
setHash: function pdfViewSetHash(hash) {
|
||||||
@ -612,6 +658,10 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
div.removeAttribute('data-loaded');
|
div.removeAttribute('data-loaded');
|
||||||
|
|
||||||
delete this.canvas;
|
delete this.canvas;
|
||||||
|
|
||||||
|
this.loadingIconDiv = document.createElement('div');
|
||||||
|
this.loadingIconDiv.className = 'loadingIcon';
|
||||||
|
div.appendChild(this.loadingIconDiv);
|
||||||
};
|
};
|
||||||
|
|
||||||
function setupAnnotations(content, scale) {
|
function setupAnnotations(content, scale) {
|
||||||
@ -649,7 +699,15 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
if (!item.content) {
|
if (!item.content) {
|
||||||
content.setAttribute('hidden', true);
|
content.setAttribute('hidden', true);
|
||||||
} else {
|
} 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() {
|
image.addEventListener('mouseover', function annotationImageOver() {
|
||||||
this.nextSibling.removeAttribute('hidden');
|
this.nextSibling.removeAttribute('hidden');
|
||||||
}, false);
|
}, false);
|
||||||
@ -743,6 +801,8 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
|
|
||||||
if (scale && scale !== PDFView.currentScale)
|
if (scale && scale !== PDFView.currentScale)
|
||||||
PDFView.parseScale(scale, true);
|
PDFView.parseScale(scale, true);
|
||||||
|
else if (PDFView.currentScale === kUnknownScale)
|
||||||
|
PDFView.parseScale(kDefaultScale, true);
|
||||||
|
|
||||||
setTimeout(function pageViewScrollIntoViewRelayout() {
|
setTimeout(function pageViewScrollIntoViewRelayout() {
|
||||||
// letting page to re-layout before scrolling
|
// letting page to re-layout before scrolling
|
||||||
@ -766,7 +826,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.drawingRequired = function() {
|
this.drawingRequired = function() {
|
||||||
return !div.hasChildNodes();
|
return !div.querySelector('canvas');
|
||||||
};
|
};
|
||||||
|
|
||||||
this.draw = function pageviewDraw(callback) {
|
this.draw = function pageviewDraw(callback) {
|
||||||
@ -801,19 +861,23 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
ctx.translate(-this.x * scale, -this.y * scale);
|
ctx.translate(-this.x * scale, -this.y * scale);
|
||||||
|
|
||||||
this.content.startRendering(ctx,
|
// Rendering area
|
||||||
(function pageViewDrawCallback(error) {
|
|
||||||
if (error)
|
|
||||||
PDFView.error('An error occurred while rendering the page.', error);
|
|
||||||
this.stats = content.bench;
|
|
||||||
this.updateStats();
|
|
||||||
if (this.onAfterDraw)
|
|
||||||
this.onAfterDraw();
|
|
||||||
|
|
||||||
cache.push(this);
|
var self = this;
|
||||||
callback();
|
this.content.startRendering(ctx, function pageViewDrawCallback(error) {
|
||||||
}).bind(this), textLayer
|
div.removeChild(self.loadingIconDiv);
|
||||||
);
|
|
||||||
|
if (error)
|
||||||
|
PDFView.error('An error occurred while rendering the page.', error);
|
||||||
|
|
||||||
|
self.stats = content.bench;
|
||||||
|
self.updateStats();
|
||||||
|
if (self.onAfterDraw)
|
||||||
|
self.onAfterDraw();
|
||||||
|
|
||||||
|
cache.push(self);
|
||||||
|
callback();
|
||||||
|
}, textLayer);
|
||||||
|
|
||||||
setupAnnotations(this.content, this.scale);
|
setupAnnotations(this.content, this.scale);
|
||||||
div.setAttribute('data-loaded', true);
|
div.setAttribute('data-loaded', true);
|
||||||
@ -831,7 +895,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
|
|
||||||
var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
||||||
var anchor = document.createElement('a');
|
var anchor = document.createElement('a');
|
||||||
anchor.href = '#' + id;
|
anchor.href = PDFView.getAnchorUrl('#page=' + id);
|
||||||
anchor.onclick = function stopNivigation() {
|
anchor.onclick = function stopNivigation() {
|
||||||
PDFView.page = id;
|
PDFView.page = id;
|
||||||
return false;
|
return false;
|
||||||
@ -963,22 +1027,55 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
|
|||||||
var self = this;
|
var self = this;
|
||||||
var textDivs = this.textDivs;
|
var textDivs = this.textDivs;
|
||||||
var textLayerDiv = this.textLayerDiv;
|
var textLayerDiv = this.textLayerDiv;
|
||||||
this.textLayerTimer = setInterval(function renderTextLayer() {
|
var renderTimer = null;
|
||||||
|
var renderingDone = false;
|
||||||
|
var renderInterval = 0;
|
||||||
|
var resumeInterval = 500; // in ms
|
||||||
|
|
||||||
|
// Render the text layer, one div at a time
|
||||||
|
function renderTextLayer() {
|
||||||
if (textDivs.length === 0) {
|
if (textDivs.length === 0) {
|
||||||
clearInterval(self.textLayerTimer);
|
clearInterval(renderTimer);
|
||||||
|
renderingDone = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var textDiv = textDivs.shift();
|
var textDiv = textDivs.shift();
|
||||||
if (textDiv.dataset.textLength >= 1) { // avoid div by zero
|
if (textDiv.dataset.textLength > 0) {
|
||||||
textLayerDiv.appendChild(textDiv);
|
textLayerDiv.appendChild(textDiv);
|
||||||
// Adjust div width (via letterSpacing) to match canvas text
|
|
||||||
// Due to the .offsetWidth calls, this is slow
|
if (textDiv.dataset.textLength > 1) { // avoid div by zero
|
||||||
textDiv.style.letterSpacing =
|
// Adjust div width (via letterSpacing) to match canvas text
|
||||||
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
|
// Due to the .offsetWidth calls, this is slow
|
||||||
(textDiv.dataset.textLength - 1)) + 'px';
|
// This needs to come after appending to the DOM
|
||||||
|
textDiv.style.letterSpacing =
|
||||||
|
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
|
||||||
|
(textDiv.dataset.textLength - 1)) + 'px';
|
||||||
|
}
|
||||||
|
} // textLength > 0
|
||||||
|
}
|
||||||
|
renderTimer = setInterval(renderTextLayer, renderInterval);
|
||||||
|
|
||||||
|
// Stop rendering when user scrolls. Resume after XXX milliseconds
|
||||||
|
// of no scroll events
|
||||||
|
var scrollTimer = null;
|
||||||
|
function textLayerOnScroll() {
|
||||||
|
if (renderingDone) {
|
||||||
|
window.removeEventListener('scroll', textLayerOnScroll, false);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}, 0);
|
|
||||||
};
|
// Immediately pause rendering
|
||||||
|
clearInterval(renderTimer);
|
||||||
|
|
||||||
|
clearTimeout(scrollTimer);
|
||||||
|
scrollTimer = setTimeout(function textLayerScrollTimer() {
|
||||||
|
// Resume rendering
|
||||||
|
renderTimer = setInterval(renderTextLayer, renderInterval);
|
||||||
|
}, resumeInterval);
|
||||||
|
}; // textLayerOnScroll
|
||||||
|
|
||||||
|
window.addEventListener('scroll', textLayerOnScroll, false);
|
||||||
|
}; // endLayout
|
||||||
|
|
||||||
this.appendText = function textLayerBuilderAppendText(text,
|
this.appendText = function textLayerBuilderAppendText(text,
|
||||||
fontName, fontSize) {
|
fontName, fontSize) {
|
||||||
@ -1006,12 +1103,18 @@ window.addEventListener('load', function webViewerLoad(evt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var scale = ('scale' in params) ? params.scale : 0;
|
var scale = ('scale' in params) ? params.scale : 0;
|
||||||
PDFView.open(params.file || kDefaultURL, parseFloat(scale));
|
var file = PDFJS.isFirefoxExtension ?
|
||||||
|
window.location.toString() : params.file || kDefaultURL;
|
||||||
|
PDFView.open(file, parseFloat(scale));
|
||||||
|
|
||||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
if (PDFJS.isFirefoxExtension || !window.File || !window.FileReader ||
|
||||||
|
!window.FileList || !window.Blob) {
|
||||||
document.getElementById('fileInput').setAttribute('hidden', 'true');
|
document.getElementById('fileInput').setAttribute('hidden', 'true');
|
||||||
else
|
document.getElementById('fileInputSeperator')
|
||||||
|
.setAttribute('hidden', 'true');
|
||||||
|
} else {
|
||||||
document.getElementById('fileInput').value = null;
|
document.getElementById('fileInput').value = null;
|
||||||
|
}
|
||||||
|
|
||||||
if ('disableWorker' in params)
|
if ('disableWorker' in params)
|
||||||
PDFJS.disableWorker = (params['disableWorker'] === 'true');
|
PDFJS.disableWorker = (params['disableWorker'] === 'true');
|
||||||
@ -1101,8 +1204,8 @@ function updateViewarea() {
|
|||||||
store.set('zoom', normalizedScaleValue);
|
store.set('zoom', normalizedScaleValue);
|
||||||
store.set('scrollLeft', Math.round(topLeft.x));
|
store.set('scrollLeft', Math.round(topLeft.x));
|
||||||
store.set('scrollTop', Math.round(topLeft.y));
|
store.set('scrollTop', Math.round(topLeft.y));
|
||||||
|
var href = PDFView.getAnchorUrl(pdfOpenParams);
|
||||||
document.getElementById('viewBookmark').href = pdfOpenParams;
|
document.getElementById('viewBookmark').href = href;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('scroll', function webViewerScroll(evt) {
|
window.addEventListener('scroll', function webViewerScroll(evt) {
|
||||||
@ -1242,7 +1345,7 @@ window.addEventListener('keydown', function keydown(evt) {
|
|||||||
handled = true;
|
handled = true;
|
||||||
break;
|
break;
|
||||||
case 48: // '0'
|
case 48: // '0'
|
||||||
PDFView.setScale(kDefaultScale, true);
|
PDFView.parseScale(kDefaultScale, true);
|
||||||
handled = true;
|
handled = true;
|
||||||
break;
|
break;
|
||||||
case 37: // left arrow
|
case 37: // left arrow
|
||||||
|