Merge upstream.

This commit is contained in:
Brendan Dahl 2012-03-21 16:13:24 -07:00
commit 2d7e1d6346
33 changed files with 1046 additions and 769 deletions

View File

@ -210,6 +210,7 @@ pages-repo: | $(BUILD_DIR)
# copy of the pdf.js source.
CONTENT_DIR := content
BUILD_NUMBER := `git log --format=oneline $(EXTENSION_BASE_VERSION).. | wc -l | awk '{print $$1}'`
PDFJSSCRIPT_VERSION := 0.2.$(BUILD_NUMBER)
EXTENSION_WEB_FILES = \
web/images \
web/viewer.css \
@ -225,14 +226,28 @@ FIREFOX_CONTENT_DIR := $(EXTENSION_SRC)/firefox/$(CONTENT_DIR)/
FIREFOX_EXTENSION_FILES_TO_COPY = \
*.js \
*.rdf \
*.png \
install.rdf.in \
README.mozilla \
components \
../../LICENSE \
$(NULL)
FIREFOX_EXTENSION_FILES = \
content \
*.js \
bootstrap.js \
install.rdf \
icon.png \
icon64.png \
components \
content \
LICENSE \
$(NULL)
FIREFOX_MC_EXTENSION_FILES = \
bootstrap.js \
icon.png \
icon64.png \
components \
content \
LICENSE \
$(NULL)
CHROME_BUILD_DIR := $(BUILD_DIR)/chrome
@ -265,9 +280,12 @@ extension: | production
# We don't need pdf.js anymore since its inlined
@rm -Rf $(FIREFOX_BUILD_CONTENT)/$(BUILD_DIR)/;
# Update the build version number
@sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/install.rdf
@sed -i.bak "s/PDFJSSCRIPT_BUILD/$(BUILD_NUMBER)/" $(FIREFOX_BUILD_DIR)/update.rdf
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/install.rdf.in
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/update.rdf
@sed -i.bak "s/PDFJSSCRIPT_VERSION/$(PDFJSSCRIPT_VERSION)/" $(FIREFOX_BUILD_DIR)/README.mozilla
@rm -f $(FIREFOX_BUILD_DIR)/*.bak
@find $(FIREFOX_BUILD_DIR) -name ".*" -delete
# Create the xpi
@cd $(FIREFOX_BUILD_DIR); zip -r $(FIREFOX_EXTENSION_NAME) $(FIREFOX_EXTENSION_FILES)
@echo "extension created: " $(FIREFOX_EXTENSION_NAME)
@ -276,6 +294,8 @@ extension: | production
@rm -f $(FIREFOX_BUILD_DIR)/*.bak
@cd $(FIREFOX_BUILD_DIR); zip -r $(FIREFOX_AMO_EXTENSION_NAME) $(FIREFOX_EXTENSION_FILES)
@echo "AMO extension created: " $(FIREFOX_AMO_EXTENSION_NAME)
# List all files for mozilla-central
@cd $(FIREFOX_BUILD_DIR); find $(FIREFOX_MC_EXTENSION_FILES) -type f > extension-files
# Clear out everything in the chrome extension build directory
@rm -Rf $(CHROME_BUILD_DIR)

View File

@ -1,6 +1,6 @@
# PDF.JS
pdf.js is an HTML5 technology experiment that explores building a faithful
and efficient Portable Document Format (PDF) renderer without native code
assistance.

View File

@ -1,18 +1,28 @@
<!doctype html>
<script>
function isPdfDownloadable(details) {
return details.url.indexOf('pdfjs.action=download') >= 0;
}
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if (isPdfDownloadable(details))
return;
var viewerPage = 'content/web/viewer.html';
var url = chrome.extension.getURL(viewerPage) + '?file=' + details.url;
var url = chrome.extension.getURL(viewerPage) +
'?file=' + encodeURIComponent(details.url);
return { redirectUrl: url };
},
{
urls: [
"http://*/*.pdf",
"file://*/*.pdf",
"file://*/*.pdf"
],
types: [ "main_frame" ]
},
["blocking"]);
</script>

View File

@ -0,0 +1,4 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
Current extension version is: PDFJSSCRIPT_VERSION

View File

@ -10,10 +10,14 @@ let Cc = Components.classes;
let Ci = Components.interfaces;
let Cm = Components.manager;
let Cu = Components.utils;
let application = Cc['@mozilla.org/fuel/application;1']
.getService(Ci.fuelIApplication);
Cu.import('resource://gre/modules/Services.jsm');
function log(str) {
if (!application.prefs.getValue(EXT_PREFIX + '.pdfBugEnabled', false))
return;
dump(str + '\n');
}
@ -60,12 +64,7 @@ function startup(aData, aReason) {
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);
var aliasURI = ioService.newURI('content/', 'UTF-8', aData.resourceURI);
resProt.setSubstitution(RESOURCE_NAME, aliasURI);
// Load the component and register it.
@ -73,12 +72,11 @@ function startup(aData, aReason) {
'components/PdfStreamConverter.js';
Cu.import(pdfStreamConverterUrl);
Factory.register(PdfStreamConverter);
Services.prefs.setBoolPref('extensions.pdf.js.active', true);
}
function shutdown(aData, aReason) {
if (Services.prefs.getBoolPref('extensions.pdf.js.active'))
Services.prefs.setBoolPref('extensions.pdf.js.active', false);
if (aReason == APP_SHUTDOWN)
return;
var ioService = Services.io;
var resProt = ioService.getProtocolHandler('resource')
.QueryInterface(Ci.nsIResProtocolHandler);
@ -87,18 +85,14 @@ function shutdown(aData, aReason) {
// Remove the contract/component.
Factory.unregister();
// Unload the converter
if (pdfStreamConverterUrl) {
Cu.unload(pdfStreamConverterUrl);
pdfStreamConverterUrl = null;
}
Cu.unload(pdfStreamConverterUrl);
pdfStreamConverterUrl = null;
}
function install(aData, aReason) {
Services.prefs.setBoolPref('extensions.pdf.js.active', false);
}
function uninstall(aData, aReason) {
Services.prefs.clearUserPref('extensions.pdf.js.active');
application.prefs.setValue(EXT_PREFIX + '.database', '{}');
}

View File

@ -17,7 +17,15 @@ const MAX_DATABASE_LENGTH = 4096;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import('resource://gre/modules/Services.jsm');
let application = Cc['@mozilla.org/fuel/application;1']
.getService(Ci.fuelIApplication);
let privateBrowsing = Cc['@mozilla.org/privatebrowsing;1']
.getService(Ci.nsIPrivateBrowsingService);
let inPrivateBrowswing = privateBrowsing.privateBrowsingEnabled;
function log(aMsg) {
if (!application.prefs.getValue(EXT_PREFIX + '.pdfBugEnabled', false))
return;
let msg = 'PdfStreamConverter.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
Services.console.logStringMessage(msg);
dump(msg + '\n');
@ -40,11 +48,6 @@ function topWindow(win) {
.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() {
@ -66,6 +69,9 @@ ChromeActions.prototype = {
if (this.inPrivateBrowswing)
return '{}';
return application.prefs.getValue(EXT_PREFIX + '.database', '{}');
},
pdfBugEnabled: function() {
return application.prefs.getValue(EXT_PREFIX + '.pdfBugEnabled', false);
}
};
@ -122,15 +128,12 @@ PdfStreamConverter.prototype = {
// nsIStreamConverter::asyncConvertData
asyncConvertData: function(aFromType, aToType, aListener, aCtxt) {
if (!Services.prefs.getBoolPref('extensions.pdf.js.active'))
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
// Ignoring HTTP POST requests -- pdf.js has to repeat the request.
var skipConversion = false;
try {
var request = aCtxt;
request.QueryInterface(Ci.nsIHttpChannel);
skipConversion = (request.requestMethod === 'POST');
skipConversion = (request.requestMethod !== 'GET');
} catch (e) {
// Non-HTTP request... continue normally.
}

BIN
extensions/firefox/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -5,20 +5,18 @@
<Description about="urn:mozilla:install-manifest">
<em:id>uriloader@pdf.js</em:id>
<em:name>pdf.js</em:name>
<em:version>0.2.PDFJSSCRIPT_BUILD</em:version>
<em:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL>
<em:name>PDF Viewer</em:name>
<em:version>PDFJSSCRIPT_VERSION</em:version>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>6.0</em:minVersion>
<em:maxVersion>13.0a1</em:maxVersion>
<em:maxVersion>14.0a1</em:maxVersion>
</Description>
</em:targetApplication>
<em:bootstrap>true</em:bootstrap>
<em:unpack>true</em:unpack>
<em:creator>Mozilla Labs</em:creator>
<em:description>pdf.js uri loader</em:description>
<em:creator>Mozilla</em:creator>
<em:description>Uses HTML5 to display PDF files directly in Firefox.</em:description>
<em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL>
<em:type>2</em:type>
<!-- Use the raw link for updates so we we can use SSL. -->

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>uriloader@pdf.js</em:id>
<em:name>PDF Viewer</em:name>
<em:version>PDFJSSCRIPT_VERSION</em:version>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>@FIREFOX_VERSION@</em:minVersion>
<em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<em:strictCompatibility>true</em:strictCompatibility>
<em:bootstrap>true</em:bootstrap>
<em:creator>Mozilla</em:creator>
<em:description>Uses HTML5 to display PDF files directly in Firefox.</em:description>
<em:homepageURL>http://support.mozilla.org/kb/using-mozilla-pdf-viewer</em:homepageURL>
<em:type>2</em:type>
</Description>
</RDF>

View File

@ -8,7 +8,7 @@
<RDF:Seq>
<RDF:li>
<RDF:Description>
<em:version>0.2.PDFJSSCRIPT_BUILD</em:version>
<em:version>PDFJSSCRIPT_VERSION</em:version>
<em:targetApplication>
<RDF:Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>

View File

@ -1,8 +1,10 @@
# ShellJS - Unix shell commands for Node.js [![Build Status](https://secure.travis-ci.org/arturadib/shelljs.png)](http://travis-ci.org/arturadib/shelljs)
_This project is young and experimental. Use at your own risk._
ShellJS is a **portable** (Windows included) implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands.
The project is both [unit-tested](http://travis-ci.org/arturadib/shelljs) and battle-tested at Mozilla's [pdf.js](http://github.com/mozilla/pdf.js).
The project is [unit-tested](http://travis-ci.org/arturadib/shelljs) and is being used at Mozilla's [pdf.js](http://github.com/mozilla/pdf.js).
### Example
@ -12,7 +14,7 @@ require('shelljs/global');
// Copy files to release dir
mkdir('-p', 'out/Release');
cp('-R', 'lib/*.js', 'out/Release');
cp('-R', 'stuff/*', 'out/Release');
// Replace macros in each .js file
cd('lib');
@ -130,6 +132,27 @@ Returns list of files in the given path, or in current directory if no path prov
For convenient iteration via `for (file in ls())`, the format returned is a hash object:
`{ 'file1':null, 'dir1/file2':null, ...}`.
#### find(path [,path ...])
#### find(path_array)
Examples:
```javascript
find('src', 'lib');
find(['src', 'lib']); // same as above
for (file in find('.')) {
if (!file.match(/\.js$/))
continue;
// all files at this point end in '.js'
}
```
Returns list of all files (however deep) in the given paths. For convenient iteration
via `for (file in find(...))`, the format returned is a hash object:
`{ 'file1':null, 'dir1/file2':null, ...}`.
The main difference with respect to `ls('-R', path)` is that the resulting file names
include the base directories, e.g. `lib/resources/file1` instead of just `file1`.
#### cp('[options ,] source [,source ...], dest')
#### cp('[options ,] source_array, dest')
Available options:
@ -195,6 +218,21 @@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above
Creates directories.
#### test(expression)
Available expression primaries:
+ `'-d', 'path'`: true if path is a directory
+ `'-f', 'path'`: true if path is a regular file
Examples:
```javascript
if (test('-d', path)) { /* do something with dir */ };
if (!test('-f', path)) continue; // skip if it's a regular file
```
Evaluates expression using the available primaries and returns corresponding value.
#### cat(file [, file ...])
#### cat(file_array)

View File

@ -57,6 +57,7 @@ function _pwd(options) {
};
exports.pwd = wrap('pwd', _pwd);
//@
//@ #### ls([options ,] path [,path ...])
//@ #### ls([options ,] path_array)
@ -159,6 +160,54 @@ function _ls(options, paths) {
exports.ls = wrap('ls', _ls);
//@
//@ #### find(path [,path ...])
//@ #### find(path_array)
//@ Examples:
//@
//@ ```javascript
//@ find('src', 'lib');
//@ find(['src', 'lib']); // same as above
//@ for (file in find('.')) {
//@ if (!file.match(/\.js$/))
//@ continue;
//@ // all files at this point end in '.js'
//@ }
//@ ```
//@
//@ Returns list of all files (however deep) in the given paths. For convenient iteration
//@ via `for (file in find(...))`, the format returned is a hash object:
//@ `{ 'file1':null, 'dir1/file2':null, ...}`.
//@
//@ The main difference from `ls('-R', path)` is that the resulting file names
//@ include the base directories, e.g. `lib/resources/file1` instead of just `file1`.
function _find(options, paths) {
if (!paths)
error('no path specified');
else if (typeof paths === 'object')
paths = paths; // assume array
else if (typeof paths === 'string')
paths = [].slice.call(arguments, 1);
var hash = {};
// why not simply do ls('-R', paths)? because the output wouldn't give the base dirs
// to get the base dir in the output, we need instead ls('-R', 'dir/*') for every directory
paths.forEach(function(file){
hash[file] = null;
if (fs.statSync(file).isDirectory()) {
for (subfile in _ls('-Ra', file+'/*'))
hash[subfile] = null;
}
});
return hash;
}
exports.find = wrap('find', _find);
//@
//@ #### cp('[options ,] source [,source ...], dest')
//@ #### cp('[options ,] source_array, dest')
@ -438,6 +487,42 @@ function _mkdir(options, dirs) {
}; // mkdir
exports.mkdir = wrap('mkdir', _mkdir);
//@
//@ #### test(expression)
//@ Available expression primaries:
//@
//@ + `'-d', 'path'`: true if path is a directory
//@ + `'-f', 'path'`: true if path is a regular file
//@
//@ Examples:
//@
//@ ```javascript
//@ if (test('-d', path)) { /* do something with dir */ };
//@ if (!test('-f', path)) continue; // skip if it's a regular file
//@ ```
//@
//@ Evaluates expression using the available primaries and returns corresponding value.
function _test(options, path) {
if (!path)
error('no path given');
// hack - only works with unary primaries
options = parseOptions(options, {
'd': 'directory',
'f': 'file'
});
if (!options.directory && !options.file)
error('could not interpret expression');
if (options.directory)
return fs.existsSync(path) && fs.statSync(path).isDirectory();
if (options.file)
return fs.existsSync(path) && fs.statSync(path).isFile();
}; // test
exports.test = wrap('test', _test);
//@
//@ #### cat(file [, file ...])
//@ #### cat(file_array)

56
make.js
View File

@ -149,7 +149,7 @@ target.pagesrepo = function() {
echo();
echo('Cloning project repo...');
echo('(This operation can take a while, depending on network conditions)');
exec('git clone -b gh-pages --depth=1 ' + REPO + ' ' + ßGH_PAGES_DIR,
exec('git clone -b gh-pages --depth=1 ' + REPO + ' ' + GH_PAGES_DIR,
{silent: true});
echo('Done.');
}
@ -168,13 +168,16 @@ target.pagesrepo = function() {
//
var EXTENSION_WEB_FILES =
['web/images',
['web/debugger.js',
'web/images',
'web/viewer.css',
'web/viewer.js',
'web/viewer.html',
'web/viewer-production.html'],
EXTENSION_BASE_VERSION = '4bb289ec499013de66eb421737a4dbb4a9273eda',
EXTENSION_BUILD_NUMBER;
EXTENSION_VERSION_PREFIX = '0.2.',
EXTENSION_BUILD_NUMBER,
EXTENSION_VERSION;
//
// make extension
@ -200,6 +203,8 @@ target.buildnumber = function() {
.output.match(/\n/g).length; // get # of lines in git output
echo('Extension build number: ' + EXTENSION_BUILD_NUMBER);
EXTENSION_VERSION = EXTENSION_VERSION_PREFIX + EXTENSION_BUILD_NUMBER;
};
//
@ -215,13 +220,26 @@ target.firefox = function() {
FIREFOX_EXTENSION_FILES_TO_COPY =
['*.js',
'*.rdf',
'components'];
FIREFOX_EXTENSION_FILES =
['content',
'*.js',
'install.rdf',
'*.png',
'install.rdf.in',
'README.mozilla',
'components',
'content'];
'../../LICENSE'];
FIREFOX_EXTENSION_FILES =
['bootstrap.js',
'install.rdf',
'icon.png',
'icon64.png',
'components',
'content',
'LICENSE'];
FIREFOX_MC_EXTENSION_FILES =
['bootstrap.js',
'icon.png',
'icon64.png',
'components',
'content',
'LICENSE'];
FIREFOX_EXTENSION_NAME = 'pdf.js.xpi',
FIREFOX_AMO_EXTENSION_NAME = 'pdf.js.amo.xpi';
@ -258,10 +276,17 @@ target.firefox = function() {
// We don't need pdf.js anymore since its inlined
rm('-Rf', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR);
// Remove '.DS_Store' and other hidden files
for (file in find(FIREFOX_BUILD_DIR)) {
if (file.match(/^\./))
rm('-f', file);
}
// Update the build version number
sed('-i', /PDFJSSCRIPT_BUILD/, EXTENSION_BUILD_NUMBER, FIREFOX_BUILD_DIR + '/install.rdf');
sed('-i', /PDFJSSCRIPT_BUILD/, EXTENSION_BUILD_NUMBER, FIREFOX_BUILD_DIR + '/update.rdf');
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/install.rdf');
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/update.rdf');
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/install.rdf.in');
sed('-i', /PDFJSSCRIPT_VERSION/, EXTENSION_VERSION, FIREFOX_BUILD_DIR + '/README.mozilla');
// Create the xpi
cd(FIREFOX_BUILD_DIR);
@ -275,6 +300,15 @@ target.firefox = function() {
exec('zip -r ' + FIREFOX_AMO_EXTENSION_NAME + ' ' + FIREFOX_EXTENSION_FILES.join(' '));
echo('AMO extension created: ' + FIREFOX_AMO_EXTENSION_NAME);
cd(ROOT_DIR);
// List all files for mozilla-central
cd(FIREFOX_BUILD_DIR);
var extensionFiles = '';
for (file in find(FIREFOX_MC_EXTENSION_FILES)) {
if (test('-f', file))
extensionFiles += file+'\n';
}
extensionFiles.to('extension-files');
};
//

View File

@ -1,433 +1,436 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
var bidi = PDFJS.bidi = (function bidiClosure() {
// Character types for symbols from 0000 to 00FF.
var baseTypes = [
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
];
// Character types for symbols from 0600 to 06FF
var arabicTypes = [
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
];
function isOdd(i) {
return (i & 1) != 0;
}
function isEven(i) {
return (i & 1) == 0;
}
function findUnequal(arr, start, value) {
var j;
for (var j = start, jj = arr.length; j < jj; ++j) {
if (arr[j] != value)
return j;
}
return j;
}
function setValues(arr, start, end, value) {
for (var j = start; j < end; ++j) {
arr[j] = value;
}
}
function reverseValues(arr, start, end) {
for (var i = start, j = end - 1; i < j; ++i, --j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
function mirrorGlyphs(c) {
/*
# BidiMirroring-1.txt
0028; 0029 # LEFT PARENTHESIS
0029; 0028 # RIGHT PARENTHESIS
003C; 003E # LESS-THAN SIGN
003E; 003C # GREATER-THAN SIGN
005B; 005D # LEFT SQUARE BRACKET
005D; 005B # RIGHT SQUARE BRACKET
007B; 007D # LEFT CURLY BRACKET
007D; 007B # RIGHT CURLY BRACKET
00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
*/
switch (c) {
case '(':
return ')';
case ')':
return '(';
case '<':
return '>';
case '>':
return '<';
case ']':
return '[';
case '[':
return ']';
case '}':
return '{';
case '{':
return '}';
case '\u00AB':
return '\u00BB';
case '\u00BB':
return '\u00AB';
default:
return c;
}
}
return (function bidi(text, startLevel) {
var str = text.str;
var strLength = str.length;
if (strLength == 0)
return str;
// get types, fill arrays
var chars = new Array(strLength);
var types = new Array(strLength);
var oldtypes = new Array(strLength);
var numBidi = 0;
for (var i = 0; i < strLength; ++i) {
chars[i] = str.charAt(i);
var charCode = str.charCodeAt(i);
var charType = 'L';
if (charCode <= 0x00ff)
charType = baseTypes[charCode];
else if (0x0590 <= charCode && charCode <= 0x05f4)
charType = 'R';
else if (0x0600 <= charCode && charCode <= 0x06ff)
charType = arabicTypes[charCode & 0xff];
else if (0x0700 <= charCode && charCode <= 0x08AC)
charType = 'AL';
if (charType == 'R' || charType == 'AL' || charType == 'AN')
numBidi++;
oldtypes[i] = types[i] = charType;
}
// detect the bidi method
// if there are no rtl characters then no bidi needed
// if less than 30% chars are rtl then string is primarily ltr
// if more than 30% chars are rtl then string is primarily rtl
if (numBidi == 0) {
text.direction = 'ltr';
return str;
}
if (startLevel == -1) {
if ((strLength / numBidi) < 0.3) {
text.direction = 'ltr';
startLevel = 0;
} else {
text.direction = 'rtl';
startLevel = 1;
}
}
var levels = new Array(strLength);
for (var i = 0; i < strLength; ++i) {
levels[i] = startLevel;
}
var diffChars = new Array(strLength);
var diffLevels = new Array(strLength);
var diffTypes = new Array(strLength);
/*
X1-X10: skip most of this, since we are NOT doing the embeddings.
*/
var e = isOdd(startLevel) ? 'R' : 'L';
var sor = e;
var eor = sor;
/*
W1. Examine each non-spacing mark (NSM) in the level run, and change the
type of the NSM to the type of the previous character. If the NSM is at the
start of the level run, it will get the type of sor.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'NSM')
types[i] = lastType;
else
lastType = types[i];
}
/*
W2. Search backwards from each instance of a European number until the
first strong type (R, L, AL, or sor) is found. If an AL is found, change
the type of the European number to Arabic number.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'AL') ? 'AN' : 'EN';
else if (t == 'R' || t == 'L' || t == 'AL')
lastType = t;
}
/*
W3. Change all ALs to R.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'AL')
types[i] = 'R';
}
/*
W4. A single European separator between two European numbers changes to a
European number. A single common separator between two numbers of the same
type changes to that type:
*/
for (var i = 1; i < strLength - 1; ++i) {
if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN')
types[i] = 'EN';
if (types[i] == 'CS' && (types[i - 1] == 'EN' || types[i - 1] == 'AN') &&
types[i + 1] == types[i - 1])
types[i] = types[i - 1];
}
/*
W5. A sequence of European terminators adjacent to European numbers changes
to all European numbers:
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'EN') {
// do before
for (var j = i - 1; j >= 0; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
// do after
for (var j = i + 1; j < strLength; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
}
}
/*
W6. Otherwise, separators and terminators change to Other Neutral:
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS')
types[i] = 'ON';
}
/*
W7. Search backwards from each instance of a European number until the
first strong type (R, L, or sor) is found. If an L is found, then change
the type of the European number to L.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'L') ? 'L' : 'EN';
else if (t == 'R' || t == 'L')
lastType = t;
}
/*
N1. A sequence of neutrals takes the direction of the surrounding strong
text if the text on both sides has the same direction. European and Arabic
numbers are treated as though they were R. Start-of-level-run (sor) and
end-of-level-run (eor) are used at level run boundaries.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON') {
var end = findUnequal(types, i + 1, 'ON');
var before = sor;
if (i > 0)
before = types[i - 1];
var after = eor;
if (end + 1 < strLength)
after = types[end + 1];
if (before != 'L')
before = 'R';
if (after != 'L')
after = 'R';
if (before == after)
setValues(types, i, end, before);
i = end - 1; // reset to end (-1 so next iteration is ok)
}
}
/*
N2. Any remaining neutrals take the embedding direction.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON')
types[i] = e;
}
/*
I1. For all characters with an even (left-to-right) embedding direction,
those of type R go up one level and those of type AN or EN go up two
levels.
I2. For all characters with an odd (right-to-left) embedding direction,
those of type L, EN or AN go up one level.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (isEven(levels[i])) {
if (t == 'R') {
levels[i] += 1;
} else if (t == 'AN' || t == 'EN') {
levels[i] += 2;
}
} else { // isOdd, so
if (t == 'L' || t == 'AN' || t == 'EN') {
levels[i] += 1;
}
}
}
/*
L1. On each line, reset the embedding level of the following characters to
the paragraph embedding level:
segment separators,
paragraph separators,
any sequence of whitespace characters preceding a segment separator or
paragraph separator, and any sequence of white space characters at the end
of the line.
*/
// don't bother as text is only single line
/*
L2. From the highest level found in the text to the lowest odd level on
each line, reverse any contiguous sequence of characters that are at that
level or higher.
*/
// find highest level & lowest odd level
var highestLevel = -1;
var lowestOddLevel = 99;
for (var i = 0, ii = levels.length; i < ii; ++i) {
var level = levels[i];
if (highestLevel < level)
highestLevel = level;
if (lowestOddLevel > level && isOdd(level))
lowestOddLevel = level;
}
// now reverse between those limits
for (var level = highestLevel; level >= lowestOddLevel; --level) {
// find segments to reverse
var start = -1;
for (var i = 0, ii = levels.length; i < ii; ++i) {
if (levels[i] < level) {
if (start >= 0) {
reverseValues(chars, start, i);
start = -1;
}
} else if (start < 0) {
start = i;
}
}
if (start >= 0) {
reverseValues(chars, start, levels.length);
}
}
/*
L3. Combining marks applied to a right-to-left base character will at this
point precede their base character. If the rendering engine expects them to
follow the base characters in the final display process, then the ordering
of the marks and the base character must be reversed.
*/
// don't bother for now
/*
L4. A character that possesses the mirrored property as specified by
Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
directionality of that character is R.
*/
// don't mirror as characters are already mirrored in the pdf
// Finally, return string
var result = '';
for (var i = 0, ii = chars.length; i < ii; ++i) {
var ch = chars[i];
if (ch != '<' && ch != '>')
result += ch;
}
return result;
});
})();
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
var bidi = PDFJS.bidi = (function bidiClosure() {
// Character types for symbols from 0000 to 00FF.
var baseTypes = [
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
];
// Character types for symbols from 0600 to 06FF
var arabicTypes = [
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
];
function isOdd(i) {
return (i & 1) != 0;
}
function isEven(i) {
return (i & 1) == 0;
}
function findUnequal(arr, start, value) {
var j;
for (var j = start, jj = arr.length; j < jj; ++j) {
if (arr[j] != value)
return j;
}
return j;
}
function setValues(arr, start, end, value) {
for (var j = start; j < end; ++j) {
arr[j] = value;
}
}
function reverseValues(arr, start, end) {
for (var i = start, j = end - 1; i < j; ++i, --j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
function mirrorGlyphs(c) {
/*
# BidiMirroring-1.txt
0028; 0029 # LEFT PARENTHESIS
0029; 0028 # RIGHT PARENTHESIS
003C; 003E # LESS-THAN SIGN
003E; 003C # GREATER-THAN SIGN
005B; 005D # LEFT SQUARE BRACKET
005D; 005B # RIGHT SQUARE BRACKET
007B; 007D # LEFT CURLY BRACKET
007D; 007B # RIGHT CURLY BRACKET
00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
*/
switch (c) {
case '(':
return ')';
case ')':
return '(';
case '<':
return '>';
case '>':
return '<';
case ']':
return '[';
case '[':
return ']';
case '}':
return '{';
case '{':
return '}';
case '\u00AB':
return '\u00BB';
case '\u00BB':
return '\u00AB';
default:
return c;
}
}
function bidi(text, startLevel) {
var str = text.str;
var strLength = str.length;
if (strLength == 0)
return str;
// get types, fill arrays
var chars = new Array(strLength);
var types = new Array(strLength);
var oldtypes = new Array(strLength);
var numBidi = 0;
for (var i = 0; i < strLength; ++i) {
chars[i] = str.charAt(i);
var charCode = str.charCodeAt(i);
var charType = 'L';
if (charCode <= 0x00ff)
charType = baseTypes[charCode];
else if (0x0590 <= charCode && charCode <= 0x05f4)
charType = 'R';
else if (0x0600 <= charCode && charCode <= 0x06ff)
charType = arabicTypes[charCode & 0xff];
else if (0x0700 <= charCode && charCode <= 0x08AC)
charType = 'AL';
if (charType == 'R' || charType == 'AL' || charType == 'AN')
numBidi++;
oldtypes[i] = types[i] = charType;
}
// detect the bidi method
// if there are no rtl characters then no bidi needed
// if less than 30% chars are rtl then string is primarily ltr
// if more than 30% chars are rtl then string is primarily rtl
if (numBidi == 0) {
text.direction = 'ltr';
return str;
}
if (startLevel == -1) {
if ((strLength / numBidi) < 0.3) {
text.direction = 'ltr';
startLevel = 0;
} else {
text.direction = 'rtl';
startLevel = 1;
}
}
var levels = new Array(strLength);
for (var i = 0; i < strLength; ++i) {
levels[i] = startLevel;
}
var diffChars = new Array(strLength);
var diffLevels = new Array(strLength);
var diffTypes = new Array(strLength);
/*
X1-X10: skip most of this, since we are NOT doing the embeddings.
*/
var e = isOdd(startLevel) ? 'R' : 'L';
var sor = e;
var eor = sor;
/*
W1. Examine each non-spacing mark (NSM) in the level run, and change the
type of the NSM to the type of the previous character. If the NSM is at the
start of the level run, it will get the type of sor.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'NSM')
types[i] = lastType;
else
lastType = types[i];
}
/*
W2. Search backwards from each instance of a European number until the
first strong type (R, L, AL, or sor) is found. If an AL is found, change
the type of the European number to Arabic number.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'AL') ? 'AN' : 'EN';
else if (t == 'R' || t == 'L' || t == 'AL')
lastType = t;
}
/*
W3. Change all ALs to R.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'AL')
types[i] = 'R';
}
/*
W4. A single European separator between two European numbers changes to a
European number. A single common separator between two numbers of the same
type changes to that type:
*/
for (var i = 1; i < strLength - 1; ++i) {
if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN')
types[i] = 'EN';
if (types[i] == 'CS' && (types[i - 1] == 'EN' || types[i - 1] == 'AN') &&
types[i + 1] == types[i - 1])
types[i] = types[i - 1];
}
/*
W5. A sequence of European terminators adjacent to European numbers changes
to all European numbers:
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'EN') {
// do before
for (var j = i - 1; j >= 0; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
// do after
for (var j = i + 1; j < strLength; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
}
}
/*
W6. Otherwise, separators and terminators change to Other Neutral:
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS')
types[i] = 'ON';
}
/*
W7. Search backwards from each instance of a European number until the
first strong type (R, L, or sor) is found. If an L is found, then change
the type of the European number to L.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'L') ? 'L' : 'EN';
else if (t == 'R' || t == 'L')
lastType = t;
}
/*
N1. A sequence of neutrals takes the direction of the surrounding strong
text if the text on both sides has the same direction. European and Arabic
numbers are treated as though they were R. Start-of-level-run (sor) and
end-of-level-run (eor) are used at level run boundaries.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON') {
var end = findUnequal(types, i + 1, 'ON');
var before = sor;
if (i > 0)
before = types[i - 1];
var after = eor;
if (end + 1 < strLength)
after = types[end + 1];
if (before != 'L')
before = 'R';
if (after != 'L')
after = 'R';
if (before == after)
setValues(types, i, end, before);
i = end - 1; // reset to end (-1 so next iteration is ok)
}
}
/*
N2. Any remaining neutrals take the embedding direction.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON')
types[i] = e;
}
/*
I1. For all characters with an even (left-to-right) embedding direction,
those of type R go up one level and those of type AN or EN go up two
levels.
I2. For all characters with an odd (right-to-left) embedding direction,
those of type L, EN or AN go up one level.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (isEven(levels[i])) {
if (t == 'R') {
levels[i] += 1;
} else if (t == 'AN' || t == 'EN') {
levels[i] += 2;
}
} else { // isOdd, so
if (t == 'L' || t == 'AN' || t == 'EN') {
levels[i] += 1;
}
}
}
/*
L1. On each line, reset the embedding level of the following characters to
the paragraph embedding level:
segment separators,
paragraph separators,
any sequence of whitespace characters preceding a segment separator or
paragraph separator, and any sequence of white space characters at the end
of the line.
*/
// don't bother as text is only single line
/*
L2. From the highest level found in the text to the lowest odd level on
each line, reverse any contiguous sequence of characters that are at that
level or higher.
*/
// find highest level & lowest odd level
var highestLevel = -1;
var lowestOddLevel = 99;
for (var i = 0, ii = levels.length; i < ii; ++i) {
var level = levels[i];
if (highestLevel < level)
highestLevel = level;
if (lowestOddLevel > level && isOdd(level))
lowestOddLevel = level;
}
// now reverse between those limits
for (var level = highestLevel; level >= lowestOddLevel; --level) {
// find segments to reverse
var start = -1;
for (var i = 0, ii = levels.length; i < ii; ++i) {
if (levels[i] < level) {
if (start >= 0) {
reverseValues(chars, start, i);
start = -1;
}
} else if (start < 0) {
start = i;
}
}
if (start >= 0) {
reverseValues(chars, start, levels.length);
}
}
/*
L3. Combining marks applied to a right-to-left base character will at this
point precede their base character. If the rendering engine expects them to
follow the base characters in the final display process, then the ordering
of the marks and the base character must be reversed.
*/
// don't bother for now
/*
L4. A character that possesses the mirrored property as specified by
Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
directionality of that character is R.
*/
// don't mirror as characters are already mirrored in the pdf
// Finally, return string
var result = '';
for (var i = 0, ii = chars.length; i < ii; ++i) {
var ch = chars[i];
if (ch != '<' && ch != '>')
result += ch;
}
return result;
}
return bidi;
})();

View File

@ -70,7 +70,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() {
return CanvasExtraState;
})();
function ScratchCanvas(width, height) {
function createScratchCanvas(width, height) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
@ -188,9 +188,9 @@ function addContextCurrentTransform(ctx) {
}
var CanvasGraphics = (function CanvasGraphicsClosure() {
// Defines the time the executeIRQueue is going to be executing
// Defines the time the executeOperatorList is going to be executing
// before it stops and shedules a continue of execution.
var kExecutionTime = 50;
var kExecutionTime = 15;
function CanvasGraphics(canvasCtx, objs, textLayer) {
this.ctx = canvasCtx;
@ -199,7 +199,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.pendingClip = null;
this.res = null;
this.xobjs = null;
this.ScratchCanvas = ScratchCanvas;
this.objs = objs;
this.textLayer = textLayer;
if (canvasCtx) {
@ -229,7 +228,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
'setStrokeColor': true,
'setStrokeColorN': true,
'setFillColor': true,
'setFillColorN_IR': true,
'setFillColorN': true,
'setStrokeGray': true,
'setFillGray': true,
'setStrokeRGBColor': true,
@ -268,15 +267,16 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.textLayer.beginLayout();
},
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
executionStartIdx, continueCallback,
stepper) {
var argsArray = codeIR.argsArray;
var fnArray = codeIR.fnArray;
executeOperatorList: function canvasGraphicsExecuteOperatorList(
operatorList,
executionStartIdx, continueCallback,
stepper) {
var argsArray = operatorList.argsArray;
var fnArray = operatorList.fnArray;
var i = executionStartIdx || 0;
var argsArrayLen = argsArray.length;
// Sometimes the IRQueue to execute is empty.
// Sometimes the OperatorList to execute is empty.
if (argsArrayLen == i) {
return i;
}
@ -314,7 +314,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
i++;
// If the entire IRQueue was executed, stop as were done.
// If the entire operatorList was executed, stop as were done.
if (i == argsArrayLen) {
return i;
}
@ -327,8 +327,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
return i;
}
// If the IRQueue isn't executed completly yet OR the execution time
// was short enough, do another execution round.
// If the operatorList isn't executed completely yet OR the execution
// time was short enough, do another execution round.
}
},
@ -556,7 +556,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.current.leading = -leading;
},
setFont: function canvasGraphicsSetFont(fontRefName, size) {
var fontObj = this.objs.get(fontRefName).fontObj;
var fontObj = this.objs.get(fontRefName);
var current = this.current;
if (!fontObj)
@ -707,7 +707,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.save();
ctx.scale(fontSize, fontSize);
ctx.transform.apply(ctx, fontMatrix);
this.executeIRQueue(glyph.codeIRQueue);
this.executeOperatorList(glyph.operatorList);
this.restore();
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
@ -908,7 +908,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.ctx.strokeStyle = color;
this.current.strokeColor = color;
},
getColorN_IR_Pattern: function canvasGraphicsGetColorN_IR_Pattern(IR, cs) {
getColorN_Pattern: function canvasGraphicsGetColorN_Pattern(IR, cs) {
if (IR[0] == 'TilingPattern') {
var args = IR[1];
var base = cs.base;
@ -930,11 +930,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
}
return pattern;
},
setStrokeColorN_IR: function canvasGraphicsSetStrokeColorN(/*...*/) {
setStrokeColorN: function canvasGraphicsSetStrokeColorN(/*...*/) {
var cs = this.current.strokeColorSpace;
if (cs.name == 'Pattern') {
this.current.strokeColor = this.getColorN_IR_Pattern(arguments, cs);
this.current.strokeColor = this.getColorN_Pattern(arguments, cs);
} else {
this.setStrokeColor.apply(this, arguments);
}
@ -946,11 +946,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.ctx.fillStyle = color;
this.current.fillColor = color;
},
setFillColorN_IR: function canvasGraphicsSetFillColorN(/*...*/) {
setFillColorN: function canvasGraphicsSetFillColorN(/*...*/) {
var cs = this.current.fillColorSpace;
if (cs.name == 'Pattern') {
this.current.fillColor = this.getColorN_IR_Pattern(arguments, cs);
this.current.fillColor = this.getColorN_Pattern(arguments, cs);
} else {
this.setFillColor.apply(this, arguments);
}
@ -1116,7 +1116,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
// scale the image to the unit square
ctx.scale(1 / w, -1 / h);
var tmpCanvas = new this.ScratchCanvas(w, h);
var tmpCanvas = createScratchCanvas(w, h);
var tmpCtx = tmpCanvas.getContext('2d');
var fillColor = this.current.fillColor;
@ -1147,7 +1147,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
// scale the image to the unit square
ctx.scale(1 / w, -1 / h);
var tmpCanvas = new this.ScratchCanvas(w, h);
var tmpCanvas = createScratchCanvas(w, h);
var tmpCtx = tmpCanvas.getContext('2d');
this.putBinaryImageData(tmpCtx, imgData, w, h);

View File

@ -135,6 +135,7 @@ var ColorSpace = (function ColorSpaceClosure() {
basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
return ['PatternCS', basePatternCS];
case 'Indexed':
case 'I':
var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
var hiVal = cs[2] + 1;
var lookup = xref.fetchIfRef(cs[3]);

View File

@ -63,13 +63,8 @@ var Page = (function PageClosure() {
function Page(xref, pageNumber, pageDict, ref) {
this.pageNumber = pageNumber;
this.pageDict = pageDict;
this.stats = {
create: Date.now(),
compile: 0.0,
fonts: 0.0,
images: 0.0,
render: 0.0
};
this.stats = new StatTimer();
this.stats.enabled = !!globalScope.PDFJS.enableStats;
this.xref = xref;
this.ref = ref;
@ -175,10 +170,10 @@ var Page = (function PageClosure() {
return shadow(this, 'rotate', rotate);
},
startRenderingFromIRQueue: function pageStartRenderingFromIRQueue(
IRQueue, fonts) {
startRenderingFromOperatorList: function pageStartRenderingFromOperatorList(
operatorList, fonts) {
var self = this;
this.IRQueue = IRQueue;
this.operatorList = operatorList;
var displayContinuation = function pageDisplayContinuation() {
// Always defer call to display() to work around bug in
@ -189,17 +184,20 @@ var Page = (function PageClosure() {
};
this.ensureFonts(fonts,
function pageStartRenderingFromIRQueueEnsureFonts() {
displayContinuation();
});
function pageStartRenderingFromOperatorListEnsureFonts() {
displayContinuation();
}
);
},
getIRQueue: function pageGetIRQueue(handler, dependency) {
if (this.IRQueue) {
getOperatorList: function pageGetOperatorList(handler, dependency) {
if (this.operatorList) {
// content was compiled
return this.IRQueue;
return this.operatorList;
}
this.stats.time('Build IR Queue');
var xref = this.xref;
var content = xref.fetchIfRef(this.content);
var resources = xref.fetchIfRef(this.resources);
@ -216,30 +214,33 @@ var Page = (function PageClosure() {
var pe = this.pe = new PartialEvaluator(
xref, handler, 'p' + this.pageNumber + '_');
var IRQueue = {};
return (this.IRQueue = pe.getIRQueue(content, resources, IRQueue,
dependency));
this.operatorList = pe.getOperatorList(content, resources, dependency);
this.stats.timeEnd('Build IR Queue');
return this.operatorList;
},
ensureFonts: function pageEnsureFonts(fonts, callback) {
this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj.
for (var i = 0, ii = fonts.length; i < ii; i++) {
fonts[i] = this.objs.objs[fonts[i]].data;
}
// Load all the fonts
var fontObjs = FontLoader.bind(
FontLoader.bind(
fonts,
function pageEnsureFontsFontObjs(fontObjs) {
this.stats.fonts = Date.now();
this.stats.timeEnd('Font Loading');
callback.call(this);
}.bind(this),
this.objs
}.bind(this)
);
},
display: function pageDisplay(gfx, callback) {
var stats = this.stats;
stats.time('Rendering');
var xref = this.xref;
var resources = xref.fetchIfRef(this.resources);
var mediaBox = xref.fetchIfRef(this.mediaBox);
@ -253,21 +254,23 @@ var Page = (function PageClosure() {
rotate: this.rotate });
var startIdx = 0;
var length = this.IRQueue.fnArray.length;
var IRQueue = this.IRQueue;
var length = this.operatorList.fnArray.length;
var operatorList = this.operatorList;
var stepper = null;
if (PDFJS.pdfBug && StepperManager.enabled) {
stepper = StepperManager.create(this.pageNumber);
stepper.init(IRQueue);
stepper.init(operatorList);
stepper.nextBreakPoint = stepper.getNextBreakPoint();
}
var self = this;
function next() {
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next, stepper);
startIdx =
gfx.executeOperatorList(operatorList, startIdx, next, stepper);
if (startIdx == length) {
self.stats.render = Date.now();
gfx.endDrawing();
stats.timeEnd('Rendering');
stats.timeEnd('Overall');
if (callback) callback();
}
}
@ -310,6 +313,22 @@ var Page = (function PageClosure() {
return null;
return item.get(name);
}
function isValidUrl(url) {
if (!url)
return false;
var colon = url.indexOf(':');
if (colon < 0)
return false;
var protocol = url.substr(0, colon);
switch (protocol) {
case 'http':
case 'https':
case 'ftp':
return true;
default:
return false;
}
}
var annotations = xref.fetchIfRef(this.annotations) || [];
var i, n = annotations.length;
@ -338,7 +357,12 @@ var Page = (function PageClosure() {
if (a) {
switch (a.get('S').name) {
case 'URI':
item.url = a.get('URI');
var url = a.get('URI');
// TODO: pdf spec mentions urls can be relative to a Base
// entry in the dictionary.
if (!isValidUrl(url))
url = '';
item.url = url;
break;
case 'GoTo':
item.dest = a.get('D');
@ -410,16 +434,16 @@ var Page = (function PageClosure() {
return items;
},
startRendering: function pageStartRendering(ctx, callback, textLayer) {
this.startRenderingTime = Date.now();
// If there is no displayReadyPromise yet, then the IRQueue was never
var stats = this.stats;
stats.time('Overall');
// If there is no displayReadyPromise yet, then the operatorList was never
// requested before. Make the request and create the promise.
if (!this.displayReadyPromise) {
this.pdf.startRendering(this);
this.displayReadyPromise = new Promise();
}
// Once the IRQueue and fonts are loaded, perform the actual rendering.
// Once the operatorList and fonts are loaded, do the actual rendering.
this.displayReadyPromise.then(
function pageDisplayReadyPromise() {
var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
@ -451,9 +475,6 @@ var Page = (function PageClosure() {
* Right now there exists one PDFDocModel on the main thread + one object
* for each worker. If there is no worker support enabled, there are two
* `PDFDocModel` objects on the main thread created.
* TODO: Refactor the internal object structure, such that there is no
* need for the `PDFDocModel` anymore and there is only one object on the
* main thread and not one entire copy on each worker instance.
*/
var PDFDocModel = (function PDFDocModelClosure() {
function PDFDocModel(arg, callback) {
@ -622,9 +643,9 @@ var PDFDoc = (function PDFDocClosure() {
this.data = data;
this.stream = stream;
this.pdf = new PDFDocModel(stream);
this.fingerprint = this.pdf.getFingerprint();
this.catalog = this.pdf.catalog;
this.pdfModel = new PDFDocModel(stream);
this.fingerprint = this.pdfModel.getFingerprint();
this.catalog = this.pdfModel.catalog;
this.objs = new PDFObjects();
this.pageCache = [];
@ -711,7 +732,8 @@ var PDFDoc = (function PDFDocClosure() {
var page = this.pageCache[pageNum];
var depFonts = data.depFonts;
page.startRenderingFromIRQueue(data.IRQueue, depFonts);
page.stats.timeEnd('Page Request');
page.startRenderingFromOperatorList(data.operatorList, depFonts);
}, this);
messageHandler.on('obj', function pdfDocObj(data) {
@ -738,31 +760,16 @@ var PDFDoc = (function PDFDocClosure() {
file = new Stream(file, 0, file.length, fontFileDict);
}
// For now, resolve the font object here direclty. The real font
// object is then created in FontLoader.bind().
this.objs.resolve(id, {
name: name,
file: file,
properties: properties
});
// At this point, only the font object is created but the font is
// not yet attached to the DOM. This is done in `FontLoader.bind`.
var font = new Font(name, file, properties);
this.objs.resolve(id, font);
break;
default:
error('Got unkown object type ' + type);
}
}, this);
messageHandler.on('font_ready', function pdfDocFontReady(data) {
var id = data[0];
var font = new FontShape(data[1]);
// If there is no string, then there is nothing to attach to the DOM.
if (!font.str) {
this.objs.resolve(id, font);
} else {
this.objs.setData(id, font);
}
}.bind(this));
messageHandler.on('page_error', function pdfDocError(data) {
var page = this.pageCache[data.pageNum];
if (page.displayReadyPromise)
@ -784,7 +791,7 @@ var PDFDoc = (function PDFDocClosure() {
var size = width * height;
var rgbaLength = size * 4;
var buf = new Uint8Array(size * components);
var tmpCanvas = new ScratchCanvas(width, height);
var tmpCanvas = createScratchCanvas(width, height);
var tmpCtx = tmpCanvas.getContext('2d');
tmpCtx.drawImage(img, 0, 0);
var data = tmpCtx.getImageData(0, 0, width, height).data;
@ -813,12 +820,13 @@ var PDFDoc = (function PDFDocClosure() {
},
get numPages() {
return this.pdf.numPages;
return this.pdfModel.numPages;
},
startRendering: function pdfDocStartRendering(page) {
// The worker might not be ready to receive the page request yet.
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
page.stats.time('Page Request');
this.messageHandler.send('page_request', page.pageNumber + 1);
}.bind(this));
},
@ -827,7 +835,7 @@ var PDFDoc = (function PDFDocClosure() {
if (this.pageCache[n])
return this.pageCache[n];
var page = this.pdf.getPage(n);
var page = this.pdfModel.getPage(n);
// Add a reference to the objects such that Page can forward the reference
// to the CanvasGraphics and so on.
page.objs = this.objs;

View File

@ -570,7 +570,6 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
};
}
error('Unknown crypto method');
return null;
}
CipherTransformFactory.prototype = {

View File

@ -112,8 +112,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
};
PartialEvaluator.prototype = {
getIRQueue: function partialEvaluatorGetIRQueue(stream, resources,
queue, dependency) {
getOperatorList: function partialEvaluatorGetOperatorList(stream, resources,
dependency, queue) {
var self = this;
var xref = this.xref;
@ -136,8 +136,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var fontRes = resources.get('Font');
// TODO: TOASK: Is it possible to get here? If so, what does
// args[0].name should be like???
assert(fontRes, 'fontRes not available');
fontRes = xref.fetchIfRef(fontRes);
@ -177,7 +175,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// Ensure the font is ready before the font is set
// and later on used for drawing.
// TODO: This should get insert to the IRQueue only once per
// OPTIMIZE: This should get insert to the operatorList only once per
// page.
insertDependency([loadedName]);
return loadedName;
@ -239,6 +237,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}, handler, xref, resources, image, inline);
}
if (!queue)
queue = {};
if (!queue.argsArray) {
queue.argsArray = [];
}
@ -280,9 +281,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// TODO figure out how to type-check vararg functions
if ((cmd == 'SCN' || cmd == 'scn') && !args[args.length - 1].code) {
// Use the IR version for setStroke/FillColorN.
fn += '_IR';
// compile tiling patterns
var patternName = args[args.length - 1];
// SCN/scn applies patterns along with normal colors
@ -295,15 +293,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (typeNum == TILING_PATTERN) {
// Create an IR of the pattern code.
var depIdx = dependencyArray.length;
var queueObj = {};
var codeIR = this.getIRQueue(pattern, dict.get('Resources') ||
resources, queueObj, dependencyArray);
var operatorList = this.getOperatorList(pattern,
dict.get('Resources') || resources, dependencyArray);
// Add the dependencies that are required to execute the
// codeIR.
// operatorList.
insertDependency(dependencyArray.slice(depIdx));
args = TilingPattern.getIR(codeIR, dict, args);
args = TilingPattern.getIR(operatorList, dict, args);
}
else if (typeNum == SHADING_PATTERN) {
var shading = xref.fetchIfRef(dict.get('Shading'));
@ -337,14 +334,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
fnArray.push('paintFormXObjectBegin');
argsArray.push([matrix, bbox]);
// This adds the IRQueue of the xObj to the current queue.
// This adds the operatorList of the xObj to the current queue.
var depIdx = dependencyArray.length;
this.getIRQueue(xobj, xobj.dict.get('Resources') || resources,
queue, dependencyArray);
// Pass in the current `queue` object. That means the `fnArray`
// and the `argsArray` in this scope is reused and new commands
// are added to them.
this.getOperatorList(xobj,
xobj.dict.get('Resources') || resources,
dependencyArray, queue);
// Add the dependencies that are required to execute the
// codeIR.
// operatorList.
insertDependency(dependencyArray.slice(depIdx));
fn = 'paintFormXObjectEnd';
@ -454,10 +455,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}
}
return {
fnArray: fnArray,
argsArray: argsArray
};
return queue;
},
extractDataStructures: function
@ -855,12 +853,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var charProcs = xref.fetchIfRef(dict.get('CharProcs'));
var fontResources = xref.fetchIfRef(dict.get('Resources')) || resources;
properties.resources = fontResources;
properties.charProcIRQueues = {};
properties.charProcOperatorList = {};
for (var key in charProcs.map) {
var glyphStream = xref.fetchIfRef(charProcs.map[key]);
var queueObj = {};
properties.charProcIRQueues[key] =
this.getIRQueue(glyphStream, fontResources, queueObj, dependency);
properties.charProcOperatorList[key] =
this.getOperatorList(glyphStream, fontResources, dependency);
}
}

View File

@ -409,8 +409,8 @@ var FontLoader = {
bind: function fontLoaderBind(fonts, callback) {
function checkFontsLoaded() {
for (var i = 0, ii = objs.length; i < ii; i++) {
var fontObj = objs[i];
for (var i = 0, ii = fonts.length; i < ii; i++) {
var fontObj = fonts[i];
if (fontObj.loading) {
return false;
}
@ -423,52 +423,45 @@ var FontLoader = {
return true;
}
var rules = [], names = [], objs = [];
var rules = [], names = [], fontsToLoad = [];
var fontCreateTimer = 0;
for (var i = 0, ii = fonts.length; i < ii; i++) {
var font = fonts[i];
// If there is already a fontObj on the font, then it was loaded/attached
// to the page already and we don't have to do anything for this font
// here future.
if (font.fontObj) {
// Add the font to the DOM only once or skip if the font
// is already loaded.
if (font.attached || font.loading == false) {
continue;
}
font.attached = true;
var obj = new Font(font.name, font.file, font.properties);
// Store the fontObj on the font such that `setFont` in CanvasGraphics
// can reuse it later again.
font.fontObj = obj;
objs.push(obj);
fontsToLoad.push(font);
var str = '';
var data = obj.data;
var data = font.data;
if (data) {
var length = data.length;
for (var j = 0; j < length; j++)
str += String.fromCharCode(data[j]);
var rule = isWorker ? obj.bindWorker(str) : obj.bindDOM(str);
var rule = font.bindDOM(str);
if (rule) {
rules.push(rule);
names.push(obj.loadedName);
names.push(font.loadedName);
}
}
}
this.listeningForFontLoad = false;
if (!isWorker && rules.length) {
FontLoader.prepareFontLoadEvent(rules, names, objs);
FontLoader.prepareFontLoadEvent(rules, names, fontsToLoad);
}
if (!checkFontsLoaded()) {
document.documentElement.addEventListener(
'pdfjsFontLoad', checkFontsLoaded, false);
}
return objs;
},
// Set things up so that at least one pdfjsFontLoad event is
// dispatched when all the @font-face |rules| for |names| have been
@ -476,7 +469,7 @@ var FontLoader = {
// has already started in this (outer) document, so that they should
// be ordered before the load in the subdocument.
prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules, names,
objs) {
fonts) {
/** Hack begin */
// There's no event when a font has finished downloading so the
// following code is a dirty hack to 'guess' when a font is
@ -500,6 +493,15 @@ var FontLoader = {
// The postMessage() hackery was added to work around chrome bug
// 82402.
// Validate the names parameter -- the values can used to construct HTML.
if (!/^\w+$/.test(names.join(''))) {
error('Invalid font name(s): ' + names.join());
// Normally the error-function throws. But if a malicious code
// intercepts the function call then the return is needed.
return;
}
var div = document.createElement('div');
div.setAttribute('style',
'visibility: hidden;' +
@ -517,8 +519,8 @@ var FontLoader = {
'message',
function fontLoaderMessage(e) {
var fontNames = JSON.parse(e.data);
for (var i = 0, ii = objs.length; i < ii; ++i) {
var font = objs[i];
for (var i = 0, ii = fonts.length; i < ii; ++i) {
var font = fonts[i];
font.loading = false;
}
var evt = document.createEvent('Events');
@ -764,7 +766,7 @@ var Font = (function FontClosure() {
function Font(name, file, properties) {
this.name = name;
this.coded = properties.coded;
this.charProcIRQueues = properties.charProcIRQueues;
this.charProcOperatorList = properties.charProcOperatorList;
this.resources = properties.resources;
this.sizes = [];
@ -829,8 +831,6 @@ var Font = (function FontClosure() {
return;
}
this.loadedName = getUniqueName();
properties.id = this.loadedName;
var data;
switch (type) {
case 'Type1':
@ -864,6 +864,7 @@ var Font = (function FontClosure() {
this.widthMultiplier = !properties.fontMatrix ? 1.0 :
1.0 / properties.fontMatrix[0];
this.encoding = properties.baseEncoding;
this.loadedName = properties.loadedName;
this.loading = true;
};
@ -2273,17 +2274,6 @@ var Font = (function FontClosure() {
}
},
bindWorker: function font_bindWorker(data) {
postMessage({
action: 'font',
data: {
raw: data,
fontName: this.loadedName,
mimetype: this.mimetype
}
});
},
bindDOM: function font_bindDom(data) {
var fontName = this.loadedName;
@ -2337,7 +2327,7 @@ var Font = (function FontClosure() {
},
charToGlyph: function fonts_charToGlyph(charcode) {
var fontCharCode, width, codeIRQueue;
var fontCharCode, width, operatorList;
var width = this.widths[charcode];
@ -2372,7 +2362,7 @@ var Font = (function FontClosure() {
break;
case 'Type3':
var glyphName = this.differences[charcode] || this.encoding[charcode];
codeIRQueue = this.charProcIRQueues[glyphName];
operatorList = this.charProcOperatorList[glyphName];
fontCharCode = charcode;
break;
case 'TrueType':
@ -2415,7 +2405,7 @@ var Font = (function FontClosure() {
fontChar: String.fromCharCode(fontCharCode),
unicode: unicodeChars,
width: width,
codeIRQueue: codeIRQueue
operatorList: operatorList
};
},

View File

@ -134,6 +134,8 @@ var Catalog = (function CatalogClosure() {
while (queue.length > 0) {
var i = queue.shift();
var outlineDict = xref.fetch(i.obj);
if (outlineDict === null)
continue;
if (!outlineDict.has('Title'))
error('Invalid outline item');
var dest = outlineDict.get('A');
@ -512,7 +514,6 @@ var XRef = (function XRefClosure() {
return dict;
// nothing helps
error('Invalid PDF structure');
return null;
},
readXRef: function readXref(startXRef) {
var stream = this.stream;
@ -569,6 +570,8 @@ var XRef = (function XRefClosure() {
},
getEntry: function xRefGetEntry(i) {
var e = this.entries[i];
if (e === null)
return null;
return e.free ? null : e; // returns null is the entry is free
},
fetchIfRef: function xRefFetchIfRef(obj) {
@ -719,12 +722,10 @@ var PDFObjects = (function PDFObjectsClosure() {
// If there isn't an object yet or the object isn't resolved, then the
// data isn't ready yet!
if (!obj || !obj.isResolved) {
if (!obj || !obj.isResolved)
error('Requesting object that isn\'t resolved yet ' + objId);
return null;
} else {
return obj.data;
}
return obj.data;
},
/**

View File

@ -53,15 +53,14 @@ var Parser = (function ParserClosure() {
this.shift();
var dict = new Dict();
while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) {
if (!isName(this.buf1)) {
if (!isName(this.buf1))
error('Dictionary key must be a name object');
} else {
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
if (isEOF(this.buf1))
error('End of file inside dictionary');
@ -106,15 +105,14 @@ var Parser = (function ParserClosure() {
// parse dictionary
var dict = new Dict();
while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
if (!isName(this.buf1)) {
if (!isName(this.buf1))
error('Dictionary key must be a name object');
} else {
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
// parse image stream
@ -176,10 +174,8 @@ var Parser = (function ParserClosure() {
// get length
var length = this.fetchIfRef(dict.get('Length'));
if (!isInt(length)) {
if (!isInt(length))
error('Bad ' + length + ' attribute in stream');
length = 0;
}
// skip over the stream data
stream.pos = pos + length;
@ -208,14 +204,13 @@ var Parser = (function ParserClosure() {
filter = filterArray[i];
if (!isName(filter))
error('Bad filter name: ' + filter);
else {
params = null;
if (isArray(paramsArray) && (i in paramsArray))
params = paramsArray[i];
stream = this.makeFilter(stream, filter.name, length, params);
// after the first stream the length variable is invalid
length = null;
}
params = null;
if (isArray(paramsArray) && (i in paramsArray))
params = paramsArray[i];
stream = this.makeFilter(stream, filter.name, length, params);
// after the first stream the length variable is invalid
length = null;
}
}
return stream;
@ -527,17 +522,15 @@ var Lexer = (function LexerClosure() {
// fall through
case ')':
error('Illegal character: ' + ch);
return Error;
}
// command
var str = ch;
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
stream.skip();
if (str.length == 128) {
if (str.length == 128)
error('Command token too long: ' + str.length);
break;
}
str += ch;
}
if (str == 'true')
@ -594,7 +587,6 @@ var Linearization = (function LinearizationClosure() {
return obj;
}
error('"' + name + '" field in linearization table is invalid');
return 0;
},
getHint: function linearizationGetHint(index) {
var linDict = this.linDict;
@ -607,7 +599,6 @@ var Linearization = (function LinearizationClosure() {
return obj2;
}
error('Hints table in linearization table is invalid: ' + index);
return 0;
},
get length() {
if (!isDict(this.linDict))

View File

@ -82,7 +82,7 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
fnObj = xref.fetchIfRef(fnObj);
if (isArray(fnObj))
error('No support for array of functions');
else if (!isPDFFunction(fnObj))
if (!isPDFFunction(fnObj))
error('Invalid function');
var fn = PDFFunction.parse(xref, fnObj);
@ -190,7 +190,7 @@ var TilingPattern = (function TilingPatternClosure() {
var MAX_PATTERN_SIZE = 512;
function TilingPattern(IR, color, ctx, objs) {
var IRQueue = IR[2];
var operatorList = IR[2];
this.matrix = IR[3];
var bbox = IR[4];
var xstep = IR[5];
@ -222,7 +222,7 @@ var TilingPattern = (function TilingPatternClosure() {
width = height = MAX_PATTERN_SIZE;
}
var tmpCanvas = new ScratchCanvas(width, height);
var tmpCanvas = createScratchCanvas(width, height);
// set the new canvas element context as the graphics context
var tmpCtx = tmpCanvas.getContext('2d');
@ -259,12 +259,12 @@ var TilingPattern = (function TilingPatternClosure() {
graphics.endPath();
}
graphics.executeIRQueue(IRQueue);
graphics.executeOperatorList(operatorList);
this.canvas = tmpCanvas;
}
TilingPattern.getIR = function tiling_getIR(codeIR, dict, args) {
TilingPattern.getIR = function tiling_getIR(operatorList, dict, args) {
var matrix = dict.get('Matrix');
var bbox = dict.get('BBox');
var xstep = dict.get('XStep');
@ -272,7 +272,7 @@ var TilingPattern = (function TilingPatternClosure() {
var paintType = dict.get('PaintType');
return [
'TilingPattern', args, codeIR, matrix, bbox, xstep, ystep, paintType
'TilingPattern', args, operatorList, matrix, bbox, xstep, ystep, paintType
];
};

View File

@ -402,7 +402,7 @@ var Promise = (function PromiseClosure() {
if (this.isResolved) {
var data = this.data;
callback.call(null, data);
} else if (this.isRejected && errorback) {
} else if (this.isRejected && errback) {
var error = this.error;
errback.call(null, error);
} else {
@ -416,3 +416,55 @@ var Promise = (function PromiseClosure() {
return Promise;
})();
var StatTimer = (function StatTimerClosure() {
function rpad(str, pad, length) {
while (str.length < length)
str += pad;
return str;
}
function StatTimer() {
this.started = {};
this.times = [];
this.enabled = true;
}
StatTimer.prototype = {
time: function statTimerTime(name) {
if (!this.enabled)
return;
if (name in this.started)
throw 'Timer is already running for ' + name;
this.started[name] = Date.now();
},
timeEnd: function statTimerTimeEnd(name) {
if (!this.enabled)
return;
if (!(name in this.started))
throw 'Timer has not been started for ' + name;
this.times.push({
'name': name,
'start': this.started[name],
'end': Date.now()
});
// Remove timer from started so it can be called again.
delete this.started[name];
},
toString: function statTimerToString() {
var times = this.times;
var out = '';
// Find the longest name for padding purposes.
var longest = 0;
for (var i = 0, ii = times.length; i < ii; ++i) {
var name = times[i]['name'];
if (name.length > longest)
longest = name.length;
}
for (var i = 0, ii = times.length; i < ii; ++i) {
var span = times[i];
var duration = span.end - span.start;
out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
}
return out;
}
};
return StatTimer;
})();

View File

@ -79,7 +79,7 @@ MessageHandler.prototype = {
var WorkerMessageHandler = {
setup: function wphSetup(handler) {
var pdfDoc = null;
var pdfModel = null;
handler.on('test', function wphSetupTest(data) {
handler.send('test', data instanceof Uint8Array);
@ -88,7 +88,7 @@ var WorkerMessageHandler = {
handler.on('doc', function wphSetupDoc(data) {
// Create only the model of the PDFDoc, which is enough for
// processing the content of the pdf.
pdfDoc = new PDFDocModel(new Stream(data));
pdfModel = new PDFDocModel(new Stream(data));
});
handler.on('page_request', function wphSetupPageRequest(pageNum) {
@ -103,14 +103,14 @@ var WorkerMessageHandler = {
var start = Date.now();
var dependency = [];
var IRQueue = null;
var operatorList = null;
try {
var page = pdfDoc.getPage(pageNum);
var page = pdfModel.getPage(pageNum);
// Pre compile the pdf page and fetch the fonts/images.
IRQueue = page.getIRQueue(handler, dependency);
operatorList = page.getOperatorList(handler, dependency);
} catch (e) {
var minimumStackMessage =
'worker.js: while trying to getPage() and getIRQueue()';
'worker.js: while trying to getPage() and getOperatorList()';
// Turn the error into an obj that can be serialized
if (typeof e === 'string') {
@ -137,8 +137,8 @@ var WorkerMessageHandler = {
return;
}
console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum,
Date.now() - start, IRQueue.fnArray.length);
console.log('page=%d - getOperatorList: time=%dms, len=%d', pageNum,
Date.now() - start, operatorList.fnArray.length);
// Filter the dependecies for fonts.
var fonts = {};
@ -151,59 +151,10 @@ var WorkerMessageHandler = {
handler.send('page', {
pageNum: pageNum,
IRQueue: IRQueue,
operatorList: operatorList,
depFonts: Object.keys(fonts)
});
}, this);
handler.on('font', function wphSetupFont(data) {
var objId = data[0];
var name = data[1];
var file = data[2];
var properties = data[3];
var font = {
name: name,
file: file,
properties: properties
};
// Some fonts don't have a file, e.g. the build in ones like Arial.
if (file) {
var fontFileDict = new Dict();
fontFileDict.map = file.dict.map;
var fontFile = new Stream(file.bytes, file.start,
file.end - file.start, fontFileDict);
// Check if this is a FlateStream. Otherwise just use the created
// Stream one. This makes complex_ttf_font.pdf work.
var cmf = file.bytes[0];
if ((cmf & 0x0f) == 0x08) {
font.file = new FlateStream(fontFile);
} else {
font.file = fontFile;
}
}
var obj = new Font(font.name, font.file, font.properties);
var str = '';
var objData = obj.data;
if (objData) {
var length = objData.length;
for (var j = 0; j < length; ++j)
str += String.fromCharCode(objData[j]);
}
obj.str = str;
// Remove the data array form the font object, as it's not needed
// anymore as we sent over the ready str.
delete obj.data;
handler.send('font_ready', [objId, obj]);
});
}
};

View File

@ -20,6 +20,7 @@
!scan-bad.pdf
!freeculture.pdf
!pdfkit_compressed.pdf
!TAMReview.pdf
!issue918.pdf
!issue1249.pdf
!smaskdim.pdf

BIN
test/pdfs/TAMReview.pdf Normal file

Binary file not shown.

View File

@ -472,6 +472,14 @@
"rounds": 1,
"type": "eq"
},
{ "id": "tamreview",
"file": "pdfs/TAMReview.pdf",
"md5": "8039aba56790d3597d2bc8c794a51301",
"rounds": 1,
"pageLimit": 5,
"link": true,
"type": "eq"
},
{ "id": "issue925",
"file": "pdfs/issue925.pdf",
"md5": "f58fe943090aff89dcc8e771bc0db4c2",

View File

@ -318,6 +318,58 @@ var Stepper = (function StepperClosure() {
return Stepper;
})();
var Stats = (function Stats() {
var stats = [];
function clear(node) {
while (node.hasChildNodes())
node.removeChild(node.lastChild);
}
function getStatIndex(pageNumber) {
for (var i = 0, ii = stats.length; i < ii; ++i)
if (stats[i].pageNumber === pageNumber)
return i;
return false;
}
return {
// Poperties/functions needed by PDFBug.
id: 'Stats',
name: 'Stats',
panel: null,
manager: null,
init: function init() {
this.panel.setAttribute('style', 'padding: 5px;');
PDFJS.enableStats = true;
},
enabled: false,
active: false,
// Stats specific functions.
add: function(pageNumber, stat) {
if (!stat)
return;
var statsIndex = getStatIndex(pageNumber);
if (statsIndex !== false) {
var b = stats[statsIndex];
this.panel.removeChild(b.div);
stats.splice(statsIndex, 1);
}
var wrapper = document.createElement('div');
wrapper.className = 'stats';
var title = document.createElement('div');
title.className = 'title';
title.textContent = 'Page: ' + pageNumber;
var statsDiv = document.createElement('div');
statsDiv.textContent = stat.toString();
wrapper.appendChild(title);
wrapper.appendChild(statsDiv);
stats.push({ pageNumber: pageNumber, div: wrapper });
stats.sort(function(a, b) { return a.pageNumber - b.pageNumber});
clear(this.panel);
for (var i = 0, ii = stats.length; i < ii; ++i)
this.panel.appendChild(stats[i].div);
}
};
})();
// Manages all the debugging tools.
var PDFBug = (function PDFBugClosure() {
var panelWidth = 300;
@ -327,8 +379,29 @@ var PDFBug = (function PDFBugClosure() {
return {
tools: [
FontInspector,
StepperManager
StepperManager,
Stats
],
enable: function(ids) {
var all = false, tools = this.tools;
if (ids.length === 1 && ids[0] === 'all')
all = true;
for (var i = 0; i < tools.length; ++i) {
var tool = tools[i];
if (all || ids.indexOf(tool.id) !== -1)
tool.enabled = true;
}
if (!all) {
// Sort the tools by the order they are enabled.
tools.sort(function(a, b) {
var indexA = ids.indexOf(a.id);
indexA = indexA < 0 ? tools.length : indexA;
var indexB = ids.indexOf(b.id);
indexB = indexB < 0 ? tools.length : indexB;
return indexA - indexB;
});
}
},
init: function init() {
/*
* Basic Layout:

View File

@ -65,16 +65,6 @@ body {
line-height: 16px;
}
span#info {
display: none;
}
@-moz-document regexp("http:.*debug=1.*") {
span#info {
display: inline-block;
}
}
/* === Sidebar === */
#sidebar {
position: fixed;
@ -442,3 +432,11 @@ canvas {
background: yellow;
opacity: 0.3;
}
#PDFBug .stats {
font-size: 10px;
white-space: pre;
font-family: courier;
}
#PDFBug .stats .title {
font-weight: bold;
}

View File

@ -102,7 +102,6 @@
<img src="images/bookmark.svg" alt="Bookmark" align="top" height="16"/>
</a>
<span id="info">--</span>
</div>
<div id="errorWrapper" hidden='true'>
<div id="errorMessageLeft">

View File

@ -234,6 +234,7 @@ var PDFView = {
return;
}
pages[val - 1].updateStats();
currentPageNumber = val;
var event = document.createEvent('UIEvents');
event.initUIEvent('pagechange', false, false, window, 0);
@ -870,7 +871,6 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
// Rendering area
var self = this;
stats.begin = Date.now();
this.content.startRendering(ctx, function pageViewDrawCallback(error) {
if (self.loadingIconDiv) {
div.removeChild(self.loadingIconDiv);
@ -880,6 +880,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
if (error)
PDFView.error('An error occurred while rendering the page.', error);
self.stats = content.stats;
self.updateStats();
if (self.onAfterDraw)
self.onAfterDraw();
@ -893,10 +894,10 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
};
this.updateStats = function pageViewUpdateStats() {
var t1 = stats.compile, t2 = stats.fonts, t3 = stats.render;
var str = 'Time to compile/fonts/render: ' +
(t1 - stats.begin) + '/' + (t2 - t1) + '/' + (t3 - t2) + ' ms';
document.getElementById('info').textContent = str;
if (PDFJS.pdfBug && Stats.enabled) {
var stats = this.stats;
Stats.add(this.id, stats);
}
};
};
@ -1181,20 +1182,12 @@ window.addEventListener('load', function webViewerLoad(evt) {
if ('disableTextLayer' in hashParams)
PDFJS.disableTextLayer = (hashParams['disableTextLayer'] === 'true');
if ('pdfBug' in hashParams) {
if ('pdfBug' in hashParams &&
(!PDFJS.isFirefoxExtension || FirefoxCom.request('pdfBugEnabled'))) {
PDFJS.pdfBug = true;
var pdfBug = hashParams['pdfBug'];
var all = false, enabled = [];
if (pdfBug === 'all')
all = true;
else
enabled = pdfBug.split(',');
var debugTools = PDFBug.tools;
for (var i = 0; i < debugTools.length; ++i) {
var tool = debugTools[i];
if (all || enabled.indexOf(tool.id) !== -1)
tool.enabled = true;
}
var enabled = pdfBug.split(',');
PDFBug.enable(enabled);
PDFBug.init();
}