This commit is contained in:
oscar.plaisant@icloud.com
2023-10-23 23:30:51 +02:00
parent 95f9cde0c2
commit 717479f658
3433 changed files with 1756195 additions and 0 deletions

178
.obsidian/pdfjs/pdfextract/pdfjs/LICENSE vendored Normal file
View File

@@ -0,0 +1,178 @@
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

View File

@@ -0,0 +1,138 @@
# PDF.js
PDF.js is a Portable Document Format (PDF) viewer that is built with HTML5.
PDF.js is community-driven and supported by Mozilla Labs. Our goal is to
create a general-purpose, web standards-based platform for parsing and
rendering PDFs.
## Contributing
PDF.js is an open source project and always looking for more contributors. To
get involved checkout:
+ [Workflow](https://github.com/mozilla/pdf.js/wiki/Contributing)
+ [Style Guide](https://github.com/mozilla/pdf.js/wiki/Style-Guide)
+ [FAQ](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions)
+ [Good Beginner Bugs](https://github.com/mozilla/pdf.js/issues?direction=desc&labels=5-good-beginner-bug&page=1&sort=created&state=open)
+ [Priorities](https://github.com/mozilla/pdf.js/milestones)
+ [Attend a Public Meeting](https://github.com/mozilla/pdf.js/wiki/Weekly-Public-Meetings)
For further questions or guidance feel free to stop by #pdfjs on
irc.mozilla.org.
## Getting Started
### Online demo
+ http://mozilla.github.io/pdf.js/web/viewer.html
### Browser Extensions
#### Firefox
PDF.js is built into version 19+ of Firefox, however two extensions are still
available that are updated at a different rate:
+ [Development Version](http://mozilla.github.io/pdf.js/extensions/firefox/pdf.js.xpi) - This version is updated every time new code is merged into the PDF.js codebase. This should be quite stable but still might break from time to time.
+ [Stable Version](https://addons.mozilla.org/firefox/addon/pdfjs) - After version 24 of Firefox is released we no longer plan to support the stable extension. The stable version will then be considered whatever is built into Firefox.
#### Chrome and Opera
The Chromium extension is still somewhat experimental but it can be installed two
ways:
+ [Unofficial Version](https://chrome.google.com/webstore/detail/pdf-viewer/oemmndcbldboiebfnladdacbdfmadadm) - *This extension is maintained by a PDF.js contributor.*
+ Build Your Own - Get the code as explained below and issue `node make chromium`. Then open
Chrome, go to `Tools > Extension` and load the (unpackaged) extension from the
directory `build/chromium`.
The version of the extension for the Opera browser can be found at the [Opera add-ons catalog](https://addons.opera.com/en/extensions/details/pdf-viewer/).
## Getting the Code
To get a local copy of the current code, clone it using git:
$ git clone git://github.com/mozilla/pdf.js.git pdfjs
$ cd pdfjs
Next, you need to start a local web server as some browsers don't allow opening
PDF files for a file:// url:
$ node make server
You can install Node via [nvm](https://github.com/creationix/nvm) or the
[official package](http://nodejs.org). If everything worked out, you can now
serve
+ http://localhost:8888/web/viewer.html
You can also view all the test pdf files on the right side serving
+ http://localhost:8888/test/pdfs/?frame
## Building PDF.js
In order to bundle all `src/` files into two productions scripts and build the generic
viewer, issue:
$ node make generic
This will generate `pdf.js` and `pdf.worker.js` in the `build/generic/build/` directory.
Both scripts are needed but only `pdf.js` needs to be included since `pdf.worker.js` will
be loaded by `pdf.js`. If you want to support more browsers than Firefox you'll also need
to include `compatibility.js` from `build/generic/web/`. The PDF.js files are large and
should be minified for production.
## Learning
You can play with the PDF.js API directly from your browser through the live
demos below:
+ [Hello world](http://mozilla.github.io/pdf.js/examples/learning/helloworld.html)
+ [Simple reader with prev/next page controls](http://mozilla.github.io/pdf.js/examples/learning/prevnext.html)
The repo contains a hello world example that you can run locally:
+ [examples/helloworld/](https://github.com/mozilla/pdf.js/blob/master/examples/helloworld/)
For an introduction to the PDF.js code, check out the presentation by our
contributor Julian Viereck:
+ http://www.youtube.com/watch?v=Iv15UY-4Fg8
You can read more about PDF.js here:
+ http://andreasgal.com/2011/06/15/pdf-js/
+ http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/
Even more learning resources can be found at:
+ https://github.com/mozilla/pdf.js/wiki/Additional-Learning-Resources
## Questions
Check out our FAQs and get answers to common questions:
+ https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions
Talk to us on IRC:
+ #pdfjs on irc.mozilla.org
Join our mailing list:
+ dev-pdf-js@lists.mozilla.org
Subscribe either using lists.mozilla.org or Google Groups:
+ https://lists.mozilla.org/listinfo/dev-pdf-js
+ https://groups.google.com/group/mozilla.dev.pdf-js/topics
Follow us on twitter: @pdfjs
+ http://twitter.com/#!/pdfjs
Weekly Public Meetings
+ https://github.com/mozilla/pdf.js/wiki/Weekly-Public-Meetings

View File

@@ -0,0 +1,562 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals PDFJS, Util, isDict, isName, stringToPDFString, warn, Dict, Stream,
stringToBytes, assert, Promise, isArray, ObjectLoader, OperatorList,
isValidUrl, OPS, createPromiseCapability, AnnotationType */
'use strict';
var DEFAULT_ICON_SIZE = 22; // px
var SUPPORTED_TYPES = ['Link', 'Text', 'Widget'];
var Annotation = (function AnnotationClosure() {
// 12.5.5: Algorithm: Appearance streams
function getTransformMatrix(rect, bbox, matrix) {
var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix);
var minX = bounds[0];
var minY = bounds[1];
var maxX = bounds[2];
var maxY = bounds[3];
if (minX === maxX || minY === maxY) {
// From real-life file, bbox was [0, 0, 0, 0]. In this case,
// just apply the transform for rect
return [1, 0, 0, 1, rect[0], rect[1]];
}
var xRatio = (rect[2] - rect[0]) / (maxX - minX);
var yRatio = (rect[3] - rect[1]) / (maxY - minY);
return [
xRatio,
0,
0,
yRatio,
rect[0] - minX * xRatio,
rect[1] - minY * yRatio
];
}
function getDefaultAppearance(dict) {
var appearanceState = dict.get('AP');
if (!isDict(appearanceState)) {
return;
}
var appearance;
var appearances = appearanceState.get('N');
if (isDict(appearances)) {
var as = dict.get('AS');
if (as && appearances.has(as.name)) {
appearance = appearances.get(as.name);
}
} else {
appearance = appearances;
}
return appearance;
}
function Annotation(params) {
var dict = params.dict;
var data = this.data = {};
data.subtype = dict.get('Subtype').name;
var rect = dict.get('Rect') || [0, 0, 0, 0];
data.rect = Util.normalizeRect(rect);
data.annotationFlags = dict.get('F');
if (data.subtype=='Highlight') {
var content = dict.get('Contents');
data.content = stringToPDFString(content || '');
var title = dict.get('T');
data.title = stringToPDFString(title || '');
}
// annotation name
data.name = dict.get('NM');
// modification time
data.mtime = dict.get('M');
// get quad points for annotation
data.quadPoints = [];
var quadpts = dict.get('QuadPoints') || [];
for (var j = 0; j < quadpts.length; j += 8) {
// NB: we don't transform the quadpoints here, but later once we know
// the user space => device space transformation.
var topLeft = {x: quadpts[j + 4], y: quadpts[j + 5]};
var bottomRight = {x: quadpts[j + 2], y: quadpts[j + 3]};
var quad = {};
quad.x = Math.min(topLeft.x, bottomRight.x);
quad.y = Math.min(topLeft.y, bottomRight.y);
quad.width = Math.abs(topLeft.x - bottomRight.x);
quad.height = Math.abs(topLeft.y - bottomRight.y);
data.quadPoints.push(quad);
}
var color = dict.get('C');
if (isArray(color) && color.length === 3) {
// TODO(mack): currently only supporting rgb; need support different
// colorspaces
data.color = color;
} else {
data.color = [0, 0, 0];
}
// Some types of annotations have border style dict which has more
// info than the border array
if (dict.has('BS')) {
var borderStyle = dict.get('BS');
data.borderWidth = borderStyle.has('W') ? borderStyle.get('W') : 1;
} else {
var borderArray = dict.get('Border') || [0, 0, 1];
data.borderWidth = borderArray[2] || 0;
// TODO: implement proper support for annotations with line dash patterns.
var dashArray = borderArray[3];
if (data.borderWidth > 0 && dashArray) {
if (!isArray(dashArray)) {
// Ignore the border if dashArray is not actually an array,
// this is consistent with the behaviour in Adobe Reader.
data.borderWidth = 0;
} else {
var dashArrayLength = dashArray.length;
if (dashArrayLength > 0) {
// According to the PDF specification: the elements in a dashArray
// shall be numbers that are nonnegative and not all equal to zero.
var isInvalid = false;
var numPositive = 0;
for (var i = 0; i < dashArrayLength; i++) {
var validNumber = (+dashArray[i] >= 0);
if (!validNumber) {
isInvalid = true;
break;
} else if (dashArray[i] > 0) {
numPositive++;
}
}
if (isInvalid || numPositive === 0) {
data.borderWidth = 0;
}
}
}
}
}
this.appearance = getDefaultAppearance(dict);
data.hasAppearance = !!this.appearance;
data.id = params.ref.num;
}
Annotation.prototype = {
getData: function Annotation_getData() {
return this.data;
},
isInvisible: function Annotation_isInvisible() {
var data = this.data;
if (data && SUPPORTED_TYPES.indexOf(data.subtype) !== -1) {
return false;
} else {
return !!(data &&
data.annotationFlags && // Default: not invisible
data.annotationFlags & 0x1); // Invisible
}
},
isViewable: function Annotation_isViewable() {
var data = this.data;
return !!(!this.isInvisible() &&
data &&
(!data.annotationFlags ||
!(data.annotationFlags & 0x22)) && // Hidden or NoView
data.rect); // rectangle is necessary
},
isPrintable: function Annotation_isPrintable() {
var data = this.data;
return !!(!this.isInvisible() &&
data &&
data.annotationFlags && // Default: not printable
data.annotationFlags & 0x4 && // Print
!(data.annotationFlags & 0x2) && // Hidden
data.rect); // rectangle is necessary
},
loadResources: function Annotation_loadResources(keys) {
return new Promise(function (resolve, reject) {
this.appearance.dict.getAsync('Resources').then(function (resources) {
if (!resources) {
resolve();
return;
}
var objectLoader = new ObjectLoader(resources.map,
keys,
resources.xref);
objectLoader.load().then(function() {
resolve(resources);
}, reject);
}, reject);
}.bind(this));
},
getOperatorList: function Annotation_getOperatorList(evaluator) {
if (!this.appearance) {
return Promise.resolve(new OperatorList());
}
var data = this.data;
var appearanceDict = this.appearance.dict;
var resourcesPromise = this.loadResources([
'ExtGState',
'ColorSpace',
'Pattern',
'Shading',
'XObject',
'Font'
// ProcSet
// Properties
]);
var bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
var matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
var transform = getTransformMatrix(data.rect, bbox, matrix);
var self = this;
return resourcesPromise.then(function(resources) {
var opList = new OperatorList();
opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]);
return evaluator.getOperatorList(self.appearance, resources, opList).
then(function () {
opList.addOp(OPS.endAnnotation, []);
self.appearance.reset();
return opList;
});
});
}
};
Annotation.getConstructor =
function Annotation_getConstructor(subtype, fieldType) {
if (!subtype) {
return;
}
// TODO(mack): Implement FreeText annotations
if (subtype === 'Link') {
return LinkAnnotation;
} else if (subtype === 'Text') {
return TextAnnotation;
} else if (subtype === 'Widget') {
if (!fieldType) {
return;
}
if (fieldType === 'Tx') {
return TextWidgetAnnotation;
} else {
return WidgetAnnotation;
}
} else {
return Annotation;
}
};
Annotation.fromRef = function Annotation_fromRef(xref, ref) {
var dict = xref.fetchIfRef(ref);
if (!isDict(dict)) {
return;
}
var subtype = dict.get('Subtype');
subtype = isName(subtype) ? subtype.name : '';
if (!subtype) {
return;
}
var fieldType = Util.getInheritableProperty(dict, 'FT');
fieldType = isName(fieldType) ? fieldType.name : '';
var Constructor = Annotation.getConstructor(subtype, fieldType);
if (!Constructor) {
return;
}
var params = {
dict: dict,
ref: ref,
};
var annotation = new Constructor(params);
if (annotation.isViewable() || annotation.isPrintable()) {
return annotation;
} else {
if (SUPPORTED_TYPES.indexOf(subtype) === -1) {
warn('unimplemented annotation type: ' + subtype);
}
}
};
Annotation.appendToOperatorList = function Annotation_appendToOperatorList(
annotations, opList, pdfManager, partialEvaluator, intent) {
function reject(e) {
annotationsReadyCapability.reject(e);
}
var annotationsReadyCapability = createPromiseCapability();
var annotationPromises = [];
for (var i = 0, n = annotations.length; i < n; ++i) {
if (intent === 'display' && annotations[i].isViewable() ||
intent === 'print' && annotations[i].isPrintable()) {
annotationPromises.push(
annotations[i].getOperatorList(partialEvaluator));
}
}
Promise.all(annotationPromises).then(function(datas) {
opList.addOp(OPS.beginAnnotations, []);
for (var i = 0, n = datas.length; i < n; ++i) {
var annotOpList = datas[i];
opList.addOpList(annotOpList);
}
opList.addOp(OPS.endAnnotations, []);
annotationsReadyCapability.resolve();
}, reject);
return annotationsReadyCapability.promise;
};
return Annotation;
})();
var WidgetAnnotation = (function WidgetAnnotationClosure() {
function WidgetAnnotation(params) {
Annotation.call(this, params);
var dict = params.dict;
var data = this.data;
data.fieldValue = stringToPDFString(
Util.getInheritableProperty(dict, 'V') || '');
data.alternativeText = stringToPDFString(dict.get('TU') || '');
data.defaultAppearance = Util.getInheritableProperty(dict, 'DA') || '';
var fieldType = Util.getInheritableProperty(dict, 'FT');
data.fieldType = isName(fieldType) ? fieldType.name : '';
data.fieldFlags = Util.getInheritableProperty(dict, 'Ff') || 0;
this.fieldResources = Util.getInheritableProperty(dict, 'DR') || Dict.empty;
// Building the full field name by collecting the field and
// its ancestors 'T' data and joining them using '.'.
var fieldName = [];
var namedItem = dict;
var ref = params.ref;
while (namedItem) {
var parent = namedItem.get('Parent');
var parentRef = namedItem.getRaw('Parent');
var name = namedItem.get('T');
if (name) {
fieldName.unshift(stringToPDFString(name));
} else if (parent && ref) {
// The field name is absent, that means more than one field
// with the same name may exist. Replacing the empty name
// with the '`' plus index in the parent's 'Kids' array.
// This is not in the PDF spec but necessary to id the
// the input controls.
var kids = parent.get('Kids');
var j, jj;
for (j = 0, jj = kids.length; j < jj; j++) {
var kidRef = kids[j];
if (kidRef.num === ref.num && kidRef.gen === ref.gen) {
break;
}
}
fieldName.unshift('`' + j);
}
namedItem = parent;
ref = parentRef;
}
data.fullName = fieldName.join('.');
}
var parent = Annotation.prototype;
Util.inherit(WidgetAnnotation, Annotation, {
isViewable: function WidgetAnnotation_isViewable() {
if (this.data.fieldType === 'Sig') {
warn('unimplemented annotation type: Widget signature');
return false;
}
return parent.isViewable.call(this);
}
});
return WidgetAnnotation;
})();
var TextWidgetAnnotation = (function TextWidgetAnnotationClosure() {
function TextWidgetAnnotation(params) {
WidgetAnnotation.call(this, params);
this.data.textAlignment = Util.getInheritableProperty(params.dict, 'Q');
this.data.annotationType = AnnotationType.WIDGET;
this.data.hasHtml = !this.data.hasAppearance && !!this.data.fieldValue;
}
Util.inherit(TextWidgetAnnotation, WidgetAnnotation, {
getOperatorList: function TextWidgetAnnotation_getOperatorList(evaluator) {
if (this.appearance) {
return Annotation.prototype.getOperatorList.call(this, evaluator);
}
var opList = new OperatorList();
var data = this.data;
// Even if there is an appearance stream, ignore it. This is the
// behaviour used by Adobe Reader.
if (!data.defaultAppearance) {
return Promise.resolve(opList);
}
var stream = new Stream(stringToBytes(data.defaultAppearance));
return evaluator.getOperatorList(stream, this.fieldResources, opList).
then(function () {
return opList;
});
}
});
return TextWidgetAnnotation;
})();
var InteractiveAnnotation = (function InteractiveAnnotationClosure() {
function InteractiveAnnotation(params) {
Annotation.call(this, params);
this.data.hasHtml = true;
}
Util.inherit(InteractiveAnnotation, Annotation, { });
return InteractiveAnnotation;
})();
var TextAnnotation = (function TextAnnotationClosure() {
function TextAnnotation(params) {
InteractiveAnnotation.call(this, params);
var dict = params.dict;
var data = this.data;
var content = dict.get('Contents');
var title = dict.get('T');
data.annotationType = AnnotationType.TEXT;
data.content = stringToPDFString(content || '');
data.title = stringToPDFString(title || '');
if (data.hasAppearance) {
data.name = 'NoIcon';
} else {
data.rect[1] = data.rect[3] - DEFAULT_ICON_SIZE;
data.rect[2] = data.rect[0] + DEFAULT_ICON_SIZE;
data.name = dict.has('Name') ? dict.get('Name').name : 'Note';
}
if (dict.has('C')) {
data.hasBgColor = true;
}
}
Util.inherit(TextAnnotation, InteractiveAnnotation, { });
return TextAnnotation;
})();
var LinkAnnotation = (function LinkAnnotationClosure() {
function LinkAnnotation(params) {
InteractiveAnnotation.call(this, params);
var dict = params.dict;
var data = this.data;
data.annotationType = AnnotationType.LINK;
var action = dict.get('A');
if (action) {
var linkType = action.get('S').name;
if (linkType === 'URI') {
var url = action.get('URI');
if (isName(url)) {
// Some bad PDFs do not put parentheses around relative URLs.
url = '/' + url.name;
} else if (url) {
url = addDefaultProtocolToUrl(url);
}
// TODO: pdf spec mentions urls can be relative to a Base
// entry in the dictionary.
if (!isValidUrl(url, false)) {
url = '';
}
data.url = url;
} else if (linkType === 'GoTo') {
data.dest = action.get('D');
} else if (linkType === 'GoToR') {
var urlDict = action.get('F');
if (isDict(urlDict)) {
// We assume that the 'url' is a Filspec dictionary
// and fetch the url without checking any further
url = urlDict.get('F') || '';
}
// TODO: pdf reference says that GoToR
// can also have 'NewWindow' attribute
if (!isValidUrl(url, false)) {
url = '';
}
data.url = url;
data.dest = action.get('D');
} else if (linkType === 'Named') {
data.action = action.get('N').name;
} else {
warn('unrecognized link type: ' + linkType);
}
} else if (dict.has('Dest')) {
// simple destination link
var dest = dict.get('Dest');
data.dest = isName(dest) ? dest.name : dest;
}
}
// Lets URLs beginning with 'www.' default to using the 'http://' protocol.
function addDefaultProtocolToUrl(url) {
if (url && url.indexOf('www.') === 0) {
return ('http://' + url);
}
return url;
}
Util.inherit(LinkAnnotation, InteractiveAnnotation, {
hasOperatorList: function LinkAnnotation_hasOperatorList() {
return false;
}
});
return LinkAnnotation;
})();

View File

@@ -0,0 +1,185 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
'use strict';
/* This class implements the QM Coder decoding as defined in
* JPEG 2000 Part I Final Committee Draft Version 1.0
* Annex C.3 Arithmetic decoding procedure
* available at http://www.jpeg.org/public/fcd15444-1.pdf
*
* The arithmetic decoder is used in conjunction with context models to decode
* JPEG2000 and JBIG2 streams.
*/
var ArithmeticDecoder = (function ArithmeticDecoderClosure() {
// Table C-2
var QeTable = [
{qe: 0x5601, nmps: 1, nlps: 1, switchFlag: 1},
{qe: 0x3401, nmps: 2, nlps: 6, switchFlag: 0},
{qe: 0x1801, nmps: 3, nlps: 9, switchFlag: 0},
{qe: 0x0AC1, nmps: 4, nlps: 12, switchFlag: 0},
{qe: 0x0521, nmps: 5, nlps: 29, switchFlag: 0},
{qe: 0x0221, nmps: 38, nlps: 33, switchFlag: 0},
{qe: 0x5601, nmps: 7, nlps: 6, switchFlag: 1},
{qe: 0x5401, nmps: 8, nlps: 14, switchFlag: 0},
{qe: 0x4801, nmps: 9, nlps: 14, switchFlag: 0},
{qe: 0x3801, nmps: 10, nlps: 14, switchFlag: 0},
{qe: 0x3001, nmps: 11, nlps: 17, switchFlag: 0},
{qe: 0x2401, nmps: 12, nlps: 18, switchFlag: 0},
{qe: 0x1C01, nmps: 13, nlps: 20, switchFlag: 0},
{qe: 0x1601, nmps: 29, nlps: 21, switchFlag: 0},
{qe: 0x5601, nmps: 15, nlps: 14, switchFlag: 1},
{qe: 0x5401, nmps: 16, nlps: 14, switchFlag: 0},
{qe: 0x5101, nmps: 17, nlps: 15, switchFlag: 0},
{qe: 0x4801, nmps: 18, nlps: 16, switchFlag: 0},
{qe: 0x3801, nmps: 19, nlps: 17, switchFlag: 0},
{qe: 0x3401, nmps: 20, nlps: 18, switchFlag: 0},
{qe: 0x3001, nmps: 21, nlps: 19, switchFlag: 0},
{qe: 0x2801, nmps: 22, nlps: 19, switchFlag: 0},
{qe: 0x2401, nmps: 23, nlps: 20, switchFlag: 0},
{qe: 0x2201, nmps: 24, nlps: 21, switchFlag: 0},
{qe: 0x1C01, nmps: 25, nlps: 22, switchFlag: 0},
{qe: 0x1801, nmps: 26, nlps: 23, switchFlag: 0},
{qe: 0x1601, nmps: 27, nlps: 24, switchFlag: 0},
{qe: 0x1401, nmps: 28, nlps: 25, switchFlag: 0},
{qe: 0x1201, nmps: 29, nlps: 26, switchFlag: 0},
{qe: 0x1101, nmps: 30, nlps: 27, switchFlag: 0},
{qe: 0x0AC1, nmps: 31, nlps: 28, switchFlag: 0},
{qe: 0x09C1, nmps: 32, nlps: 29, switchFlag: 0},
{qe: 0x08A1, nmps: 33, nlps: 30, switchFlag: 0},
{qe: 0x0521, nmps: 34, nlps: 31, switchFlag: 0},
{qe: 0x0441, nmps: 35, nlps: 32, switchFlag: 0},
{qe: 0x02A1, nmps: 36, nlps: 33, switchFlag: 0},
{qe: 0x0221, nmps: 37, nlps: 34, switchFlag: 0},
{qe: 0x0141, nmps: 38, nlps: 35, switchFlag: 0},
{qe: 0x0111, nmps: 39, nlps: 36, switchFlag: 0},
{qe: 0x0085, nmps: 40, nlps: 37, switchFlag: 0},
{qe: 0x0049, nmps: 41, nlps: 38, switchFlag: 0},
{qe: 0x0025, nmps: 42, nlps: 39, switchFlag: 0},
{qe: 0x0015, nmps: 43, nlps: 40, switchFlag: 0},
{qe: 0x0009, nmps: 44, nlps: 41, switchFlag: 0},
{qe: 0x0005, nmps: 45, nlps: 42, switchFlag: 0},
{qe: 0x0001, nmps: 45, nlps: 43, switchFlag: 0},
{qe: 0x5601, nmps: 46, nlps: 46, switchFlag: 0}
];
// C.3.5 Initialisation of the decoder (INITDEC)
function ArithmeticDecoder(data, start, end) {
this.data = data;
this.bp = start;
this.dataEnd = end;
this.chigh = data[start];
this.clow = 0;
this.byteIn();
this.chigh = ((this.chigh << 7) & 0xFFFF) | ((this.clow >> 9) & 0x7F);
this.clow = (this.clow << 7) & 0xFFFF;
this.ct -= 7;
this.a = 0x8000;
}
ArithmeticDecoder.prototype = {
// C.3.4 Compressed data input (BYTEIN)
byteIn: function ArithmeticDecoder_byteIn() {
var data = this.data;
var bp = this.bp;
if (data[bp] === 0xFF) {
var b1 = data[bp + 1];
if (b1 > 0x8F) {
this.clow += 0xFF00;
this.ct = 8;
} else {
bp++;
this.clow += (data[bp] << 9);
this.ct = 7;
this.bp = bp;
}
} else {
bp++;
this.clow += bp < this.dataEnd ? (data[bp] << 8) : 0xFF00;
this.ct = 8;
this.bp = bp;
}
if (this.clow > 0xFFFF) {
this.chigh += (this.clow >> 16);
this.clow &= 0xFFFF;
}
},
// C.3.2 Decoding a decision (DECODE)
readBit: function ArithmeticDecoder_readBit(contexts, pos) {
// contexts are packed into 1 byte:
// highest 7 bits carry cx.index, lowest bit carries cx.mps
var cx_index = contexts[pos] >> 1, cx_mps = contexts[pos] & 1;
var qeTableIcx = QeTable[cx_index];
var qeIcx = qeTableIcx.qe;
var d;
var a = this.a - qeIcx;
if (this.chigh < qeIcx) {
// exchangeLps
if (a < qeIcx) {
a = qeIcx;
d = cx_mps;
cx_index = qeTableIcx.nmps;
} else {
a = qeIcx;
d = 1 ^ cx_mps;
if (qeTableIcx.switchFlag === 1) {
cx_mps = d;
}
cx_index = qeTableIcx.nlps;
}
} else {
this.chigh -= qeIcx;
if ((a & 0x8000) !== 0) {
this.a = a;
return cx_mps;
}
// exchangeMps
if (a < qeIcx) {
d = 1 ^ cx_mps;
if (qeTableIcx.switchFlag === 1) {
cx_mps = d;
}
cx_index = qeTableIcx.nlps;
} else {
d = cx_mps;
cx_index = qeTableIcx.nmps;
}
}
// C.3.3 renormD;
do {
if (this.ct === 0) {
this.byteIn();
}
a <<= 1;
this.chigh = ((this.chigh << 1) & 0xFFFF) | ((this.clow >> 15) & 1);
this.clow = (this.clow << 1) & 0xFFFF;
this.ct--;
} while ((a & 0x8000) === 0);
this.a = a;
contexts[pos] = cx_index << 1 | cx_mps;
return d;
}
};
return ArithmeticDecoder;
})();

View File

@@ -0,0 +1,427 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* globals PDFJS */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
'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) {
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 createBidiText(str, isLTR, vertical) {
return {
str: str,
dir: (vertical ? 'ttb' : (isLTR ? 'ltr' : 'rtl'))
};
}
// These are used in bidi(), which is called frequently. We re-use them on
// each call to avoid unnecessary allocations.
var chars = [];
var types = [];
function bidi(str, startLevel, vertical) {
var isLTR = true;
var strLength = str.length;
if (strLength === 0 || vertical) {
return createBidiText(str, isLTR, vertical);
}
// Get types and fill arrays
chars.length = strLength;
types.length = strLength;
var numBidi = 0;
var i, ii;
for (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++;
}
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) {
isLTR = true;
return createBidiText(str, isLTR);
}
if (startLevel === -1) {
if ((strLength / numBidi) < 0.3) {
isLTR = true;
startLevel = 0;
} else {
isLTR = false;
startLevel = 1;
}
}
var levels = [];
for (i = 0; i < strLength; ++i) {
levels[i] = startLevel;
}
/*
X1-X10: skip most of this, since we are NOT doing the embeddings.
*/
var e = (isOdd(startLevel) ? 'R' : 'L');
var sor = e;
var eor = sor;
/*
W1. Examine each non-spacing mark (NSM) in the level run, and change the
type of the NSM to the type of the previous character. If the NSM is at the
start of the level run, it will get the type of sor.
*/
var lastType = sor;
for (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.
*/
lastType = sor;
var t;
for (i = 0; i < strLength; ++i) {
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 (i = 0; i < strLength; ++i) {
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 (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 (i = 0; i < strLength; ++i) {
if (types[i] === 'EN') {
// do before
var j;
for (j = i - 1; j >= 0; --j) {
if (types[j] !== 'ET') {
break;
}
types[j] = 'EN';
}
// do after
for (j = i + 1; j < strLength; --j) {
if (types[j] !== 'ET') {
break;
}
types[j] = 'EN';
}
}
}
/*
W6. Otherwise, separators and terminators change to Other Neutral:
*/
for (i = 0; i < strLength; ++i) {
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.
*/
lastType = sor;
for (i = 0; i < strLength; ++i) {
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 (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 (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 (i = 0; i < strLength; ++i) {
t = types[i];
if (isEven(levels[i])) {
if (t === 'R') {
levels[i] += 1;
} else if (t === 'AN' || t === 'EN') {
levels[i] += 2;
}
} else { // isOdd
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;
var level;
for (i = 0, ii = levels.length; i < ii; ++i) {
level = levels[i];
if (highestLevel < level) {
highestLevel = level;
}
if (lowestOddLevel > level && isOdd(level)) {
lowestOddLevel = level;
}
}
// now reverse between those limits
for (level = highestLevel; level >= lowestOddLevel; --level) {
// find segments to reverse
var start = -1;
for (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 (i = 0, ii = chars.length; i < ii; ++i) {
var ch = chars[i];
if (ch !== '<' && ch !== '>') {
result += ch;
}
}
return createBidiText(result, isLTR);
}
return bidi;
})();

View File

@@ -0,0 +1,119 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
'use strict';
var ISOAdobeCharset = [
'.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar',
'percent', 'ampersand', 'quoteright', 'parenleft', 'parenright',
'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero',
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight',
'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question',
'at', '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',
'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent',
'sterling', 'fraction', 'yen', 'florin', 'section', 'currency',
'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft',
'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl',
'periodcentered', 'paragraph', 'bullet', 'quotesinglbase',
'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis',
'perthousand', 'questiondown', 'grave', 'acute', 'circumflex', 'tilde',
'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla',
'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine',
'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae', 'dotlessi', 'lslash',
'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu',
'trademark', 'Eth', 'onehalf', 'plusminus', 'Thorn', 'onequarter',
'divide', 'brokenbar', 'degree', 'thorn', 'threequarters', 'twosuperior',
'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright',
'Aacute', 'Acircumflex', 'Adieresis', 'Agrave', 'Aring', 'Atilde',
'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute',
'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex',
'Odieresis', 'Ograve', 'Otilde', 'Scaron', 'Uacute', 'Ucircumflex',
'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute',
'acircumflex', 'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla',
'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute', 'icircumflex',
'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis',
'ograve', 'otilde', 'scaron', 'uacute', 'ucircumflex', 'udieresis',
'ugrave', 'yacute', 'ydieresis', 'zcaron'
];
var ExpertCharset = [
'.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle',
'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior',
'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma',
'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle',
'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle',
'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle',
'colon', 'semicolon', 'commasuperior', 'threequartersemdash',
'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior',
'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior',
'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall',
'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall',
'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall',
'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall',
'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary',
'onefitted', 'rupiah', 'Tildesmall', 'exclamdownsmall', 'centoldstyle',
'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall',
'Brevesmall', 'Caronsmall', 'Dotaccentsmall', 'Macronsmall',
'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall',
'Cedillasmall', 'onequarter', 'onehalf', 'threequarters',
'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths',
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
'periodinferior', 'commainferior', 'Agravesmall', 'Aacutesmall',
'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall',
'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall',
'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall',
'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall',
'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall',
'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall',
'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall',
'Ydieresissmall'
];
var ExpertSubsetCharset = [
'.notdef', 'space', 'dollaroldstyle', 'dollarsuperior',
'parenleftsuperior', 'parenrightsuperior', 'twodotenleader',
'onedotenleader', 'comma', 'hyphen', 'period', 'fraction',
'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle',
'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle',
'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior',
'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior',
'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior',
'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior',
'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior',
'parenrightinferior', 'hyphensuperior', 'colonmonetary', 'onefitted',
'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior', 'onequarter',
'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths',
'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior',
'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior',
'zeroinferior', 'oneinferior', 'twoinferior', 'threeinferior',
'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior',
'periodinferior', 'commainferior'
];

View File

@@ -0,0 +1,560 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals assert, MissingDataException, isInt, NetworkManager, Promise,
isEmptyObj, createPromiseCapability */
'use strict';
var ChunkedStream = (function ChunkedStreamClosure() {
function ChunkedStream(length, chunkSize, manager) {
this.bytes = new Uint8Array(length);
this.start = 0;
this.pos = 0;
this.end = length;
this.chunkSize = chunkSize;
this.loadedChunks = [];
this.numChunksLoaded = 0;
this.numChunks = Math.ceil(length / chunkSize);
this.manager = manager;
this.progressiveDataLength = 0;
this.lastSuccessfulEnsureByteChunk = -1; // a single-entry cache
}
// required methods for a stream. if a particular stream does not
// implement these, an error should be thrown
ChunkedStream.prototype = {
getMissingChunks: function ChunkedStream_getMissingChunks() {
var chunks = [];
for (var chunk = 0, n = this.numChunks; chunk < n; ++chunk) {
if (!this.loadedChunks[chunk]) {
chunks.push(chunk);
}
}
return chunks;
},
getBaseStreams: function ChunkedStream_getBaseStreams() {
return [this];
},
allChunksLoaded: function ChunkedStream_allChunksLoaded() {
return this.numChunksLoaded === this.numChunks;
},
onReceiveData: function ChunkedStream_onReceiveData(begin, chunk) {
var end = begin + chunk.byteLength;
assert(begin % this.chunkSize === 0, 'Bad begin offset: ' + begin);
// Using this.length is inaccurate here since this.start can be moved
// See ChunkedStream.moveStart()
var length = this.bytes.length;
assert(end % this.chunkSize === 0 || end === length,
'Bad end offset: ' + end);
this.bytes.set(new Uint8Array(chunk), begin);
var chunkSize = this.chunkSize;
var beginChunk = Math.floor(begin / chunkSize);
var endChunk = Math.floor((end - 1) / chunkSize) + 1;
var curChunk;
for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
if (!this.loadedChunks[curChunk]) {
this.loadedChunks[curChunk] = true;
++this.numChunksLoaded;
}
}
},
onReceiveProgressiveData:
function ChunkedStream_onReceiveProgressiveData(data) {
var position = this.progressiveDataLength;
var beginChunk = Math.floor(position / this.chunkSize);
this.bytes.set(new Uint8Array(data), position);
position += data.byteLength;
this.progressiveDataLength = position;
var endChunk = position >= this.end ? this.numChunks :
Math.floor(position / this.chunkSize);
var curChunk;
for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
if (!this.loadedChunks[curChunk]) {
this.loadedChunks[curChunk] = true;
++this.numChunksLoaded;
}
}
},
ensureByte: function ChunkedStream_ensureByte(pos) {
var chunk = Math.floor(pos / this.chunkSize);
if (chunk === this.lastSuccessfulEnsureByteChunk) {
return;
}
if (!this.loadedChunks[chunk]) {
throw new MissingDataException(pos, pos + 1);
}
this.lastSuccessfulEnsureByteChunk = chunk;
},
ensureRange: function ChunkedStream_ensureRange(begin, end) {
if (begin >= end) {
return;
}
if (end <= this.progressiveDataLength) {
return;
}
var chunkSize = this.chunkSize;
var beginChunk = Math.floor(begin / chunkSize);
var endChunk = Math.floor((end - 1) / chunkSize) + 1;
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
if (!this.loadedChunks[chunk]) {
throw new MissingDataException(begin, end);
}
}
},
nextEmptyChunk: function ChunkedStream_nextEmptyChunk(beginChunk) {
var chunk, n;
for (chunk = beginChunk, n = this.numChunks; chunk < n; ++chunk) {
if (!this.loadedChunks[chunk]) {
return chunk;
}
}
// Wrap around to beginning
for (chunk = 0; chunk < beginChunk; ++chunk) {
if (!this.loadedChunks[chunk]) {
return chunk;
}
}
return null;
},
hasChunk: function ChunkedStream_hasChunk(chunk) {
return !!this.loadedChunks[chunk];
},
get length() {
return this.end - this.start;
},
get isEmpty() {
return this.length === 0;
},
getByte: function ChunkedStream_getByte() {
var pos = this.pos;
if (pos >= this.end) {
return -1;
}
this.ensureByte(pos);
return this.bytes[this.pos++];
},
getUint16: function ChunkedStream_getUint16() {
var b0 = this.getByte();
var b1 = this.getByte();
return (b0 << 8) + b1;
},
getInt32: function ChunkedStream_getInt32() {
var b0 = this.getByte();
var b1 = this.getByte();
var b2 = this.getByte();
var b3 = this.getByte();
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
},
// returns subarray of original buffer
// should only be read
getBytes: function ChunkedStream_getBytes(length) {
var bytes = this.bytes;
var pos = this.pos;
var strEnd = this.end;
if (!length) {
this.ensureRange(pos, strEnd);
return bytes.subarray(pos, strEnd);
}
var end = pos + length;
if (end > strEnd) {
end = strEnd;
}
this.ensureRange(pos, end);
this.pos = end;
return bytes.subarray(pos, end);
},
peekByte: function ChunkedStream_peekByte() {
var peekedByte = this.getByte();
this.pos--;
return peekedByte;
},
peekBytes: function ChunkedStream_peekBytes(length) {
var bytes = this.getBytes(length);
this.pos -= bytes.length;
return bytes;
},
getByteRange: function ChunkedStream_getBytes(begin, end) {
this.ensureRange(begin, end);
return this.bytes.subarray(begin, end);
},
skip: function ChunkedStream_skip(n) {
if (!n) {
n = 1;
}
this.pos += n;
},
reset: function ChunkedStream_reset() {
this.pos = this.start;
},
moveStart: function ChunkedStream_moveStart() {
this.start = this.pos;
},
makeSubStream: function ChunkedStream_makeSubStream(start, length, dict) {
this.ensureRange(start, start + length);
function ChunkedStreamSubstream() {}
ChunkedStreamSubstream.prototype = Object.create(this);
ChunkedStreamSubstream.prototype.getMissingChunks = function() {
var chunkSize = this.chunkSize;
var beginChunk = Math.floor(this.start / chunkSize);
var endChunk = Math.floor((this.end - 1) / chunkSize) + 1;
var missingChunks = [];
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
if (!this.loadedChunks[chunk]) {
missingChunks.push(chunk);
}
}
return missingChunks;
};
var subStream = new ChunkedStreamSubstream();
subStream.pos = subStream.start = start;
subStream.end = start + length || this.end;
subStream.dict = dict;
return subStream;
},
isStream: true
};
return ChunkedStream;
})();
var ChunkedStreamManager = (function ChunkedStreamManagerClosure() {
function ChunkedStreamManager(length, chunkSize, url, args) {
this.stream = new ChunkedStream(length, chunkSize, this);
this.length = length;
this.chunkSize = chunkSize;
this.url = url;
this.disableAutoFetch = args.disableAutoFetch;
var msgHandler = this.msgHandler = args.msgHandler;
if (args.chunkedViewerLoading) {
msgHandler.on('OnDataRange', this.onReceiveData.bind(this));
msgHandler.on('OnDataProgress', this.onProgress.bind(this));
this.sendRequest = function ChunkedStreamManager_sendRequest(begin, end) {
msgHandler.send('RequestDataRange', { begin: begin, end: end });
};
} else {
var getXhr = function getXhr() {
//#if B2G
// return new XMLHttpRequest({ mozSystem: true });
//#else
return new XMLHttpRequest();
//#endif
};
this.networkManager = new NetworkManager(this.url, {
getXhr: getXhr,
httpHeaders: args.httpHeaders,
withCredentials: args.withCredentials
});
this.sendRequest = function ChunkedStreamManager_sendRequest(begin, end) {
this.networkManager.requestRange(begin, end, {
onDone: this.onReceiveData.bind(this),
onProgress: this.onProgress.bind(this)
});
};
}
this.currRequestId = 0;
this.chunksNeededByRequest = {};
this.requestsByChunk = {};
this.callbacksByRequest = {};
this.progressiveDataLength = 0;
this._loadedStreamCapability = createPromiseCapability();
if (args.initialData) {
this.onReceiveData({chunk: args.initialData});
}
}
ChunkedStreamManager.prototype = {
onLoadedStream: function ChunkedStreamManager_getLoadedStream() {
return this._loadedStreamCapability.promise;
},
// Get all the chunks that are not yet loaded and groups them into
// contiguous ranges to load in as few requests as possible
requestAllChunks: function ChunkedStreamManager_requestAllChunks() {
var missingChunks = this.stream.getMissingChunks();
this.requestChunks(missingChunks);
return this._loadedStreamCapability.promise;
},
requestChunks: function ChunkedStreamManager_requestChunks(chunks,
callback) {
var requestId = this.currRequestId++;
var chunksNeeded;
var i, ii;
this.chunksNeededByRequest[requestId] = chunksNeeded = {};
for (i = 0, ii = chunks.length; i < ii; i++) {
if (!this.stream.hasChunk(chunks[i])) {
chunksNeeded[chunks[i]] = true;
}
}
if (isEmptyObj(chunksNeeded)) {
if (callback) {
callback();
}
return;
}
this.callbacksByRequest[requestId] = callback;
var chunksToRequest = [];
for (var chunk in chunksNeeded) {
chunk = chunk | 0;
if (!(chunk in this.requestsByChunk)) {
this.requestsByChunk[chunk] = [];
chunksToRequest.push(chunk);
}
this.requestsByChunk[chunk].push(requestId);
}
if (!chunksToRequest.length) {
return;
}
var groupedChunksToRequest = this.groupChunks(chunksToRequest);
for (i = 0; i < groupedChunksToRequest.length; ++i) {
var groupedChunk = groupedChunksToRequest[i];
var begin = groupedChunk.beginChunk * this.chunkSize;
var end = Math.min(groupedChunk.endChunk * this.chunkSize, this.length);
this.sendRequest(begin, end);
}
},
getStream: function ChunkedStreamManager_getStream() {
return this.stream;
},
// Loads any chunks in the requested range that are not yet loaded
requestRange: function ChunkedStreamManager_requestRange(
begin, end, callback) {
end = Math.min(end, this.length);
var beginChunk = this.getBeginChunk(begin);
var endChunk = this.getEndChunk(end);
var chunks = [];
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
chunks.push(chunk);
}
this.requestChunks(chunks, callback);
},
requestRanges: function ChunkedStreamManager_requestRanges(ranges,
callback) {
ranges = ranges || [];
var chunksToRequest = [];
for (var i = 0; i < ranges.length; i++) {
var beginChunk = this.getBeginChunk(ranges[i].begin);
var endChunk = this.getEndChunk(ranges[i].end);
for (var chunk = beginChunk; chunk < endChunk; ++chunk) {
if (chunksToRequest.indexOf(chunk) < 0) {
chunksToRequest.push(chunk);
}
}
}
chunksToRequest.sort(function(a, b) { return a - b; });
this.requestChunks(chunksToRequest, callback);
},
// Groups a sorted array of chunks into as few continguous larger
// chunks as possible
groupChunks: function ChunkedStreamManager_groupChunks(chunks) {
var groupedChunks = [];
var beginChunk = -1;
var prevChunk = -1;
for (var i = 0; i < chunks.length; ++i) {
var chunk = chunks[i];
if (beginChunk < 0) {
beginChunk = chunk;
}
if (prevChunk >= 0 && prevChunk + 1 !== chunk) {
groupedChunks.push({ beginChunk: beginChunk,
endChunk: prevChunk + 1 });
beginChunk = chunk;
}
if (i + 1 === chunks.length) {
groupedChunks.push({ beginChunk: beginChunk,
endChunk: chunk + 1 });
}
prevChunk = chunk;
}
return groupedChunks;
},
onProgress: function ChunkedStreamManager_onProgress(args) {
var bytesLoaded = (this.stream.numChunksLoaded * this.chunkSize +
args.loaded);
this.msgHandler.send('DocProgress', {
loaded: bytesLoaded,
total: this.length
});
},
onReceiveData: function ChunkedStreamManager_onReceiveData(args) {
var chunk = args.chunk;
var isProgressive = args.begin === undefined;
var begin = isProgressive ? this.progressiveDataLength : args.begin;
var end = begin + chunk.byteLength;
var beginChunk = Math.floor(begin / this.chunkSize);
var endChunk = end < this.length ? Math.floor(end / this.chunkSize) :
Math.ceil(end / this.chunkSize);
if (isProgressive) {
this.stream.onReceiveProgressiveData(chunk);
this.progressiveDataLength = end;
} else {
this.stream.onReceiveData(begin, chunk);
}
if (this.stream.allChunksLoaded()) {
this._loadedStreamCapability.resolve(this.stream);
}
var loadedRequests = [];
var i, requestId;
for (chunk = beginChunk; chunk < endChunk; ++chunk) {
// The server might return more chunks than requested
var requestIds = this.requestsByChunk[chunk] || [];
delete this.requestsByChunk[chunk];
for (i = 0; i < requestIds.length; ++i) {
requestId = requestIds[i];
var chunksNeeded = this.chunksNeededByRequest[requestId];
if (chunk in chunksNeeded) {
delete chunksNeeded[chunk];
}
if (!isEmptyObj(chunksNeeded)) {
continue;
}
loadedRequests.push(requestId);
}
}
// If there are no pending requests, automatically fetch the next
// unfetched chunk of the PDF
if (!this.disableAutoFetch && isEmptyObj(this.requestsByChunk)) {
var nextEmptyChunk;
if (this.stream.numChunksLoaded === 1) {
// This is a special optimization so that after fetching the first
// chunk, rather than fetching the second chunk, we fetch the last
// chunk.
var lastChunk = this.stream.numChunks - 1;
if (!this.stream.hasChunk(lastChunk)) {
nextEmptyChunk = lastChunk;
}
} else {
nextEmptyChunk = this.stream.nextEmptyChunk(endChunk);
}
if (isInt(nextEmptyChunk)) {
this.requestChunks([nextEmptyChunk]);
}
}
for (i = 0; i < loadedRequests.length; ++i) {
requestId = loadedRequests[i];
var callback = this.callbacksByRequest[requestId];
delete this.callbacksByRequest[requestId];
if (callback) {
callback();
}
}
this.msgHandler.send('DocProgress', {
loaded: this.stream.numChunksLoaded * this.chunkSize,
total: this.length
});
},
onError: function ChunkedStreamManager_onError(err) {
this._loadedStreamCapability.reject(err);
},
getBeginChunk: function ChunkedStreamManager_getBeginChunk(begin) {
var chunk = Math.floor(begin / this.chunkSize);
return chunk;
},
getEndChunk: function ChunkedStreamManager_getEndChunk(end) {
if (end % this.chunkSize === 0) {
return end / this.chunkSize;
}
// 0 -> 0
// 1 -> 1
// 99 -> 1
// 100 -> 1
// 101 -> 2
var chunk = Math.floor((end - 1) / this.chunkSize) + 1;
return chunk;
}
};
return ChunkedStreamManager;
})();

View File

@@ -0,0 +1,956 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals Util, isString, isInt, warn, error, isCmd, isEOF, isName, Lexer,
isStream, StringStream, PDFJS, assert */
'use strict';
var BUILT_IN_CMAPS = [
// << Start unicode maps.
'Adobe-GB1-UCS2',
'Adobe-CNS1-UCS2',
'Adobe-Japan1-UCS2',
'Adobe-Korea1-UCS2',
// >> End unicode maps.
'78-EUC-H',
'78-EUC-V',
'78-H',
'78-RKSJ-H',
'78-RKSJ-V',
'78-V',
'78ms-RKSJ-H',
'78ms-RKSJ-V',
'83pv-RKSJ-H',
'90ms-RKSJ-H',
'90ms-RKSJ-V',
'90msp-RKSJ-H',
'90msp-RKSJ-V',
'90pv-RKSJ-H',
'90pv-RKSJ-V',
'Add-H',
'Add-RKSJ-H',
'Add-RKSJ-V',
'Add-V',
'Adobe-CNS1-0',
'Adobe-CNS1-1',
'Adobe-CNS1-2',
'Adobe-CNS1-3',
'Adobe-CNS1-4',
'Adobe-CNS1-5',
'Adobe-CNS1-6',
'Adobe-GB1-0',
'Adobe-GB1-1',
'Adobe-GB1-2',
'Adobe-GB1-3',
'Adobe-GB1-4',
'Adobe-GB1-5',
'Adobe-Japan1-0',
'Adobe-Japan1-1',
'Adobe-Japan1-2',
'Adobe-Japan1-3',
'Adobe-Japan1-4',
'Adobe-Japan1-5',
'Adobe-Japan1-6',
'Adobe-Korea1-0',
'Adobe-Korea1-1',
'Adobe-Korea1-2',
'B5-H',
'B5-V',
'B5pc-H',
'B5pc-V',
'CNS-EUC-H',
'CNS-EUC-V',
'CNS1-H',
'CNS1-V',
'CNS2-H',
'CNS2-V',
'ETHK-B5-H',
'ETHK-B5-V',
'ETen-B5-H',
'ETen-B5-V',
'ETenms-B5-H',
'ETenms-B5-V',
'EUC-H',
'EUC-V',
'Ext-H',
'Ext-RKSJ-H',
'Ext-RKSJ-V',
'Ext-V',
'GB-EUC-H',
'GB-EUC-V',
'GB-H',
'GB-V',
'GBK-EUC-H',
'GBK-EUC-V',
'GBK2K-H',
'GBK2K-V',
'GBKp-EUC-H',
'GBKp-EUC-V',
'GBT-EUC-H',
'GBT-EUC-V',
'GBT-H',
'GBT-V',
'GBTpc-EUC-H',
'GBTpc-EUC-V',
'GBpc-EUC-H',
'GBpc-EUC-V',
'H',
'HKdla-B5-H',
'HKdla-B5-V',
'HKdlb-B5-H',
'HKdlb-B5-V',
'HKgccs-B5-H',
'HKgccs-B5-V',
'HKm314-B5-H',
'HKm314-B5-V',
'HKm471-B5-H',
'HKm471-B5-V',
'HKscs-B5-H',
'HKscs-B5-V',
'Hankaku',
'Hiragana',
'KSC-EUC-H',
'KSC-EUC-V',
'KSC-H',
'KSC-Johab-H',
'KSC-Johab-V',
'KSC-V',
'KSCms-UHC-H',
'KSCms-UHC-HW-H',
'KSCms-UHC-HW-V',
'KSCms-UHC-V',
'KSCpc-EUC-H',
'KSCpc-EUC-V',
'Katakana',
'NWP-H',
'NWP-V',
'RKSJ-H',
'RKSJ-V',
'Roman',
'UniCNS-UCS2-H',
'UniCNS-UCS2-V',
'UniCNS-UTF16-H',
'UniCNS-UTF16-V',
'UniCNS-UTF32-H',
'UniCNS-UTF32-V',
'UniCNS-UTF8-H',
'UniCNS-UTF8-V',
'UniGB-UCS2-H',
'UniGB-UCS2-V',
'UniGB-UTF16-H',
'UniGB-UTF16-V',
'UniGB-UTF32-H',
'UniGB-UTF32-V',
'UniGB-UTF8-H',
'UniGB-UTF8-V',
'UniJIS-UCS2-H',
'UniJIS-UCS2-HW-H',
'UniJIS-UCS2-HW-V',
'UniJIS-UCS2-V',
'UniJIS-UTF16-H',
'UniJIS-UTF16-V',
'UniJIS-UTF32-H',
'UniJIS-UTF32-V',
'UniJIS-UTF8-H',
'UniJIS-UTF8-V',
'UniJIS2004-UTF16-H',
'UniJIS2004-UTF16-V',
'UniJIS2004-UTF32-H',
'UniJIS2004-UTF32-V',
'UniJIS2004-UTF8-H',
'UniJIS2004-UTF8-V',
'UniJISPro-UCS2-HW-V',
'UniJISPro-UCS2-V',
'UniJISPro-UTF8-V',
'UniJISX0213-UTF32-H',
'UniJISX0213-UTF32-V',
'UniJISX02132004-UTF32-H',
'UniJISX02132004-UTF32-V',
'UniKS-UCS2-H',
'UniKS-UCS2-V',
'UniKS-UTF16-H',
'UniKS-UTF16-V',
'UniKS-UTF32-H',
'UniKS-UTF32-V',
'UniKS-UTF8-H',
'UniKS-UTF8-V',
'V',
'WP-Symbol'];
// CMap, not to be confused with TrueType's cmap.
var CMap = (function CMapClosure() {
function CMap(builtInCMap) {
// Codespace ranges are stored as follows:
// [[1BytePairs], [2BytePairs], [3BytePairs], [4BytePairs]]
// where nBytePairs are ranges e.g. [low1, high1, low2, high2, ...]
this.codespaceRanges = [[], [], [], []];
this.numCodespaceRanges = 0;
// Map entries have one of two forms.
// - cid chars are 16-bit unsigned integers, stored as integers.
// - bf chars are variable-length byte sequences, stored as strings, with
// one byte per character.
this._map = [];
this.vertical = false;
this.useCMap = null;
this.builtInCMap = builtInCMap;
}
CMap.prototype = {
addCodespaceRange: function(n, low, high) {
this.codespaceRanges[n - 1].push(low, high);
this.numCodespaceRanges++;
},
mapCidRange: function(low, high, dstLow) {
while (low <= high) {
this._map[low++] = dstLow++;
}
},
mapBfRange: function(low, high, dstLow) {
var lastByte = dstLow.length - 1;
while (low <= high) {
this._map[low++] = dstLow;
// Only the last byte has to be incremented.
dstLow = dstLow.substr(0, lastByte) +
String.fromCharCode(dstLow.charCodeAt(lastByte) + 1);
}
},
mapBfRangeToArray: function(low, high, array) {
var i = 0, ii = array.length;
while (low <= high && i < ii) {
this._map[low] = array[i++];
++low;
}
},
// This is used for both bf and cid chars.
mapOne: function(src, dst) {
this._map[src] = dst;
},
lookup: function(code) {
return this._map[code];
},
contains: function(code) {
return this._map[code] !== undefined;
},
forEach: function(callback) {
// Most maps have fewer than 65536 entries, and for those we use normal
// array iteration. But really sparse tables are possible -- e.g. with
// indices in the *billions*. For such tables we use for..in, which isn't
// ideal because it stringifies the indices for all present elements, but
// it does avoid iterating over every undefined entry.
var map = this._map;
var length = map.length;
var i;
if (length <= 0x10000) {
for (i = 0; i < length; i++) {
if (map[i] !== undefined) {
callback(i, map[i]);
}
}
} else {
for (i in this._map) {
callback(i, map[i]);
}
}
},
charCodeOf: function(value) {
return this._map.indexOf(value);
},
getMap: function() {
return this._map;
},
readCharCode: function(str, offset, out) {
var c = 0;
var codespaceRanges = this.codespaceRanges;
var codespaceRangesLen = this.codespaceRanges.length;
// 9.7.6.2 CMap Mapping
// The code length is at most 4.
for (var n = 0; n < codespaceRangesLen; n++) {
c = ((c << 8) | str.charCodeAt(offset + n)) >>> 0;
// Check each codespace range to see if it falls within.
var codespaceRange = codespaceRanges[n];
for (var k = 0, kk = codespaceRange.length; k < kk;) {
var low = codespaceRange[k++];
var high = codespaceRange[k++];
if (c >= low && c <= high) {
out.charcode = c;
out.length = n + 1;
return;
}
}
}
out.charcode = 0;
out.length = 1;
}
};
return CMap;
})();
// A special case of CMap, where the _map array implicitly has a length of
// 65535 and each element is equal to its index.
var IdentityCMap = (function IdentityCMapClosure() {
function IdentityCMap(vertical, n) {
CMap.call(this);
this.vertical = vertical;
this.addCodespaceRange(n, 0, 0xffff);
}
Util.inherit(IdentityCMap, CMap, {});
IdentityCMap.prototype = {
addCodespaceRange: CMap.prototype.addCodespaceRange,
mapCidRange: function(low, high, dstLow) {
error('should not call mapCidRange');
},
mapBfRange: function(low, high, dstLow) {
error('should not call mapBfRange');
},
mapBfRangeToArray: function(low, high, array) {
error('should not call mapBfRangeToArray');
},
mapOne: function(src, dst) {
error('should not call mapCidOne');
},
lookup: function(code) {
return (isInt(code) && code <= 0xffff) ? code : undefined;
},
contains: function(code) {
return isInt(code) && code <= 0xffff;
},
forEach: function(callback) {
for (var i = 0; i <= 0xffff; i++) {
callback(i, i);
}
},
charCodeOf: function(value) {
return (isInt(value) && value <= 0xffff) ? value : -1;
},
getMap: function() {
// Sometimes identity maps must be instantiated, but it's rare.
var map = new Array(0x10000);
for (var i = 0; i <= 0xffff; i++) {
map[i] = i;
}
return map;
},
readCharCode: CMap.prototype.readCharCode
};
return IdentityCMap;
})();
var BinaryCMapReader = (function BinaryCMapReaderClosure() {
function fetchBinaryData(url) {
var nonBinaryRequest = PDFJS.disableWorker;
var request = new XMLHttpRequest();
request.open('GET', url, false);
if (!nonBinaryRequest) {
try {
request.responseType = 'arraybuffer';
nonBinaryRequest = request.responseType !== 'arraybuffer';
} catch (e) {
nonBinaryRequest = true;
}
}
if (nonBinaryRequest && request.overrideMimeType) {
request.overrideMimeType('text/plain; charset=x-user-defined');
}
request.send(null);
if (nonBinaryRequest ? !request.responseText : !request.response) {
error('Unable to get binary cMap at: ' + url);
}
if (nonBinaryRequest) {
var data = Array.prototype.map.call(request.responseText, function (ch) {
return ch.charCodeAt(0) & 255;
});
return new Uint8Array(data);
}
return new Uint8Array(request.response);
}
function hexToInt(a, size) {
var n = 0;
for (var i = 0; i <= size; i++) {
n = (n << 8) | a[i];
}
return n >>> 0;
}
function hexToStr(a, size) {
// This code is hot. Special-case some common values to avoid creating an
// object with subarray().
if (size === 1) {
return String.fromCharCode(a[0], a[1]);
}
if (size === 3) {
return String.fromCharCode(a[0], a[1], a[2], a[3]);
}
return String.fromCharCode.apply(null, a.subarray(0, size + 1));
}
function addHex(a, b, size) {
var c = 0;
for (var i = size; i >= 0; i--) {
c += a[i] + b[i];
a[i] = c & 255;
c >>= 8;
}
}
function incHex(a, size) {
var c = 1;
for (var i = size; i >= 0 && c > 0; i--) {
c += a[i];
a[i] = c & 255;
c >>= 8;
}
}
var MAX_NUM_SIZE = 16;
var MAX_ENCODED_NUM_SIZE = 19; // ceil(MAX_NUM_SIZE * 7 / 8)
function BinaryCMapStream(data) {
this.buffer = data;
this.pos = 0;
this.end = data.length;
this.tmpBuf = new Uint8Array(MAX_ENCODED_NUM_SIZE);
}
BinaryCMapStream.prototype = {
readByte: function () {
if (this.pos >= this.end) {
return -1;
}
return this.buffer[this.pos++];
},
readNumber: function () {
var n = 0;
var last;
do {
var b = this.readByte();
if (b < 0) {
error('unexpected EOF in bcmap');
}
last = !(b & 0x80);
n = (n << 7) | (b & 0x7F);
} while (!last);
return n;
},
readSigned: function () {
var n = this.readNumber();
return (n & 1) ? ~(n >>> 1) : n >>> 1;
},
readHex: function (num, size) {
num.set(this.buffer.subarray(this.pos,
this.pos + size + 1));
this.pos += size + 1;
},
readHexNumber: function (num, size) {
var last;
var stack = this.tmpBuf, sp = 0;
do {
var b = this.readByte();
if (b < 0) {
error('unexpected EOF in bcmap');
}
last = !(b & 0x80);
stack[sp++] = b & 0x7F;
} while (!last);
var i = size, buffer = 0, bufferSize = 0;
while (i >= 0) {
while (bufferSize < 8 && stack.length > 0) {
buffer = (stack[--sp] << bufferSize) | buffer;
bufferSize += 7;
}
num[i] = buffer & 255;
i--;
buffer >>= 8;
bufferSize -= 8;
}
},
readHexSigned: function (num, size) {
this.readHexNumber(num, size);
var sign = num[size] & 1 ? 255 : 0;
var c = 0;
for (var i = 0; i <= size; i++) {
c = ((c & 1) << 8) | num[i];
num[i] = (c >> 1) ^ sign;
}
},
readString: function () {
var len = this.readNumber();
var s = '';
for (var i = 0; i < len; i++) {
s += String.fromCharCode(this.readNumber());
}
return s;
}
};
function processBinaryCMap(url, cMap, extend) {
var data = fetchBinaryData(url);
var stream = new BinaryCMapStream(data);
var header = stream.readByte();
cMap.vertical = !!(header & 1);
var useCMap = null;
var start = new Uint8Array(MAX_NUM_SIZE);
var end = new Uint8Array(MAX_NUM_SIZE);
var char = new Uint8Array(MAX_NUM_SIZE);
var charCode = new Uint8Array(MAX_NUM_SIZE);
var tmp = new Uint8Array(MAX_NUM_SIZE);
var code;
var b;
while ((b = stream.readByte()) >= 0) {
var type = b >> 5;
if (type === 7) { // metadata, e.g. comment or usecmap
switch (b & 0x1F) {
case 0:
stream.readString(); // skipping comment
break;
case 1:
useCMap = stream.readString();
break;
}
continue;
}
var sequence = !!(b & 0x10);
var dataSize = b & 15;
assert(dataSize + 1 <= MAX_NUM_SIZE);
var ucs2DataSize = 1;
var subitemsCount = stream.readNumber();
var i;
switch (type) {
case 0: // codespacerange
stream.readHex(start, dataSize);
stream.readHexNumber(end, dataSize);
addHex(end, start, dataSize);
cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize),
hexToInt(end, dataSize));
for (i = 1; i < subitemsCount; i++) {
incHex(end, dataSize);
stream.readHexNumber(start, dataSize);
addHex(start, end, dataSize);
stream.readHexNumber(end, dataSize);
addHex(end, start, dataSize);
cMap.addCodespaceRange(dataSize + 1, hexToInt(start, dataSize),
hexToInt(end, dataSize));
}
break;
case 1: // notdefrange
stream.readHex(start, dataSize);
stream.readHexNumber(end, dataSize);
addHex(end, start, dataSize);
code = stream.readNumber();
// undefined range, skipping
for (i = 1; i < subitemsCount; i++) {
incHex(end, dataSize);
stream.readHexNumber(start, dataSize);
addHex(start, end, dataSize);
stream.readHexNumber(end, dataSize);
addHex(end, start, dataSize);
code = stream.readNumber();
// nop
}
break;
case 2: // cidchar
stream.readHex(char, dataSize);
code = stream.readNumber();
cMap.mapOne(hexToInt(char, dataSize), code);
for (i = 1; i < subitemsCount; i++) {
incHex(char, dataSize);
if (!sequence) {
stream.readHexNumber(tmp, dataSize);
addHex(char, tmp, dataSize);
}
code = stream.readSigned() + (code + 1);
cMap.mapOne(hexToInt(char, dataSize), code);
}
break;
case 3: // cidrange
stream.readHex(start, dataSize);
stream.readHexNumber(end, dataSize);
addHex(end, start, dataSize);
code = stream.readNumber();
cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize),
code);
for (i = 1; i < subitemsCount; i++) {
incHex(end, dataSize);
if (!sequence) {
stream.readHexNumber(start, dataSize);
addHex(start, end, dataSize);
} else {
start.set(end);
}
stream.readHexNumber(end, dataSize);
addHex(end, start, dataSize);
code = stream.readNumber();
cMap.mapCidRange(hexToInt(start, dataSize), hexToInt(end, dataSize),
code);
}
break;
case 4: // bfchar
stream.readHex(char, ucs2DataSize);
stream.readHex(charCode, dataSize);
cMap.mapOne(hexToInt(char, ucs2DataSize),
hexToStr(charCode, dataSize));
for (i = 1; i < subitemsCount; i++) {
incHex(char, ucs2DataSize);
if (!sequence) {
stream.readHexNumber(tmp, ucs2DataSize);
addHex(char, tmp, ucs2DataSize);
}
incHex(charCode, dataSize);
stream.readHexSigned(tmp, dataSize);
addHex(charCode, tmp, dataSize);
cMap.mapOne(hexToInt(char, ucs2DataSize),
hexToStr(charCode, dataSize));
}
break;
case 5: // bfrange
stream.readHex(start, ucs2DataSize);
stream.readHexNumber(end, ucs2DataSize);
addHex(end, start, ucs2DataSize);
stream.readHex(charCode, dataSize);
cMap.mapBfRange(hexToInt(start, ucs2DataSize),
hexToInt(end, ucs2DataSize),
hexToStr(charCode, dataSize));
for (i = 1; i < subitemsCount; i++) {
incHex(end, ucs2DataSize);
if (!sequence) {
stream.readHexNumber(start, ucs2DataSize);
addHex(start, end, ucs2DataSize);
} else {
start.set(end);
}
stream.readHexNumber(end, ucs2DataSize);
addHex(end, start, ucs2DataSize);
stream.readHex(charCode, dataSize);
cMap.mapBfRange(hexToInt(start, ucs2DataSize),
hexToInt(end, ucs2DataSize),
hexToStr(charCode, dataSize));
}
break;
default:
error('Unknown type: ' + type);
break;
}
}
if (useCMap) {
extend(useCMap);
}
return cMap;
}
function BinaryCMapReader() {}
BinaryCMapReader.prototype = {
read: processBinaryCMap
};
return BinaryCMapReader;
})();
var CMapFactory = (function CMapFactoryClosure() {
function strToInt(str) {
var a = 0;
for (var i = 0; i < str.length; i++) {
a = (a << 8) | str.charCodeAt(i);
}
return a >>> 0;
}
function expectString(obj) {
if (!isString(obj)) {
error('Malformed CMap: expected string.');
}
}
function expectInt(obj) {
if (!isInt(obj)) {
error('Malformed CMap: expected int.');
}
}
function parseBfChar(cMap, lexer) {
while (true) {
var obj = lexer.getObj();
if (isEOF(obj)) {
break;
}
if (isCmd(obj, 'endbfchar')) {
return;
}
expectString(obj);
var src = strToInt(obj);
obj = lexer.getObj();
// TODO are /dstName used?
expectString(obj);
var dst = obj;
cMap.mapOne(src, dst);
}
}
function parseBfRange(cMap, lexer) {
while (true) {
var obj = lexer.getObj();
if (isEOF(obj)) {
break;
}
if (isCmd(obj, 'endbfrange')) {
return;
}
expectString(obj);
var low = strToInt(obj);
obj = lexer.getObj();
expectString(obj);
var high = strToInt(obj);
obj = lexer.getObj();
if (isInt(obj) || isString(obj)) {
var dstLow = isInt(obj) ? String.fromCharCode(obj) : obj;
cMap.mapBfRange(low, high, dstLow);
} else if (isCmd(obj, '[')) {
obj = lexer.getObj();
var array = [];
while (!isCmd(obj, ']') && !isEOF(obj)) {
array.push(obj);
obj = lexer.getObj();
}
cMap.mapBfRangeToArray(low, high, array);
} else {
break;
}
}
error('Invalid bf range.');
}
function parseCidChar(cMap, lexer) {
while (true) {
var obj = lexer.getObj();
if (isEOF(obj)) {
break;
}
if (isCmd(obj, 'endcidchar')) {
return;
}
expectString(obj);
var src = strToInt(obj);
obj = lexer.getObj();
expectInt(obj);
var dst = obj;
cMap.mapOne(src, dst);
}
}
function parseCidRange(cMap, lexer) {
while (true) {
var obj = lexer.getObj();
if (isEOF(obj)) {
break;
}
if (isCmd(obj, 'endcidrange')) {
return;
}
expectString(obj);
var low = strToInt(obj);
obj = lexer.getObj();
expectString(obj);
var high = strToInt(obj);
obj = lexer.getObj();
expectInt(obj);
var dstLow = obj;
cMap.mapCidRange(low, high, dstLow);
}
}
function parseCodespaceRange(cMap, lexer) {
while (true) {
var obj = lexer.getObj();
if (isEOF(obj)) {
break;
}
if (isCmd(obj, 'endcodespacerange')) {
return;
}
if (!isString(obj)) {
break;
}
var low = strToInt(obj);
obj = lexer.getObj();
if (!isString(obj)) {
break;
}
var high = strToInt(obj);
cMap.addCodespaceRange(obj.length, low, high);
}
error('Invalid codespace range.');
}
function parseWMode(cMap, lexer) {
var obj = lexer.getObj();
if (isInt(obj)) {
cMap.vertical = !!obj;
}
}
function parseCMap(cMap, lexer, builtInCMapParams, useCMap) {
var previous;
var embededUseCMap;
objLoop: while (true) {
var obj = lexer.getObj();
if (isEOF(obj)) {
break;
} else if (isName(obj)) {
if (obj.name === 'WMode') {
parseWMode(cMap, lexer);
}
previous = obj;
} else if (isCmd(obj)) {
switch (obj.cmd) {
case 'endcmap':
break objLoop;
case 'usecmap':
if (isName(previous)) {
embededUseCMap = previous.name;
}
break;
case 'begincodespacerange':
parseCodespaceRange(cMap, lexer);
break;
case 'beginbfchar':
parseBfChar(cMap, lexer);
break;
case 'begincidchar':
parseCidChar(cMap, lexer);
break;
case 'beginbfrange':
parseBfRange(cMap, lexer);
break;
case 'begincidrange':
parseCidRange(cMap, lexer);
break;
}
}
}
if (!useCMap && embededUseCMap) {
// Load the usecmap definition from the file only if there wasn't one
// specified.
useCMap = embededUseCMap;
}
if (useCMap) {
extendCMap(cMap, builtInCMapParams, useCMap);
}
}
function extendCMap(cMap, builtInCMapParams, useCMap) {
cMap.useCMap = createBuiltInCMap(useCMap, builtInCMapParams);
// If there aren't any code space ranges defined clone all the parent ones
// into this cMap.
if (cMap.numCodespaceRanges === 0) {
var useCodespaceRanges = cMap.useCMap.codespaceRanges;
for (var i = 0; i < useCodespaceRanges.length; i++) {
cMap.codespaceRanges[i] = useCodespaceRanges[i].slice();
}
cMap.numCodespaceRanges = cMap.useCMap.numCodespaceRanges;
}
// Merge the map into the current one, making sure not to override
// any previously defined entries.
cMap.useCMap.forEach(function(key, value) {
if (!cMap.contains(key)) {
cMap.mapOne(key, cMap.useCMap.lookup(key));
}
});
}
function parseBinaryCMap(name, builtInCMapParams) {
var url = builtInCMapParams.url + name + '.bcmap';
var cMap = new CMap(true);
new BinaryCMapReader().read(url, cMap, function (useCMap) {
extendCMap(cMap, builtInCMapParams, useCMap);
});
return cMap;
}
function createBuiltInCMap(name, builtInCMapParams) {
if (name === 'Identity-H') {
return new IdentityCMap(false, 2);
} else if (name === 'Identity-V') {
return new IdentityCMap(true, 2);
}
if (BUILT_IN_CMAPS.indexOf(name) === -1) {
error('Unknown cMap name: ' + name);
}
assert(builtInCMapParams, 'built-in cMap parameters are not provided');
if (builtInCMapParams.packed) {
return parseBinaryCMap(name, builtInCMapParams);
}
var request = new XMLHttpRequest();
var url = builtInCMapParams.url + name;
request.open('GET', url, false);
request.send(null);
if (!request.responseText) {
error('Unable to get cMap at: ' + url);
}
var cMap = new CMap(true);
var lexer = new Lexer(new StringStream(request.responseText));
parseCMap(cMap, lexer, builtInCMapParams, null);
return cMap;
}
return {
create: function (encoding, builtInCMapParams, useCMap) {
if (isName(encoding)) {
return createBuiltInCMap(encoding.name, builtInCMapParams);
} else if (isStream(encoding)) {
var cMap = new CMap();
var lexer = new Lexer(encoding);
try {
parseCMap(cMap, lexer, builtInCMapParams, useCMap);
} catch (e) {
warn('Invalid CMap data. ' + e);
}
return cMap;
}
error('Encoding required.');
}
};
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,531 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals assert, calculateMD5, Catalog, Dict, error, info, isArray,
isArrayBuffer, isName, isStream, isString, createPromiseCapability,
Linearization, NullStream, PartialEvaluator, shadow, Stream, Lexer,
StreamsSequenceStream, stringToPDFString, stringToBytes, Util, XRef,
MissingDataException, Promise, Annotation, ObjectLoader, OperatorList
*/
'use strict';
var Page = (function PageClosure() {
var LETTER_SIZE_MEDIABOX = [0, 0, 612, 792];
function Page(pdfManager, xref, pageIndex, pageDict, ref, fontCache) {
this.pdfManager = pdfManager;
this.pageIndex = pageIndex;
this.pageDict = pageDict;
this.xref = xref;
this.ref = ref;
this.fontCache = fontCache;
this.idCounters = {
obj: 0
};
this.resourcesPromise = null;
}
Page.prototype = {
getPageProp: function Page_getPageProp(key) {
return this.pageDict.get(key);
},
getInheritedPageProp: function Page_inheritPageProp(key) {
var dict = this.pageDict;
var value = dict.get(key);
while (value === undefined) {
dict = dict.get('Parent');
if (!dict) {
break;
}
value = dict.get(key);
}
return value;
},
get content() {
return this.getPageProp('Contents');
},
get resources() {
var value = this.getInheritedPageProp('Resources');
// For robustness: The spec states that a \Resources entry has to be
// present, but can be empty. Some document omit it still. In this case
// return an empty dictionary:
if (value === undefined) {
value = Dict.empty;
}
return shadow(this, 'resources', value);
},
get mediaBox() {
var obj = this.getInheritedPageProp('MediaBox');
// Reset invalid media box to letter size.
if (!isArray(obj) || obj.length !== 4) {
obj = LETTER_SIZE_MEDIABOX;
}
return shadow(this, 'mediaBox', obj);
},
get view() {
var mediaBox = this.mediaBox;
var cropBox = this.getInheritedPageProp('CropBox');
if (!isArray(cropBox) || cropBox.length !== 4) {
return shadow(this, 'view', mediaBox);
}
// From the spec, 6th ed., p.963:
// "The crop, bleed, trim, and art boxes should not ordinarily
// extend beyond the boundaries of the media box. If they do, they are
// effectively reduced to their intersection with the media box."
cropBox = Util.intersect(cropBox, mediaBox);
if (!cropBox) {
return shadow(this, 'view', mediaBox);
}
return shadow(this, 'view', cropBox);
},
get annotationRefs() {
return shadow(this, 'annotationRefs',
this.getInheritedPageProp('Annots'));
},
get rotate() {
var rotate = this.getInheritedPageProp('Rotate') || 0;
// Normalize rotation so it's a multiple of 90 and between 0 and 270
if (rotate % 90 !== 0) {
rotate = 0;
} else if (rotate >= 360) {
rotate = rotate % 360;
} else if (rotate < 0) {
// The spec doesn't cover negatives, assume its counterclockwise
// rotation. The following is the other implementation of modulo.
rotate = ((rotate % 360) + 360) % 360;
}
return shadow(this, 'rotate', rotate);
},
getContentStream: function Page_getContentStream() {
var content = this.content;
var stream;
if (isArray(content)) {
// fetching items
var xref = this.xref;
var i, n = content.length;
var streams = [];
for (i = 0; i < n; ++i) {
streams.push(xref.fetchIfRef(content[i]));
}
stream = new StreamsSequenceStream(streams);
} else if (isStream(content)) {
stream = content;
} else {
// replacing non-existent page content with empty one
stream = new NullStream();
}
return stream;
},
loadResources: function Page_loadResources(keys) {
if (!this.resourcesPromise) {
// TODO: add async getInheritedPageProp and remove this.
this.resourcesPromise = this.pdfManager.ensure(this, 'resources');
}
return this.resourcesPromise.then(function resourceSuccess() {
var objectLoader = new ObjectLoader(this.resources.map,
keys,
this.xref);
return objectLoader.load();
}.bind(this));
},
getOperatorList: function Page_getOperatorList(handler, intent) {
var self = this;
var pdfManager = this.pdfManager;
var contentStreamPromise = pdfManager.ensure(this, 'getContentStream',
[]);
var resourcesPromise = this.loadResources([
'ExtGState',
'ColorSpace',
'Pattern',
'Shading',
'XObject',
'Font'
// ProcSet
// Properties
]);
var partialEvaluator = new PartialEvaluator(pdfManager, this.xref,
handler, this.pageIndex,
'p' + this.pageIndex + '_',
this.idCounters,
this.fontCache);
var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]);
var pageListPromise = dataPromises.then(function(data) {
var contentStream = data[0];
var opList = new OperatorList(intent, handler, self.pageIndex);
handler.send('StartRenderPage', {
transparency: partialEvaluator.hasBlendModes(self.resources),
pageIndex: self.pageIndex,
intent: intent
});
return partialEvaluator.getOperatorList(contentStream, self.resources,
opList).then(function () {
return opList;
});
});
var annotationsPromise = pdfManager.ensure(this, 'annotations');
return Promise.all([pageListPromise, annotationsPromise]).then(
function(datas) {
var pageOpList = datas[0];
var annotations = datas[1];
if (annotations.length === 0) {
pageOpList.flush(true);
return pageOpList;
}
var annotationsReadyPromise = Annotation.appendToOperatorList(
annotations, pageOpList, pdfManager, partialEvaluator, intent);
return annotationsReadyPromise.then(function () {
pageOpList.flush(true);
return pageOpList;
});
});
},
extractTextContent: function Page_extractTextContent() {
var handler = {
on: function nullHandlerOn() {},
send: function nullHandlerSend() {}
};
var self = this;
var pdfManager = this.pdfManager;
var contentStreamPromise = pdfManager.ensure(this, 'getContentStream',
[]);
var resourcesPromise = this.loadResources([
'ExtGState',
'XObject',
'Font'
]);
var dataPromises = Promise.all([contentStreamPromise,
resourcesPromise]);
return dataPromises.then(function(data) {
var contentStream = data[0];
var partialEvaluator = new PartialEvaluator(pdfManager, self.xref,
handler, self.pageIndex,
'p' + self.pageIndex + '_',
self.idCounters,
self.fontCache);
return partialEvaluator.getTextContent(contentStream,
self.resources);
});
},
getAnnotationsData: function Page_getAnnotationsData() {
var annotations = this.annotations;
var annotationsData = [];
for (var i = 0, n = annotations.length; i < n; ++i) {
annotationsData.push(annotations[i].getData());
}
// sort items in visual order: top->bottom, left->right
function sortAnnotations(a, b) {
// rect=[x1, y1, x2, y2]
if (a.rect[2] < b.rect[0]) return -1;
if (b.rect[2] < a.rect[0]) return 1;
if (a.rect[1] < b.rect[1]) return 1;
if (a.rect[1] > b.rect[1]) return -1;
return 0;
}
annotationsData.sort(sortAnnotations);
// return annotations
return annotationsData;
},
get annotations() {
var annotations = [];
var annotationRefs = (this.annotationRefs || []);
for (var i = 0, n = annotationRefs.length; i < n; ++i) {
var annotationRef = annotationRefs[i];
var annotation = Annotation.fromRef(this.xref, annotationRef);
if (annotation) {
annotations.push(annotation);
}
}
return shadow(this, 'annotations', annotations);
}
};
return Page;
})();
/**
* The `PDFDocument` holds all the data of the PDF file. Compared to the
* `PDFDoc`, this one doesn't have any job management code.
* Right now there exists one PDFDocument on the main thread + one object
* for each worker. If there is no worker support enabled, there are two
* `PDFDocument` objects on the main thread created.
*/
var PDFDocument = (function PDFDocumentClosure() {
function PDFDocument(pdfManager, arg, password) {
if (isStream(arg)) {
init.call(this, pdfManager, arg, password);
} else if (isArrayBuffer(arg)) {
init.call(this, pdfManager, new Stream(arg), password);
} else {
error('PDFDocument: Unknown argument type');
}
}
function init(pdfManager, stream, password) {
assert(stream.length > 0, 'stream must have data');
this.pdfManager = pdfManager;
this.stream = stream;
var xref = new XRef(this.stream, password, pdfManager);
this.xref = xref;
}
function find(stream, needle, limit, backwards) {
var pos = stream.pos;
var end = stream.end;
var strBuf = [];
if (pos + limit > end) {
limit = end - pos;
}
for (var n = 0; n < limit; ++n) {
strBuf.push(String.fromCharCode(stream.getByte()));
}
var str = strBuf.join('');
stream.pos = pos;
var index = backwards ? str.lastIndexOf(needle) : str.indexOf(needle);
if (index === -1) {
return false; /* not found */
}
stream.pos += index;
return true; /* found */
}
var DocumentInfoValidators = {
get entries() {
// Lazily build this since all the validation functions below are not
// defined until after this file loads.
return shadow(this, 'entries', {
Title: isString,
Author: isString,
Subject: isString,
Keywords: isString,
Creator: isString,
Producer: isString,
CreationDate: isString,
ModDate: isString,
Trapped: isName
});
}
};
PDFDocument.prototype = {
parse: function PDFDocument_parse(recoveryMode) {
this.setup(recoveryMode);
try {
// checking if AcroForm is present
this.acroForm = this.catalog.catDict.get('AcroForm');
if (this.acroForm) {
this.xfa = this.acroForm.get('XFA');
var fields = this.acroForm.get('Fields');
if ((!fields || !isArray(fields) || fields.length === 0) &&
!this.xfa) {
// no fields and no XFA -- not a form (?)
this.acroForm = null;
}
}
} catch (ex) {
info('Something wrong with AcroForm entry');
this.acroForm = null;
}
},
get linearization() {
var linearization = null;
if (this.stream.length) {
try {
linearization = Linearization.create(this.stream);
} catch (err) {
if (err instanceof MissingDataException) {
throw err;
}
info(err);
}
}
// shadow the prototype getter with a data property
return shadow(this, 'linearization', linearization);
},
get startXRef() {
var stream = this.stream;
var startXRef = 0;
var linearization = this.linearization;
if (linearization) {
// Find end of first obj.
stream.reset();
if (find(stream, 'endobj', 1024)) {
startXRef = stream.pos + 6;
}
} else {
// Find startxref by jumping backward from the end of the file.
var step = 1024;
var found = false, pos = stream.end;
while (!found && pos > 0) {
pos -= step - 'startxref'.length;
if (pos < 0) {
pos = 0;
}
stream.pos = pos;
found = find(stream, 'startxref', step, true);
}
if (found) {
stream.skip(9);
var ch;
do {
ch = stream.getByte();
} while (Lexer.isSpace(ch));
var str = '';
while (ch >= 0x20 && ch <= 0x39) { // < '9'
str += String.fromCharCode(ch);
ch = stream.getByte();
}
startXRef = parseInt(str, 10);
if (isNaN(startXRef)) {
startXRef = 0;
}
}
}
// shadow the prototype getter with a data property
return shadow(this, 'startXRef', startXRef);
},
get mainXRefEntriesOffset() {
var mainXRefEntriesOffset = 0;
var linearization = this.linearization;
if (linearization) {
mainXRefEntriesOffset = linearization.mainXRefEntriesOffset;
}
// shadow the prototype getter with a data property
return shadow(this, 'mainXRefEntriesOffset', mainXRefEntriesOffset);
},
// Find the header, remove leading garbage and setup the stream
// starting from the header.
checkHeader: function PDFDocument_checkHeader() {
var stream = this.stream;
stream.reset();
if (find(stream, '%PDF-', 1024)) {
// Found the header, trim off any garbage before it.
stream.moveStart();
// Reading file format version
var MAX_VERSION_LENGTH = 12;
var version = '', ch;
while ((ch = stream.getByte()) > 0x20) { // SPACE
if (version.length >= MAX_VERSION_LENGTH) {
break;
}
version += String.fromCharCode(ch);
}
// removing "%PDF-"-prefix
this.pdfFormatVersion = version.substring(5);
return;
}
// May not be a PDF file, continue anyway.
},
parseStartXRef: function PDFDocument_parseStartXRef() {
var startXRef = this.startXRef;
this.xref.setStartXRef(startXRef);
},
setup: function PDFDocument_setup(recoveryMode) {
this.xref.parse(recoveryMode);
this.catalog = new Catalog(this.pdfManager, this.xref);
},
get numPages() {
var linearization = this.linearization;
var num = linearization ? linearization.numPages : this.catalog.numPages;
// shadow the prototype getter
return shadow(this, 'numPages', num);
},
get documentInfo() {
var docInfo = {
PDFFormatVersion: this.pdfFormatVersion,
IsAcroFormPresent: !!this.acroForm,
IsXFAPresent: !!this.xfa
};
var infoDict;
try {
infoDict = this.xref.trailer.get('Info');
} catch (err) {
info('The document information dictionary is invalid.');
}
if (infoDict) {
var validEntries = DocumentInfoValidators.entries;
// Only fill the document info with valid entries from the spec.
for (var key in validEntries) {
if (infoDict.has(key)) {
var value = infoDict.get(key);
// Make sure the value conforms to the spec.
if (validEntries[key](value)) {
docInfo[key] = (typeof value !== 'string' ?
value : stringToPDFString(value));
} else {
info('Bad value in document info for "' + key + '"');
}
}
}
}
return shadow(this, 'documentInfo', docInfo);
},
get fingerprint() {
var xref = this.xref, hash, fileID = '';
if (xref.trailer.has('ID')) {
hash = stringToBytes(xref.trailer.get('ID')[0]);
} else {
hash = calculateMD5(this.stream.bytes.subarray(0, 100), 0, 100);
}
for (var i = 0, n = hash.length; i < n; i++) {
fileID += hash[i].toString(16);
}
return shadow(this, 'fingerprint', fileID);
},
getPage: function PDFDocument_getPage(pageIndex) {
return this.catalog.getPage(pageIndex);
},
cleanup: function PDFDocument_cleanup() {
return this.catalog.cleanup();
}
};
return PDFDocument;
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,713 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals error, bytesToString, Stream, GlyphsUnicode, CFFParser, Encodings,
Util */
'use strict';
var FontRendererFactory = (function FontRendererFactoryClosure() {
function getLong(data, offset) {
return (data[offset] << 24) | (data[offset + 1] << 16) |
(data[offset + 2] << 8) | data[offset + 3];
}
function getUshort(data, offset) {
return (data[offset] << 8) | data[offset + 1];
}
function parseCmap(data, start, end) {
var offset = (getUshort(data, start + 2) === 1 ?
getLong(data, start + 8) : getLong(data, start + 16));
var format = getUshort(data, start + offset);
var length, ranges, p, i;
if (format === 4) {
length = getUshort(data, start + offset + 2);
var segCount = getUshort(data, start + offset + 6) >> 1;
p = start + offset + 14;
ranges = [];
for (i = 0; i < segCount; i++, p += 2) {
ranges[i] = {end: getUshort(data, p)};
}
p += 2;
for (i = 0; i < segCount; i++, p += 2) {
ranges[i].start = getUshort(data, p);
}
for (i = 0; i < segCount; i++, p += 2) {
ranges[i].idDelta = getUshort(data, p);
}
for (i = 0; i < segCount; i++, p += 2) {
var idOffset = getUshort(data, p);
if (idOffset === 0) {
continue;
}
ranges[i].ids = [];
for (var j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {
ranges[i].ids[j] = getUshort(data, p + idOffset);
idOffset += 2;
}
}
return ranges;
} else if (format === 12) {
length = getLong(data, start + offset + 4);
var groups = getLong(data, start + offset + 12);
p = start + offset + 16;
ranges = [];
for (i = 0; i < groups; i++) {
ranges.push({
start: getLong(data, p),
end: getLong(data, p + 4),
idDelta: getLong(data, p + 8) - getLong(data, p)
});
p += 12;
}
return ranges;
}
error('not supported cmap: ' + format);
}
function parseCff(data, start, end) {
var properties = {};
var parser = new CFFParser(new Stream(data, start, end - start),
properties);
var cff = parser.parse();
return {
glyphs: cff.charStrings.objects,
subrs: (cff.topDict.privateDict && cff.topDict.privateDict.subrsIndex &&
cff.topDict.privateDict.subrsIndex.objects),
gsubrs: cff.globalSubrIndex && cff.globalSubrIndex.objects
};
}
function parseGlyfTable(glyf, loca, isGlyphLocationsLong) {
var itemSize, itemDecode;
if (isGlyphLocationsLong) {
itemSize = 4;
itemDecode = function fontItemDecodeLong(data, offset) {
return (data[offset] << 24) | (data[offset + 1] << 16) |
(data[offset + 2] << 8) | data[offset + 3];
};
} else {
itemSize = 2;
itemDecode = function fontItemDecode(data, offset) {
return (data[offset] << 9) | (data[offset + 1] << 1);
};
}
var glyphs = [];
var startOffset = itemDecode(loca, 0);
for (var j = itemSize; j < loca.length; j += itemSize) {
var endOffset = itemDecode(loca, j);
glyphs.push(glyf.subarray(startOffset, endOffset));
startOffset = endOffset;
}
return glyphs;
}
function lookupCmap(ranges, unicode) {
var code = unicode.charCodeAt(0);
var l = 0, r = ranges.length - 1;
while (l < r) {
var c = (l + r + 1) >> 1;
if (code < ranges[c].start) {
r = c - 1;
} else {
l = c;
}
}
if (ranges[l].start <= code && code <= ranges[l].end) {
return (ranges[l].idDelta + (ranges[l].ids ?
ranges[l].ids[code - ranges[l].start] : code)) & 0xFFFF;
}
return 0;
}
function compileGlyf(code, js, font) {
function moveTo(x, y) {
js.push('c.moveTo(' + x + ',' + y + ');');
}
function lineTo(x, y) {
js.push('c.lineTo(' + x + ',' + y + ');');
}
function quadraticCurveTo(xa, ya, x, y) {
js.push('c.quadraticCurveTo(' + xa + ',' + ya + ',' +
x + ',' + y + ');');
}
var i = 0;
var numberOfContours = ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
var flags;
var x = 0, y = 0;
i += 10;
if (numberOfContours < 0) {
// composite glyph
do {
flags = (code[i] << 8) | code[i + 1];
var glyphIndex = (code[i + 2] << 8) | code[i + 3];
i += 4;
var arg1, arg2;
if ((flags & 0x01)) {
arg1 = ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
arg2 = ((code[i + 2] << 24) | (code[i + 3] << 16)) >> 16;
i += 4;
} else {
arg1 = code[i++]; arg2 = code[i++];
}
if ((flags & 0x02)) {
x = arg1;
y = arg2;
} else {
x = 0; y = 0; // TODO "they are points" ?
}
var scaleX = 1, scaleY = 1, scale01 = 0, scale10 = 0;
if ((flags & 0x08)) {
scaleX =
scaleY = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824;
i += 2;
} else if ((flags & 0x40)) {
scaleX = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824;
scaleY = ((code[i + 2] << 24) | (code[i + 3] << 16)) / 1073741824;
i += 4;
} else if ((flags & 0x80)) {
scaleX = ((code[i] << 24) | (code[i + 1] << 16)) / 1073741824;
scale01 = ((code[i + 2] << 24) | (code[i + 3] << 16)) / 1073741824;
scale10 = ((code[i + 4] << 24) | (code[i + 5] << 16)) / 1073741824;
scaleY = ((code[i + 6] << 24) | (code[i + 7] << 16)) / 1073741824;
i += 8;
}
var subglyph = font.glyphs[glyphIndex];
if (subglyph) {
js.push('c.save();');
js.push('c.transform(' + scaleX + ',' + scale01 + ',' +
scale10 + ',' + scaleY + ',' + x + ',' + y + ');');
compileGlyf(subglyph, js, font);
js.push('c.restore();');
}
} while ((flags & 0x20));
} else {
// simple glyph
var endPtsOfContours = [];
var j, jj;
for (j = 0; j < numberOfContours; j++) {
endPtsOfContours.push((code[i] << 8) | code[i + 1]);
i += 2;
}
var instructionLength = (code[i] << 8) | code[i + 1];
i += 2 + instructionLength; // skipping the instructions
var numberOfPoints = endPtsOfContours[endPtsOfContours.length - 1] + 1;
var points = [];
while (points.length < numberOfPoints) {
flags = code[i++];
var repeat = 1;
if ((flags & 0x08)) {
repeat += code[i++];
}
while (repeat-- > 0) {
points.push({flags: flags});
}
}
for (j = 0; j < numberOfPoints; j++) {
switch (points[j].flags & 0x12) {
case 0x00:
x += ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
i += 2;
break;
case 0x02:
x -= code[i++];
break;
case 0x12:
x += code[i++];
break;
}
points[j].x = x;
}
for (j = 0; j < numberOfPoints; j++) {
switch (points[j].flags & 0x24) {
case 0x00:
y += ((code[i] << 24) | (code[i + 1] << 16)) >> 16;
i += 2;
break;
case 0x04:
y -= code[i++];
break;
case 0x24:
y += code[i++];
break;
}
points[j].y = y;
}
var startPoint = 0;
for (i = 0; i < numberOfContours; i++) {
var endPoint = endPtsOfContours[i];
// contours might have implicit points, which is located in the middle
// between two neighboring off-curve points
var contour = points.slice(startPoint, endPoint + 1);
if ((contour[0].flags & 1)) {
contour.push(contour[0]); // using start point at the contour end
} else if ((contour[contour.length - 1].flags & 1)) {
// first is off-curve point, trying to use one from the end
contour.unshift(contour[contour.length - 1]);
} else {
// start and end are off-curve points, creating implicit one
var p = {
flags: 1,
x: (contour[0].x + contour[contour.length - 1].x) / 2,
y: (contour[0].y + contour[contour.length - 1].y) / 2
};
contour.unshift(p);
contour.push(p);
}
moveTo(contour[0].x, contour[0].y);
for (j = 1, jj = contour.length; j < jj; j++) {
if ((contour[j].flags & 1)) {
lineTo(contour[j].x, contour[j].y);
} else if ((contour[j + 1].flags & 1)){
quadraticCurveTo(contour[j].x, contour[j].y,
contour[j + 1].x, contour[j + 1].y);
j++;
} else {
quadraticCurveTo(contour[j].x, contour[j].y,
(contour[j].x + contour[j + 1].x) / 2,
(contour[j].y + contour[j + 1].y) / 2);
}
}
startPoint = endPoint + 1;
}
}
}
function compileCharString(code, js, font) {
var stack = [];
var x = 0, y = 0;
var stems = 0;
function moveTo(x, y) {
js.push('c.moveTo(' + x + ',' + y + ');');
}
function lineTo(x, y) {
js.push('c.lineTo(' + x + ',' + y + ');');
}
function bezierCurveTo(x1, y1, x2, y2, x, y) {
js.push('c.bezierCurveTo(' + x1 + ',' + y1 + ',' + x2 + ',' + y2 + ',' +
x + ',' + y + ');');
}
function parse(code) {
var i = 0;
while (i < code.length) {
var stackClean = false;
var v = code[i++];
var xa, xb, ya, yb, y1, y2, y3, n, subrCode;
switch (v) {
case 1: // hstem
stems += stack.length >> 1;
stackClean = true;
break;
case 3: // vstem
stems += stack.length >> 1;
stackClean = true;
break;
case 4: // vmoveto
y += stack.pop();
moveTo(x, y);
stackClean = true;
break;
case 5: // rlineto
while (stack.length > 0) {
x += stack.shift();
y += stack.shift();
lineTo(x, y);
}
break;
case 6: // hlineto
while (stack.length > 0) {
x += stack.shift();
lineTo(x, y);
if (stack.length === 0) {
break;
}
y += stack.shift();
lineTo(x, y);
}
break;
case 7: // vlineto
while (stack.length > 0) {
y += stack.shift();
lineTo(x, y);
if (stack.length === 0) {
break;
}
x += stack.shift();
lineTo(x, y);
}
break;
case 8: // rrcurveto
while (stack.length > 0) {
xa = x + stack.shift(); ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift(); y = yb + stack.shift();
bezierCurveTo(xa, ya, xb, yb, x, y);
}
break;
case 10: // callsubr
n = stack.pop() + font.subrsBias;
subrCode = font.subrs[n];
if (subrCode) {
parse(subrCode);
}
break;
case 11: // return
return;
case 12:
v = code[i++];
switch (v) {
case 34: // flex
xa = x + stack.shift();
xb = xa + stack.shift(); y1 = y + stack.shift();
x = xb + stack.shift();
bezierCurveTo(xa, y, xb, y1, x, y1);
xa = x + stack.shift();
xb = xa + stack.shift();
x = xb + stack.shift();
bezierCurveTo(xa, y1, xb, y, x, y);
break;
case 35: // flex
xa = x + stack.shift(); ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift(); y = yb + stack.shift();
bezierCurveTo(xa, ya, xb, yb, x, y);
xa = x + stack.shift(); ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift(); y = yb + stack.shift();
bezierCurveTo(xa, ya, xb, yb, x, y);
stack.pop(); // fd
break;
case 36: // hflex1
xa = x + stack.shift(); y1 = y + stack.shift();
xb = xa + stack.shift(); y2 = y1 + stack.shift();
x = xb + stack.shift();
bezierCurveTo(xa, y1, xb, y2, x, y2);
xa = x + stack.shift();
xb = xa + stack.shift(); y3 = y2 + stack.shift();
x = xb + stack.shift();
bezierCurveTo(xa, y2, xb, y3, x, y);
break;
case 37: // flex1
var x0 = x, y0 = y;
xa = x + stack.shift(); ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift(); y = yb + stack.shift();
bezierCurveTo(xa, ya, xb, yb, x, y);
xa = x + stack.shift(); ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb; y = yb;
if (Math.abs(x - x0) > Math.abs(y - y0)) {
x += stack.shift();
} else {
y += stack.shift();
}
bezierCurveTo(xa, ya, xb, yb, x, y);
break;
default:
error('unknown operator: 12 ' + v);
}
break;
case 14: // endchar
if (stack.length >= 4) {
var achar = stack.pop();
var bchar = stack.pop();
y = stack.pop();
x = stack.pop();
js.push('c.save();');
js.push('c.translate('+ x + ',' + y + ');');
var gid = lookupCmap(font.cmap, String.fromCharCode(
font.glyphNameMap[Encodings.StandardEncoding[achar]]));
compileCharString(font.glyphs[gid], js, font);
js.push('c.restore();');
gid = lookupCmap(font.cmap, String.fromCharCode(
font.glyphNameMap[Encodings.StandardEncoding[bchar]]));
compileCharString(font.glyphs[gid], js, font);
}
return;
case 18: // hstemhm
stems += stack.length >> 1;
stackClean = true;
break;
case 19: // hintmask
stems += stack.length >> 1;
i += (stems + 7) >> 3;
stackClean = true;
break;
case 20: // cntrmask
stems += stack.length >> 1;
i += (stems + 7) >> 3;
stackClean = true;
break;
case 21: // rmoveto
y += stack.pop();
x += stack.pop();
moveTo(x, y);
stackClean = true;
break;
case 22: // hmoveto
x += stack.pop();
moveTo(x, y);
stackClean = true;
break;
case 23: // vstemhm
stems += stack.length >> 1;
stackClean = true;
break;
case 24: // rcurveline
while (stack.length > 2) {
xa = x + stack.shift(); ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift(); y = yb + stack.shift();
bezierCurveTo(xa, ya, xb, yb, x, y);
}
x += stack.shift();
y += stack.shift();
lineTo(x, y);
break;
case 25: // rlinecurve
while (stack.length > 6) {
x += stack.shift();
y += stack.shift();
lineTo(x, y);
}
xa = x + stack.shift(); ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift(); y = yb + stack.shift();
bezierCurveTo(xa, ya, xb, yb, x, y);
break;
case 26: // vvcurveto
if (stack.length % 2) {
x += stack.shift();
}
while (stack.length > 0) {
xa = x; ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb; y = yb + stack.shift();
bezierCurveTo(xa, ya, xb, yb, x, y);
}
break;
case 27: // hhcurveto
if (stack.length % 2) {
y += stack.shift();
}
while (stack.length > 0) {
xa = x + stack.shift(); ya = y;
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift(); y = yb;
bezierCurveTo(xa, ya, xb, yb, x, y);
}
break;
case 28:
stack.push(((code[i] << 24) | (code[i + 1] << 16)) >> 16);
i += 2;
break;
case 29: // callgsubr
n = stack.pop() + font.gsubrsBias;
subrCode = font.gsubrs[n];
if (subrCode) {
parse(subrCode);
}
break;
case 30: // vhcurveto
while (stack.length > 0) {
xa = x; ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift();
y = yb + (stack.length === 1 ? stack.shift() : 0);
bezierCurveTo(xa, ya, xb, yb, x, y);
if (stack.length === 0) {
break;
}
xa = x + stack.shift(); ya = y;
xb = xa + stack.shift(); yb = ya + stack.shift();
y = yb + stack.shift();
x = xb + (stack.length === 1 ? stack.shift() : 0);
bezierCurveTo(xa, ya, xb, yb, x, y);
}
break;
case 31: // hvcurveto
while (stack.length > 0) {
xa = x + stack.shift(); ya = y;
xb = xa + stack.shift(); yb = ya + stack.shift();
y = yb + stack.shift();
x = xb + (stack.length === 1 ? stack.shift() : 0);
bezierCurveTo(xa, ya, xb, yb, x, y);
if (stack.length === 0) {
break;
}
xa = x; ya = y + stack.shift();
xb = xa + stack.shift(); yb = ya + stack.shift();
x = xb + stack.shift();
y = yb + (stack.length === 1 ? stack.shift() : 0);
bezierCurveTo(xa, ya, xb, yb, x, y);
}
break;
default:
if (v < 32) {
error('unknown operator: ' + v);
}
if (v < 247) {
stack.push(v - 139);
} else if (v < 251) {
stack.push((v - 247) * 256 + code[i++] + 108);
} else if (v < 255) {
stack.push(-(v - 251) * 256 - code[i++] - 108);
} else {
stack.push(((code[i] << 24) | (code[i + 1] << 16) |
(code[i + 2] << 8) | code[i + 3]) / 65536);
i += 4;
}
break;
}
if (stackClean) {
stack.length = 0;
}
}
}
parse(code);
}
var noop = '';
function CompiledFont(fontMatrix) {
this.compiledGlyphs = {};
this.fontMatrix = fontMatrix;
}
CompiledFont.prototype = {
getPathJs: function (unicode) {
var gid = lookupCmap(this.cmap, unicode);
var fn = this.compiledGlyphs[gid];
if (!fn) {
this.compiledGlyphs[gid] = fn = this.compileGlyph(this.glyphs[gid]);
}
return fn;
},
compileGlyph: function (code) {
if (!code || code.length === 0 || code[0] === 14) {
return noop;
}
var js = [];
js.push('c.save();');
js.push('c.transform(' + this.fontMatrix.join(',') + ');');
js.push('c.scale(size, -size);');
this.compileGlyphImpl(code, js);
js.push('c.restore();');
return js.join('\n');
},
compileGlyphImpl: function () {
error('Children classes should implement this.');
},
hasBuiltPath: function (unicode) {
var gid = lookupCmap(this.cmap, unicode);
return gid in this.compiledGlyphs;
}
};
function TrueTypeCompiled(glyphs, cmap, fontMatrix) {
fontMatrix = fontMatrix || [0.000488, 0, 0, 0.000488, 0, 0];
CompiledFont.call(this, fontMatrix);
this.glyphs = glyphs;
this.cmap = cmap;
this.compiledGlyphs = [];
}
Util.inherit(TrueTypeCompiled, CompiledFont, {
compileGlyphImpl: function (code, js) {
compileGlyf(code, js, this);
}
});
function Type2Compiled(cffInfo, cmap, fontMatrix, glyphNameMap) {
fontMatrix = fontMatrix || [0.001, 0, 0, 0.001, 0, 0];
CompiledFont.call(this, fontMatrix);
this.glyphs = cffInfo.glyphs;
this.gsubrs = cffInfo.gsubrs || [];
this.subrs = cffInfo.subrs || [];
this.cmap = cmap;
this.glyphNameMap = glyphNameMap || GlyphsUnicode;
this.compiledGlyphs = [];
this.gsubrsBias = (this.gsubrs.length < 1240 ?
107 : (this.gsubrs.length < 33900 ? 1131 : 32768));
this.subrsBias = (this.subrs.length < 1240 ?
107 : (this.subrs.length < 33900 ? 1131 : 32768));
}
Util.inherit(Type2Compiled, CompiledFont, {
compileGlyphImpl: function (code, js) {
compileCharString(code, js, this);
}
});
return {
create: function FontRendererFactory_create(font) {
var data = new Uint8Array(font.data);
var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
var numTables = getUshort(data, 4);
for (var i = 0, p = 12; i < numTables; i++, p += 16) {
var tag = bytesToString(data.subarray(p, p + 4));
var offset = getLong(data, p + 8);
var length = getLong(data, p + 12);
switch (tag) {
case 'cmap':
cmap = parseCmap(data, offset, offset + length);
break;
case 'glyf':
glyf = data.subarray(offset, offset + length);
break;
case 'loca':
loca = data.subarray(offset, offset + length);
break;
case 'head':
unitsPerEm = getUshort(data, offset + 18);
indexToLocFormat = getUshort(data, offset + 50);
break;
case 'CFF ':
cff = parseCff(data, offset, offset + length);
break;
}
}
if (glyf) {
var fontMatrix = (!unitsPerEm ? font.fontMatrix :
[1 / unitsPerEm, 0, 0, 1 / unitsPerEm, 0, 0]);
return new TrueTypeCompiled(
parseGlyfTable(glyf, loca, indexToLocFormat), cmap, fontMatrix);
} else {
return new Type2Compiled(cff, cmap, font.fontMatrix, font.glyphNameMap);
}
}
};
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,668 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals assert, ColorSpace, DecodeStream, error, info, isArray, ImageKind,
isStream, JpegStream, JpxImage, Name, Promise, Stream, warn */
'use strict';
var PDFImage = (function PDFImageClosure() {
/**
* Decode the image in the main thread if it supported. Resovles the promise
* when the image data is ready.
*/
function handleImageData(handler, xref, res, image) {
if (image instanceof JpegStream && image.isNativelyDecodable(xref, res)) {
// For natively supported jpegs send them to the main thread for decoding.
var dict = image.dict;
var colorSpace = dict.get('ColorSpace', 'CS');
colorSpace = ColorSpace.parse(colorSpace, xref, res);
var numComps = colorSpace.numComps;
var decodePromise = handler.sendWithPromise('JpegDecode',
[image.getIR(), numComps]);
return decodePromise.then(function (message) {
var data = message.data;
return new Stream(data, 0, data.length, image.dict);
});
} else {
return Promise.resolve(image);
}
}
/**
* Decode and clamp a value. The formula is different from the spec because we
* don't decode to float range [0,1], we decode it in the [0,max] range.
*/
function decodeAndClamp(value, addend, coefficient, max) {
value = addend + value * coefficient;
// Clamp the value to the range
return (value < 0 ? 0 : (value > max ? max : value));
}
function PDFImage(xref, res, image, inline, smask, mask, isMask) {
this.image = image;
var dict = image.dict;
if (dict.has('Filter')) {
var filter = dict.get('Filter').name;
if (filter === 'JPXDecode') {
var jpxImage = new JpxImage();
jpxImage.parseImageProperties(image.stream);
image.stream.reset();
image.bitsPerComponent = jpxImage.bitsPerComponent;
image.numComps = jpxImage.componentsCount;
} else if (filter === 'JBIG2Decode') {
image.bitsPerComponent = 1;
image.numComps = 1;
}
}
// TODO cache rendered images?
this.width = dict.get('Width', 'W');
this.height = dict.get('Height', 'H');
if (this.width < 1 || this.height < 1) {
error('Invalid image width: ' + this.width + ' or height: ' +
this.height);
}
this.interpolate = dict.get('Interpolate', 'I') || false;
this.imageMask = dict.get('ImageMask', 'IM') || false;
this.matte = dict.get('Matte') || false;
var bitsPerComponent = image.bitsPerComponent;
if (!bitsPerComponent) {
bitsPerComponent = dict.get('BitsPerComponent', 'BPC');
if (!bitsPerComponent) {
if (this.imageMask) {
bitsPerComponent = 1;
} else {
error('Bits per component missing in image: ' + this.imageMask);
}
}
}
this.bpc = bitsPerComponent;
if (!this.imageMask) {
var colorSpace = dict.get('ColorSpace', 'CS');
if (!colorSpace) {
info('JPX images (which do not require color spaces)');
switch (image.numComps) {
case 1:
colorSpace = Name.get('DeviceGray');
break;
case 3:
colorSpace = Name.get('DeviceRGB');
break;
case 4:
colorSpace = Name.get('DeviceCMYK');
break;
default:
error('JPX images with ' + this.numComps +
' color components not supported.');
}
}
this.colorSpace = ColorSpace.parse(colorSpace, xref, res);
this.numComps = this.colorSpace.numComps;
}
this.decode = dict.get('Decode', 'D');
this.needsDecode = false;
if (this.decode &&
((this.colorSpace && !this.colorSpace.isDefaultDecode(this.decode)) ||
(isMask && !ColorSpace.isDefaultDecode(this.decode, 1)))) {
this.needsDecode = true;
// Do some preprocessing to avoid more math.
var max = (1 << bitsPerComponent) - 1;
this.decodeCoefficients = [];
this.decodeAddends = [];
for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
var dmin = this.decode[i];
var dmax = this.decode[i + 1];
this.decodeCoefficients[j] = dmax - dmin;
this.decodeAddends[j] = max * dmin;
}
}
if (smask) {
this.smask = new PDFImage(xref, res, smask, false);
} else if (mask) {
if (isStream(mask)) {
this.mask = new PDFImage(xref, res, mask, false, null, null, true);
} else {
// Color key mask (just an array).
this.mask = mask;
}
}
}
/**
* Handles processing of image data and returns the Promise that is resolved
* with a PDFImage when the image is ready to be used.
*/
PDFImage.buildImage = function PDFImage_buildImage(handler, xref,
res, image, inline) {
var imagePromise = handleImageData(handler, xref, res, image);
var smaskPromise;
var maskPromise;
var smask = image.dict.get('SMask');
var mask = image.dict.get('Mask');
if (smask) {
smaskPromise = handleImageData(handler, xref, res, smask);
maskPromise = Promise.resolve(null);
} else {
smaskPromise = Promise.resolve(null);
if (mask) {
if (isStream(mask)) {
maskPromise = handleImageData(handler, xref, res, mask);
} else if (isArray(mask)) {
maskPromise = Promise.resolve(mask);
} else {
warn('Unsupported mask format.');
maskPromise = Promise.resolve(null);
}
} else {
maskPromise = Promise.resolve(null);
}
}
return Promise.all([imagePromise, smaskPromise, maskPromise]).then(
function(results) {
var imageData = results[0];
var smaskData = results[1];
var maskData = results[2];
return new PDFImage(xref, res, imageData, inline, smaskData, maskData);
});
};
/**
* Resize an image using the nearest neighbor algorithm. Currently only
* supports one and three component images.
* @param {TypedArray} pixels The original image with one component.
* @param {Number} bpc Number of bits per component.
* @param {Number} components Number of color components, 1 or 3 is supported.
* @param {Number} w1 Original width.
* @param {Number} h1 Original height.
* @param {Number} w2 New width.
* @param {Number} h2 New height.
* @param {TypedArray} dest (Optional) The destination buffer.
* @param {Number} alpha01 (Optional) Size reserved for the alpha channel.
* @return {TypedArray} Resized image data.
*/
PDFImage.resize = function PDFImage_resize(pixels, bpc, components,
w1, h1, w2, h2, dest, alpha01) {
if (components !== 1 && components !== 3) {
error('Unsupported component count for resizing.');
}
var length = w2 * h2 * components;
var temp = dest ? dest : (bpc <= 8 ? new Uint8Array(length) :
(bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length)));
var xRatio = w1 / w2;
var yRatio = h1 / h2;
var i, j, py, newIndex = 0, oldIndex;
var xScaled = new Uint16Array(w2);
var w1Scanline = w1 * components;
if (alpha01 !== 1) {
alpha01 = 0;
}
for (j = 0; j < w2; j++) {
xScaled[j] = Math.floor(j * xRatio) * components;
}
if (components === 1) {
for (i = 0; i < h2; i++) {
py = Math.floor(i * yRatio) * w1Scanline;
for (j = 0; j < w2; j++) {
oldIndex = py + xScaled[j];
temp[newIndex++] = pixels[oldIndex];
}
}
} else if (components === 3) {
for (i = 0; i < h2; i++) {
py = Math.floor(i * yRatio) * w1Scanline;
for (j = 0; j < w2; j++) {
oldIndex = py + xScaled[j];
temp[newIndex++] = pixels[oldIndex++];
temp[newIndex++] = pixels[oldIndex++];
temp[newIndex++] = pixels[oldIndex++];
newIndex += alpha01;
}
}
}
return temp;
};
PDFImage.createMask =
function PDFImage_createMask(imgArray, width, height,
imageIsFromDecodeStream, inverseDecode) {
// |imgArray| might not contain full data for every pixel of the mask, so
// we need to distinguish between |computedLength| and |actualLength|.
// In particular, if inverseDecode is true, then the array we return must
// have a length of |computedLength|.
var computedLength = ((width + 7) >> 3) * height;
var actualLength = imgArray.byteLength;
var haveFullData = computedLength === actualLength;
var data, i;
if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) {
// imgArray came from a DecodeStream and its data is in an appropriate
// form, so we can just transfer it.
data = imgArray;
} else if (!inverseDecode) {
data = new Uint8Array(actualLength);
data.set(imgArray);
} else {
data = new Uint8Array(computedLength);
data.set(imgArray);
for (i = actualLength; i < computedLength; i++) {
data[i] = 0xff;
}
}
// If necessary, invert the original mask data (but not any extra we might
// have added above). It's safe to modify the array -- whether it's the
// original or a copy, we're about to transfer it anyway, so nothing else
// in this thread can be relying on its contents.
if (inverseDecode) {
for (i = 0; i < actualLength; i++) {
data[i] = ~data[i];
}
}
return {data: data, width: width, height: height};
};
PDFImage.prototype = {
get drawWidth() {
return Math.max(this.width,
this.smask && this.smask.width || 0,
this.mask && this.mask.width || 0);
},
get drawHeight() {
return Math.max(this.height,
this.smask && this.smask.height || 0,
this.mask && this.mask.height || 0);
},
decodeBuffer: function PDFImage_decodeBuffer(buffer) {
var bpc = this.bpc;
var numComps = this.numComps;
var decodeAddends = this.decodeAddends;
var decodeCoefficients = this.decodeCoefficients;
var max = (1 << bpc) - 1;
var i, ii;
if (bpc === 1) {
// If the buffer needed decode that means it just needs to be inverted.
for (i = 0, ii = buffer.length; i < ii; i++) {
buffer[i] = +!(buffer[i]);
}
return;
}
var index = 0;
for (i = 0, ii = this.width * this.height; i < ii; i++) {
for (var j = 0; j < numComps; j++) {
buffer[index] = decodeAndClamp(buffer[index], decodeAddends[j],
decodeCoefficients[j], max);
index++;
}
}
},
getComponents: function PDFImage_getComponents(buffer) {
var bpc = this.bpc;
// This image doesn't require any extra work.
if (bpc === 8) {
return buffer;
}
var width = this.width;
var height = this.height;
var numComps = this.numComps;
var length = width * height * numComps;
var bufferPos = 0;
var output = (bpc <= 8 ? new Uint8Array(length) :
(bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length)));
var rowComps = width * numComps;
var max = (1 << bpc) - 1;
var i = 0, ii, buf;
if (bpc === 1) {
// Optimization for reading 1 bpc images.
var mask, loop1End, loop2End;
for (var j = 0; j < height; j++) {
loop1End = i + (rowComps & ~7);
loop2End = i + rowComps;
// unroll loop for all full bytes
while (i < loop1End) {
buf = buffer[bufferPos++];
output[i] = (buf >> 7) & 1;
output[i + 1] = (buf >> 6) & 1;
output[i + 2] = (buf >> 5) & 1;
output[i + 3] = (buf >> 4) & 1;
output[i + 4] = (buf >> 3) & 1;
output[i + 5] = (buf >> 2) & 1;
output[i + 6] = (buf >> 1) & 1;
output[i + 7] = buf & 1;
i += 8;
}
// handle remaing bits
if (i < loop2End) {
buf = buffer[bufferPos++];
mask = 128;
while (i < loop2End) {
output[i++] = +!!(buf & mask);
mask >>= 1;
}
}
}
} else {
// The general case that handles all other bpc values.
var bits = 0;
buf = 0;
for (i = 0, ii = length; i < ii; ++i) {
if (i % rowComps === 0) {
buf = 0;
bits = 0;
}
while (bits < bpc) {
buf = (buf << 8) | buffer[bufferPos++];
bits += 8;
}
var remainingBits = bits - bpc;
var value = buf >> remainingBits;
output[i] = (value < 0 ? 0 : (value > max ? max : value));
buf = buf & ((1 << remainingBits) - 1);
bits = remainingBits;
}
}
return output;
},
fillOpacity: function PDFImage_fillOpacity(rgbaBuf, width, height,
actualHeight, image) {
var smask = this.smask;
var mask = this.mask;
var alphaBuf, sw, sh, i, ii, j;
if (smask) {
sw = smask.width;
sh = smask.height;
alphaBuf = new Uint8Array(sw * sh);
smask.fillGrayBuffer(alphaBuf);
if (sw !== width || sh !== height) {
alphaBuf = PDFImage.resize(alphaBuf, smask.bpc, 1, sw, sh, width,
height);
}
} else if (mask) {
if (mask instanceof PDFImage) {
sw = mask.width;
sh = mask.height;
alphaBuf = new Uint8Array(sw * sh);
mask.numComps = 1;
mask.fillGrayBuffer(alphaBuf);
// Need to invert values in rgbaBuf
for (i = 0, ii = sw * sh; i < ii; ++i) {
alphaBuf[i] = 255 - alphaBuf[i];
}
if (sw !== width || sh !== height) {
alphaBuf = PDFImage.resize(alphaBuf, mask.bpc, 1, sw, sh, width,
height);
}
} else if (isArray(mask)) {
// Color key mask: if any of the compontents are outside the range
// then they should be painted.
alphaBuf = new Uint8Array(width * height);
var numComps = this.numComps;
for (i = 0, ii = width * height; i < ii; ++i) {
var opacity = 0;
var imageOffset = i * numComps;
for (j = 0; j < numComps; ++j) {
var color = image[imageOffset + j];
var maskOffset = j * 2;
if (color < mask[maskOffset] || color > mask[maskOffset + 1]) {
opacity = 255;
break;
}
}
alphaBuf[i] = opacity;
}
} else {
error('Unknown mask format.');
}
}
if (alphaBuf) {
for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
rgbaBuf[j] = alphaBuf[i];
}
} else {
// No mask.
for (i = 0, j = 3, ii = width * actualHeight; i < ii; ++i, j += 4) {
rgbaBuf[j] = 255;
}
}
},
undoPreblend: function PDFImage_undoPreblend(buffer, width, height) {
var matte = this.smask && this.smask.matte;
if (!matte) {
return;
}
var matteRgb = this.colorSpace.getRgb(matte, 0);
var matteR = matteRgb[0];
var matteG = matteRgb[1];
var matteB = matteRgb[2];
var length = width * height * 4;
var r, g, b;
for (var i = 0; i < length; i += 4) {
var alpha = buffer[i + 3];
if (alpha === 0) {
// according formula we have to get Infinity in all components
// making it white (typical paper color) should be okay
buffer[i] = 255;
buffer[i + 1] = 255;
buffer[i + 2] = 255;
continue;
}
var k = 255 / alpha;
r = (buffer[i] - matteR) * k + matteR;
g = (buffer[i + 1] - matteG) * k + matteG;
b = (buffer[i + 2] - matteB) * k + matteB;
buffer[i] = r <= 0 ? 0 : r >= 255 ? 255 : r | 0;
buffer[i + 1] = g <= 0 ? 0 : g >= 255 ? 255 : g | 0;
buffer[i + 2] = b <= 0 ? 0 : b >= 255 ? 255 : b | 0;
}
},
createImageData: function PDFImage_createImageData(forceRGBA) {
var drawWidth = this.drawWidth;
var drawHeight = this.drawHeight;
var imgData = { // other fields are filled in below
width: drawWidth,
height: drawHeight
};
var numComps = this.numComps;
var originalWidth = this.width;
var originalHeight = this.height;
var bpc = this.bpc;
// Rows start at byte boundary.
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
var imgArray;
if (!forceRGBA) {
// If it is a 1-bit-per-pixel grayscale (i.e. black-and-white) image
// without any complications, we pass a same-sized copy to the main
// thread rather than expanding by 32x to RGBA form. This saves *lots*
// of memory for many scanned documents. It's also much faster.
//
// Similarly, if it is a 24-bit-per pixel RGB image without any
// complications, we avoid expanding by 1.333x to RGBA form.
var kind;
if (this.colorSpace.name === 'DeviceGray' && bpc === 1) {
kind = ImageKind.GRAYSCALE_1BPP;
} else if (this.colorSpace.name === 'DeviceRGB' && bpc === 8 &&
!this.needsDecode) {
kind = ImageKind.RGB_24BPP;
}
if (kind && !this.smask && !this.mask &&
drawWidth === originalWidth && drawHeight === originalHeight) {
imgData.kind = kind;
imgArray = this.getImageBytes(originalHeight * rowBytes);
// If imgArray came from a DecodeStream, we're safe to transfer it
// (and thus neuter it) because it will constitute the entire
// DecodeStream's data. But if it came from a Stream, we need to
// copy it because it'll only be a portion of the Stream's data, and
// the rest will be read later on.
if (this.image instanceof DecodeStream) {
imgData.data = imgArray;
} else {
var newArray = new Uint8Array(imgArray.length);
newArray.set(imgArray);
imgData.data = newArray;
}
if (this.needsDecode) {
// Invert the buffer (which must be grayscale if we reached here).
assert(kind === ImageKind.GRAYSCALE_1BPP);
var buffer = imgData.data;
for (var i = 0, ii = buffer.length; i < ii; i++) {
buffer[i] ^= 0xff;
}
}
return imgData;
}
if (this.image instanceof JpegStream && !this.smask && !this.mask) {
imgData.kind = ImageKind.RGB_24BPP;
imgData.data = this.getImageBytes(originalHeight * rowBytes,
drawWidth, drawHeight, true);
return imgData;
}
}
imgArray = this.getImageBytes(originalHeight * rowBytes);
// imgArray can be incomplete (e.g. after CCITT fax encoding).
var actualHeight = 0 | (imgArray.length / rowBytes *
drawHeight / originalHeight);
var comps = this.getComponents(imgArray);
// If opacity data is present, use RGBA_32BPP form. Otherwise, use the
// more compact RGB_24BPP form if allowable.
var alpha01, maybeUndoPreblend;
if (!forceRGBA && !this.smask && !this.mask) {
imgData.kind = ImageKind.RGB_24BPP;
imgData.data = new Uint8Array(drawWidth * drawHeight * 3);
alpha01 = 0;
maybeUndoPreblend = false;
} else {
imgData.kind = ImageKind.RGBA_32BPP;
imgData.data = new Uint8Array(drawWidth * drawHeight * 4);
alpha01 = 1;
maybeUndoPreblend = true;
// Color key masking (opacity) must be performed before decoding.
this.fillOpacity(imgData.data, drawWidth, drawHeight, actualHeight,
comps);
}
if (this.needsDecode) {
this.decodeBuffer(comps);
}
this.colorSpace.fillRgb(imgData.data, originalWidth, originalHeight,
drawWidth, drawHeight, actualHeight, bpc, comps,
alpha01);
if (maybeUndoPreblend) {
this.undoPreblend(imgData.data, drawWidth, actualHeight);
}
return imgData;
},
fillGrayBuffer: function PDFImage_fillGrayBuffer(buffer) {
var numComps = this.numComps;
if (numComps !== 1) {
error('Reading gray scale from a color image: ' + numComps);
}
var width = this.width;
var height = this.height;
var bpc = this.bpc;
// rows start at byte boundary
var rowBytes = (width * numComps * bpc + 7) >> 3;
var imgArray = this.getImageBytes(height * rowBytes);
var comps = this.getComponents(imgArray);
var i, length;
if (bpc === 1) {
// inline decoding (= inversion) for 1 bpc images
length = width * height;
if (this.needsDecode) {
// invert and scale to {0, 255}
for (i = 0; i < length; ++i) {
buffer[i] = (comps[i] - 1) & 255;
}
} else {
// scale to {0, 255}
for (i = 0; i < length; ++i) {
buffer[i] = (-comps[i]) & 255;
}
}
return;
}
if (this.needsDecode) {
this.decodeBuffer(comps);
}
length = width * height;
// we aren't using a colorspace so we need to scale the value
var scale = 255 / ((1 << bpc) - 1);
for (i = 0; i < length; ++i) {
buffer[i] = (scale * comps[i]) | 0;
}
},
getImageBytes: function PDFImage_getImageBytes(length,
drawWidth, drawHeight,
forceRGB) {
this.image.reset();
this.image.drawWidth = drawWidth || this.width;
this.image.drawHeight = drawHeight || this.height;
this.image.forceRGB = !!forceRGB;
return this.image.getBytes(length);
}
};
return PDFImage;
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,166 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2014 Opera Software ASA
*
* 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.
*
*
* Based on https://code.google.com/p/smhasher/wiki/MurmurHash3.
* Hashes roughly 100 KB per millisecond on i7 3.4 GHz.
*/
/* globals Uint32ArrayView */
'use strict';
var MurmurHash3_64 = (function MurmurHash3_64Closure (seed) {
// Workaround for missing math precison in JS.
var MASK_HIGH = 0xffff0000;
var MASK_LOW = 0xffff;
function MurmurHash3_64 (seed) {
var SEED = 0xc3d2e1f0;
this.h1 = seed ? seed & 0xffffffff : SEED;
this.h2 = seed ? seed & 0xffffffff : SEED;
}
var alwaysUseUint32ArrayView = false;
//#if !(FIREFOX || MOZCENTRAL || B2G || CHROME)
// old webkits have issues with non-aligned arrays
try {
new Uint32Array(new Uint8Array(5).buffer, 0, 1);
} catch (e) {
alwaysUseUint32ArrayView = true;
}
//#endif
MurmurHash3_64.prototype = {
update: function MurmurHash3_64_update(input) {
var useUint32ArrayView = alwaysUseUint32ArrayView;
var i;
if (typeof input === 'string') {
var data = new Uint8Array(input.length * 2);
var length = 0;
for (i = 0; i < input.length; i++) {
var code = input.charCodeAt(i);
if (code <= 0xff) {
data[length++] = code;
}
else {
data[length++] = code >>> 8;
data[length++] = code & 0xff;
}
}
} else if (input instanceof Uint8Array) {
data = input;
length = data.length;
} else if (typeof input === 'object' && ('length' in input)) {
// processing regular arrays as well, e.g. for IE9
data = input;
length = data.length;
useUint32ArrayView = true;
} else {
throw new Error('Wrong data format in MurmurHash3_64_update. ' +
'Input must be a string or array.');
}
var blockCounts = length >> 2;
var tailLength = length - blockCounts * 4;
// we don't care about endianness here
var dataUint32 = useUint32ArrayView ?
new Uint32ArrayView(data, blockCounts) :
new Uint32Array(data.buffer, 0, blockCounts);
var k1 = 0;
var k2 = 0;
var h1 = this.h1;
var h2 = this.h2;
var C1 = 0xcc9e2d51;
var C2 = 0x1b873593;
var C1_LOW = C1 & MASK_LOW;
var C2_LOW = C2 & MASK_LOW;
for (i = 0; i < blockCounts; i++) {
if (i & 1) {
k1 = dataUint32[i];
k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
k1 = k1 << 15 | k1 >>> 17;
k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
h1 ^= k1;
h1 = h1 << 13 | h1 >>> 19;
h1 = h1 * 5 + 0xe6546b64;
} else {
k2 = dataUint32[i];
k2 = (k2 * C1 & MASK_HIGH) | (k2 * C1_LOW & MASK_LOW);
k2 = k2 << 15 | k2 >>> 17;
k2 = (k2 * C2 & MASK_HIGH) | (k2 * C2_LOW & MASK_LOW);
h2 ^= k2;
h2 = h2 << 13 | h2 >>> 19;
h2 = h2 * 5 + 0xe6546b64;
}
}
k1 = 0;
switch (tailLength) {
case 3:
k1 ^= data[blockCounts * 4 + 2] << 16;
/* falls through */
case 2:
k1 ^= data[blockCounts * 4 + 1] << 8;
/* falls through */
case 1:
k1 ^= data[blockCounts * 4];
/* falls through */
k1 = (k1 * C1 & MASK_HIGH) | (k1 * C1_LOW & MASK_LOW);
k1 = k1 << 15 | k1 >>> 17;
k1 = (k1 * C2 & MASK_HIGH) | (k1 * C2_LOW & MASK_LOW);
if (blockCounts & 1) {
h1 ^= k1;
} else {
h2 ^= k1;
}
}
this.h1 = h1;
this.h2 = h2;
return this;
},
hexdigest: function MurmurHash3_64_hexdigest () {
var h1 = this.h1;
var h2 = this.h2;
h1 ^= h2 >>> 1;
h1 = (h1 * 0xed558ccd & MASK_HIGH) | (h1 * 0x8ccd & MASK_LOW);
h2 = (h2 * 0xff51afd7 & MASK_HIGH) |
(((h2 << 16 | h1 >>> 16) * 0xafd7ed55 & MASK_HIGH) >>> 16);
h1 ^= h2 >>> 1;
h1 = (h1 * 0x1a85ec53 & MASK_HIGH) | (h1 * 0xec53 & MASK_LOW);
h2 = (h2 * 0xc4ceb9fe & MASK_HIGH) |
(((h2 << 16 | h1 >>> 16) * 0xb9fe1a85 & MASK_HIGH) >>> 16);
h1 ^= h2 >>> 1;
for (var i = 0, arr = [h1, h2], str = ''; i < arr.length; i++) {
var hex = (arr[i] >>> 0).toString(16);
while (hex.length < 8) {
hex = '0' + hex;
}
str += hex;
}
return str;
}
};
return MurmurHash3_64;
})();

View File

@@ -0,0 +1,274 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
// NOTE: Be careful what goes in this file, as it is also used from the context
// of the addon. So using warn/error in here will break the addon.
'use strict';
//#if (FIREFOX || MOZCENTRAL)
//
//Components.utils.import('resource://gre/modules/Services.jsm');
//
//var EXPORTED_SYMBOLS = ['NetworkManager'];
//
//var console = {
// log: function console_log(aMsg) {
// var msg = 'network.js: ' + (aMsg.join ? aMsg.join('') : aMsg);
// Services.console.logStringMessage(msg);
// // TODO(mack): dump() doesn't seem to work here...
// dump(msg + '\n');
// }
//}
//#endif
var NetworkManager = (function NetworkManagerClosure() {
var OK_RESPONSE = 200;
var PARTIAL_CONTENT_RESPONSE = 206;
function NetworkManager(url, args) {
this.url = url;
args = args || {};
this.isHttp = /^https?:/i.test(url);
this.httpHeaders = (this.isHttp && args.httpHeaders) || {};
this.withCredentials = args.withCredentials || false;
this.getXhr = args.getXhr ||
function NetworkManager_getXhr() {
//#if B2G
// return new XMLHttpRequest({ mozSystem: true });
//#else
return new XMLHttpRequest();
//#endif
};
this.currXhrId = 0;
this.pendingRequests = {};
this.loadedRequests = {};
}
function getArrayBuffer(xhr) {
var data = xhr.response;
if (typeof data !== 'string') {
return data;
}
var length = data.length;
var array = new Uint8Array(length);
for (var i = 0; i < length; i++) {
array[i] = data.charCodeAt(i) & 0xFF;
}
return array.buffer;
}
NetworkManager.prototype = {
requestRange: function NetworkManager_requestRange(begin, end, listeners) {
var args = {
begin: begin,
end: end
};
for (var prop in listeners) {
args[prop] = listeners[prop];
}
return this.request(args);
},
requestFull: function NetworkManager_requestFull(listeners) {
return this.request(listeners);
},
request: function NetworkManager_request(args) {
var xhr = this.getXhr();
var xhrId = this.currXhrId++;
var pendingRequest = this.pendingRequests[xhrId] = {
xhr: xhr
};
xhr.open('GET', this.url);
xhr.withCredentials = this.withCredentials;
for (var property in this.httpHeaders) {
var value = this.httpHeaders[property];
if (typeof value === 'undefined') {
continue;
}
xhr.setRequestHeader(property, value);
}
if (this.isHttp && 'begin' in args && 'end' in args) {
var rangeStr = args.begin + '-' + (args.end - 1);
xhr.setRequestHeader('Range', 'bytes=' + rangeStr);
pendingRequest.expectedStatus = 206;
} else {
pendingRequest.expectedStatus = 200;
}
if (args.onProgressiveData) {
xhr.responseType = 'moz-chunked-arraybuffer';
if (xhr.responseType === 'moz-chunked-arraybuffer') {
pendingRequest.onProgressiveData = args.onProgressiveData;
pendingRequest.mozChunked = true;
} else {
xhr.responseType = 'arraybuffer';
}
} else {
xhr.responseType = 'arraybuffer';
}
if (args.onError) {
xhr.onerror = function(evt) {
args.onError(xhr.status);
};
}
xhr.onreadystatechange = this.onStateChange.bind(this, xhrId);
xhr.onprogress = this.onProgress.bind(this, xhrId);
pendingRequest.onHeadersReceived = args.onHeadersReceived;
pendingRequest.onDone = args.onDone;
pendingRequest.onError = args.onError;
pendingRequest.onProgress = args.onProgress;
xhr.send(null);
return xhrId;
},
onProgress: function NetworkManager_onProgress(xhrId, evt) {
var pendingRequest = this.pendingRequests[xhrId];
if (!pendingRequest) {
// Maybe abortRequest was called...
return;
}
if (pendingRequest.mozChunked) {
var chunk = getArrayBuffer(pendingRequest.xhr);
pendingRequest.onProgressiveData(chunk);
}
var onProgress = pendingRequest.onProgress;
if (onProgress) {
onProgress(evt);
}
},
onStateChange: function NetworkManager_onStateChange(xhrId, evt) {
var pendingRequest = this.pendingRequests[xhrId];
if (!pendingRequest) {
// Maybe abortRequest was called...
return;
}
var xhr = pendingRequest.xhr;
if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) {
pendingRequest.onHeadersReceived();
delete pendingRequest.onHeadersReceived;
}
if (xhr.readyState !== 4) {
return;
}
if (!(xhrId in this.pendingRequests)) {
// The XHR request might have been aborted in onHeadersReceived()
// callback, in which case we should abort request
return;
}
delete this.pendingRequests[xhrId];
// success status == 0 can be on ftp, file and other protocols
if (xhr.status === 0 && this.isHttp) {
if (pendingRequest.onError) {
pendingRequest.onError(xhr.status);
}
return;
}
var xhrStatus = xhr.status || OK_RESPONSE;
// From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2:
// "A server MAY ignore the Range header". This means it's possible to
// get a 200 rather than a 206 response from a range request.
var ok_response_on_range_request =
xhrStatus === OK_RESPONSE &&
pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE;
if (!ok_response_on_range_request &&
xhrStatus !== pendingRequest.expectedStatus) {
if (pendingRequest.onError) {
pendingRequest.onError(xhr.status);
}
return;
}
this.loadedRequests[xhrId] = true;
var chunk = getArrayBuffer(xhr);
if (xhrStatus === PARTIAL_CONTENT_RESPONSE) {
var rangeHeader = xhr.getResponseHeader('Content-Range');
var matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader);
var begin = parseInt(matches[1], 10);
pendingRequest.onDone({
begin: begin,
chunk: chunk
});
} else if (pendingRequest.onProgressiveData) {
pendingRequest.onDone(null);
} else {
pendingRequest.onDone({
begin: 0,
chunk: chunk
});
}
},
hasPendingRequests: function NetworkManager_hasPendingRequests() {
for (var xhrId in this.pendingRequests) {
return true;
}
return false;
},
getRequestXhr: function NetworkManager_getXhr(xhrId) {
return this.pendingRequests[xhrId].xhr;
},
isStreamingRequest: function NetworkManager_isStreamingRequest(xhrId) {
return !!(this.pendingRequests[xhrId].onProgressiveData);
},
isPendingRequest: function NetworkManager_isPendingRequest(xhrId) {
return xhrId in this.pendingRequests;
},
isLoadedRequest: function NetworkManager_isLoadedRequest(xhrId) {
return xhrId in this.loadedRequests;
},
abortAllRequests: function NetworkManager_abortAllRequests() {
for (var xhrId in this.pendingRequests) {
this.abortRequest(xhrId | 0);
}
},
abortRequest: function NetworkManager_abortRequest(xhrId) {
var xhr = this.pendingRequests[xhrId].xhr;
delete this.pendingRequests[xhrId];
xhr.abort();
}
};
return NetworkManager;
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,893 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals Ascii85Stream, AsciiHexStream, CCITTFaxStream, Cmd, Dict, error,
FlateStream, isArray, isCmd, isDict, isInt, isName, isNum, isRef,
isString, Jbig2Stream, JpegStream, JpxStream, LZWStream, Name,
NullStream, PredictorStream, Ref, RunLengthStream, warn, info,
StreamType, MissingDataException, assert */
'use strict';
var EOF = {};
function isEOF(v) {
return (v === EOF);
}
var Parser = (function ParserClosure() {
function Parser(lexer, allowStreams, xref) {
this.lexer = lexer;
this.allowStreams = allowStreams;
this.xref = xref;
this.imageCache = {
length: 0,
adler32: 0,
stream: null
};
this.refill();
}
Parser.prototype = {
refill: function Parser_refill() {
this.buf1 = this.lexer.getObj();
this.buf2 = this.lexer.getObj();
},
shift: function Parser_shift() {
if (isCmd(this.buf2, 'ID')) {
this.buf1 = this.buf2;
this.buf2 = null;
} else {
this.buf1 = this.buf2;
this.buf2 = this.lexer.getObj();
}
},
getObj: function Parser_getObj(cipherTransform) {
var buf1 = this.buf1;
this.shift();
if (buf1 instanceof Cmd) {
switch (buf1.cmd) {
case 'BI': // inline image
return this.makeInlineImage(cipherTransform);
case '[': // array
var array = [];
while (!isCmd(this.buf1, ']') && !isEOF(this.buf1)) {
array.push(this.getObj(cipherTransform));
}
if (isEOF(this.buf1)) {
error('End of file inside array');
}
this.shift();
return array;
case '<<': // dictionary or stream
var dict = new Dict(this.xref);
while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) {
if (!isName(this.buf1)) {
info('Malformed dictionary: key must be a name object');
this.shift();
continue;
}
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');
}
// Stream objects are not allowed inside content streams or
// object streams.
if (isCmd(this.buf2, 'stream')) {
return (this.allowStreams ?
this.makeStream(dict, cipherTransform) : dict);
}
this.shift();
return dict;
default: // simple object
return buf1;
}
}
if (isInt(buf1)) { // indirect reference or integer
var num = buf1;
if (isInt(this.buf1) && isCmd(this.buf2, 'R')) {
var ref = new Ref(num, this.buf1);
this.shift();
this.shift();
return ref;
}
return num;
}
if (isString(buf1)) { // string
var str = buf1;
if (cipherTransform) {
str = cipherTransform.decryptString(str);
}
return str;
}
// simple object
return buf1;
},
makeInlineImage: function Parser_makeInlineImage(cipherTransform) {
var lexer = this.lexer;
var stream = lexer.stream;
// parse dictionary
var dict = new Dict(null);
while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
if (!isName(this.buf1)) {
error('Dictionary key must be a name object');
}
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1)) {
break;
}
dict.set(key, this.getObj(cipherTransform));
}
// parse image stream
var startPos = stream.pos;
// searching for the /EI\s/
var state = 0, ch, i, ii;
var E = 0x45, I = 0x49, SPACE = 0x20, NL = 0xA, CR = 0xD;
while ((ch = stream.getByte()) !== -1) {
if (state === 0) {
state = (ch === E) ? 1 : 0;
} else if (state === 1) {
state = (ch === I) ? 2 : 0;
} else {
assert(state === 2);
if (ch === SPACE || ch === NL || ch === CR) {
// Let's check the next five bytes are ASCII... just be sure.
var n = 5;
var followingBytes = stream.peekBytes(n);
for (i = 0; i < n; i++) {
ch = followingBytes[i];
if (ch !== NL && ch !== CR && (ch < SPACE || ch > 0x7F)) {
// Not a LF, CR, SPACE or any visible ASCII character, i.e.
// it's binary stuff. Resetting the state.
state = 0;
break;
}
}
if (state === 2) {
break; // finished!
}
} else {
state = 0;
}
}
}
var length = (stream.pos - 4) - startPos;
var imageStream = stream.makeSubStream(startPos, length, dict);
// trying to cache repeat images, first we are trying to "warm up" caching
// using length, then comparing adler32
var MAX_LENGTH_TO_CACHE = 1000;
var cacheImage = false, adler32;
if (length < MAX_LENGTH_TO_CACHE && this.imageCache.length === length) {
var imageBytes = imageStream.getBytes();
imageStream.reset();
var a = 1;
var b = 0;
for (i = 0, ii = imageBytes.length; i < ii; ++i) {
a = (a + (imageBytes[i] & 0xff)) % 65521;
b = (b + a) % 65521;
}
adler32 = (b << 16) | a;
if (this.imageCache.stream && this.imageCache.adler32 === adler32) {
this.buf2 = Cmd.get('EI');
this.shift();
this.imageCache.stream.reset();
return this.imageCache.stream;
}
cacheImage = true;
}
if (!cacheImage && !this.imageCache.stream) {
this.imageCache.length = length;
this.imageCache.stream = null;
}
if (cipherTransform) {
imageStream = cipherTransform.createStream(imageStream, length);
}
imageStream = this.filter(imageStream, dict, length);
imageStream.dict = dict;
if (cacheImage) {
imageStream.cacheKey = 'inline_' + length + '_' + adler32;
this.imageCache.adler32 = adler32;
this.imageCache.stream = imageStream;
}
this.buf2 = Cmd.get('EI');
this.shift();
return imageStream;
},
fetchIfRef: function Parser_fetchIfRef(obj) {
// not relying on the xref.fetchIfRef -- xref might not be set
return (isRef(obj) ? this.xref.fetch(obj) : obj);
},
makeStream: function Parser_makeStream(dict, cipherTransform) {
var lexer = this.lexer;
var stream = lexer.stream;
// get stream start position
lexer.skipToNextLine();
var pos = stream.pos - 1;
// get length
var length = this.fetchIfRef(dict.get('Length'));
if (!isInt(length)) {
info('Bad ' + length + ' attribute in stream');
length = 0;
}
// skip over the stream data
stream.pos = pos + length;
lexer.nextChar();
this.shift(); // '>>'
this.shift(); // 'stream'
if (!isCmd(this.buf1, 'endstream')) {
// bad stream length, scanning for endstream
stream.pos = pos;
var SCAN_BLOCK_SIZE = 2048;
var ENDSTREAM_SIGNATURE_LENGTH = 9;
var ENDSTREAM_SIGNATURE = [0x65, 0x6E, 0x64, 0x73, 0x74, 0x72, 0x65,
0x61, 0x6D];
var skipped = 0, found = false, i, j;
while (stream.pos < stream.end) {
var scanBytes = stream.peekBytes(SCAN_BLOCK_SIZE);
var scanLength = scanBytes.length - ENDSTREAM_SIGNATURE_LENGTH;
if (scanLength <= 0) {
break;
}
found = false;
for (i = 0, j = 0; i < scanLength; i++) {
var b = scanBytes[i];
if (b !== ENDSTREAM_SIGNATURE[j]) {
i -= j;
j = 0;
} else {
j++;
if (j >= ENDSTREAM_SIGNATURE_LENGTH) {
i++;
found = true;
break;
}
}
}
if (found) {
skipped += i - ENDSTREAM_SIGNATURE_LENGTH;
stream.pos += i - ENDSTREAM_SIGNATURE_LENGTH;
break;
}
skipped += scanLength;
stream.pos += scanLength;
}
if (!found) {
error('Missing endstream');
}
length = skipped;
lexer.nextChar();
this.shift();
this.shift();
}
this.shift(); // 'endstream'
stream = stream.makeSubStream(pos, length, dict);
if (cipherTransform) {
stream = cipherTransform.createStream(stream, length);
}
stream = this.filter(stream, dict, length);
stream.dict = dict;
return stream;
},
filter: function Parser_filter(stream, dict, length) {
var filter = this.fetchIfRef(dict.get('Filter', 'F'));
var params = this.fetchIfRef(dict.get('DecodeParms', 'DP'));
if (isName(filter)) {
return this.makeFilter(stream, filter.name, length, params);
}
var maybeLength = length;
if (isArray(filter)) {
var filterArray = filter;
var paramsArray = params;
for (var i = 0, ii = filterArray.length; i < ii; ++i) {
filter = filterArray[i];
if (!isName(filter)) {
error('Bad filter name: ' + filter);
}
params = null;
if (isArray(paramsArray) && (i in paramsArray)) {
params = paramsArray[i];
}
stream = this.makeFilter(stream, filter.name, maybeLength, params);
// after the first stream the length variable is invalid
maybeLength = null;
}
}
return stream;
},
makeFilter: function Parser_makeFilter(stream, name, maybeLength, params) {
if (stream.dict.get('Length') === 0) {
return new NullStream(stream);
}
try {
if (params) {
params = this.fetchIfRef(params);
}
var xrefStreamStats = this.xref.stats.streamTypes;
if (name === 'FlateDecode' || name === 'Fl') {
xrefStreamStats[StreamType.FLATE] = true;
if (params) {
return new PredictorStream(new FlateStream(stream, maybeLength),
maybeLength, params);
}
return new FlateStream(stream, maybeLength);
}
if (name === 'LZWDecode' || name === 'LZW') {
xrefStreamStats[StreamType.LZW] = true;
var earlyChange = 1;
if (params) {
if (params.has('EarlyChange')) {
earlyChange = params.get('EarlyChange');
}
return new PredictorStream(
new LZWStream(stream, maybeLength, earlyChange),
maybeLength, params);
}
return new LZWStream(stream, maybeLength, earlyChange);
}
if (name === 'DCTDecode' || name === 'DCT') {
// According to the specification: for inline images, the ID operator
// shall be followed by a single whitespace character (unless it uses
// ASCII85Decode or ASCIIHexDecode filters).
// In practice this only seems to be followed for inline JPEG images,
// and generally ignoring the first byte of the stream if it is a
// whitespace char can even *cause* issues (e.g. in the CCITTFaxDecode
// filters used in issue2984.pdf).
// Hence when the first byte of the stream of an inline JPEG image is
// a whitespace character, we thus simply skip over it.
if (isCmd(this.buf1, 'ID')) {
var firstByte = stream.peekByte();
if (firstByte === 0x0A /* LF */ || firstByte === 0x0D /* CR */ ||
firstByte === 0x20 /* SPACE */) {
stream.skip();
}
}
xrefStreamStats[StreamType.DCT] = true;
return new JpegStream(stream, maybeLength, stream.dict, this.xref);
}
if (name === 'JPXDecode' || name === 'JPX') {
xrefStreamStats[StreamType.JPX] = true;
return new JpxStream(stream, maybeLength, stream.dict);
}
if (name === 'ASCII85Decode' || name === 'A85') {
xrefStreamStats[StreamType.A85] = true;
return new Ascii85Stream(stream, maybeLength);
}
if (name === 'ASCIIHexDecode' || name === 'AHx') {
xrefStreamStats[StreamType.AHX] = true;
return new AsciiHexStream(stream, maybeLength);
}
if (name === 'CCITTFaxDecode' || name === 'CCF') {
xrefStreamStats[StreamType.CCF] = true;
return new CCITTFaxStream(stream, maybeLength, params);
}
if (name === 'RunLengthDecode' || name === 'RL') {
xrefStreamStats[StreamType.RL] = true;
return new RunLengthStream(stream, maybeLength);
}
if (name === 'JBIG2Decode') {
xrefStreamStats[StreamType.JBIG] = true;
return new Jbig2Stream(stream, maybeLength, stream.dict);
}
warn('filter "' + name + '" not supported yet');
return stream;
} catch (ex) {
if (ex instanceof MissingDataException) {
throw ex;
}
warn('Invalid stream: \"' + ex + '\"');
return new NullStream(stream);
}
}
};
return Parser;
})();
var Lexer = (function LexerClosure() {
function Lexer(stream, knownCommands) {
this.stream = stream;
this.nextChar();
// While lexing, we build up many strings one char at a time. Using += for
// this can result in lots of garbage strings. It's better to build an
// array of single-char strings and then join() them together at the end.
// And reusing a single array (i.e. |this.strBuf|) over and over for this
// purpose uses less memory than using a new array for each string.
this.strBuf = [];
// The PDFs might have "glued" commands with other commands, operands or
// literals, e.g. "q1". The knownCommands is a dictionary of the valid
// commands and their prefixes. The prefixes are built the following way:
// if there a command that is a prefix of the other valid command or
// literal (e.g. 'f' and 'false') the following prefixes must be included,
// 'fa', 'fal', 'fals'. The prefixes are not needed, if the command has no
// other commands or literals as a prefix. The knowCommands is optional.
this.knownCommands = knownCommands;
}
Lexer.isSpace = function Lexer_isSpace(ch) {
// Space is one of the following characters: SPACE, TAB, CR or LF.
return (ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A);
};
// A '1' in this array means the character is white space. A '1' or
// '2' means the character ends a name or command.
var specialChars = [
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, // 0x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
1, 0, 0, 0, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, // 2x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, // 3x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 5x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, // 7x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ax
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // bx
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // cx
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // dx
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ex
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fx
];
function toHexDigit(ch) {
if (ch >= 0x30 && ch <= 0x39) { // '0'-'9'
return ch & 0x0F;
}
if ((ch >= 0x41 && ch <= 0x46) || (ch >= 0x61 && ch <= 0x66)) {
// 'A'-'F', 'a'-'f'
return (ch & 0x0F) + 9;
}
return -1;
}
Lexer.prototype = {
nextChar: function Lexer_nextChar() {
return (this.currentChar = this.stream.getByte());
},
peekChar: function Lexer_peekChar() {
return this.stream.peekByte();
},
getNumber: function Lexer_getNumber() {
var ch = this.currentChar;
var eNotation = false;
var divideBy = 0; // different from 0 if it's a floating point value
var sign = 1;
if (ch === 0x2D) { // '-'
sign = -1;
ch = this.nextChar();
} else if (ch === 0x2B) { // '+'
ch = this.nextChar();
}
if (ch === 0x2E) { // '.'
divideBy = 10;
ch = this.nextChar();
}
if (ch < 0x30 || ch > 0x39) { // '0' - '9'
error('Invalid number: ' + String.fromCharCode(ch));
return 0;
}
var baseValue = ch - 0x30; // '0'
var powerValue = 0;
var powerValueSign = 1;
while ((ch = this.nextChar()) >= 0) {
if (0x30 <= ch && ch <= 0x39) { // '0' - '9'
var currentDigit = ch - 0x30; // '0'
if (eNotation) { // We are after an 'e' or 'E'
powerValue = powerValue * 10 + currentDigit;
} else {
if (divideBy !== 0) { // We are after a point
divideBy *= 10;
}
baseValue = baseValue * 10 + currentDigit;
}
} else if (ch === 0x2E) { // '.'
if (divideBy === 0) {
divideBy = 1;
} else {
// A number can have only one '.'
break;
}
} else if (ch === 0x2D) { // '-'
// ignore minus signs in the middle of numbers to match
// Adobe's behavior
warn('Badly formated number');
} else if (ch === 0x45 || ch === 0x65) { // 'E', 'e'
// 'E' can be either a scientific notation or the beginning of a new
// operator
ch = this.peekChar();
if (ch === 0x2B || ch === 0x2D) { // '+', '-'
powerValueSign = (ch === 0x2D) ? -1 : 1;
this.nextChar(); // Consume the sign character
} else if (ch < 0x30 || ch > 0x39) { // '0' - '9'
// The 'E' must be the beginning of a new operator
break;
}
eNotation = true;
} else {
// the last character doesn't belong to us
break;
}
}
if (divideBy !== 0) {
baseValue /= divideBy;
}
if (eNotation) {
baseValue *= Math.pow(10, powerValueSign * powerValue);
}
return sign * baseValue;
},
getString: function Lexer_getString() {
var numParen = 1;
var done = false;
var strBuf = this.strBuf;
strBuf.length = 0;
var ch = this.nextChar();
while (true) {
var charBuffered = false;
switch (ch | 0) {
case -1:
warn('Unterminated string');
done = true;
break;
case 0x28: // '('
++numParen;
strBuf.push('(');
break;
case 0x29: // ')'
if (--numParen === 0) {
this.nextChar(); // consume strings ')'
done = true;
} else {
strBuf.push(')');
}
break;
case 0x5C: // '\\'
ch = this.nextChar();
switch (ch) {
case -1:
warn('Unterminated string');
done = true;
break;
case 0x6E: // 'n'
strBuf.push('\n');
break;
case 0x72: // 'r'
strBuf.push('\r');
break;
case 0x74: // 't'
strBuf.push('\t');
break;
case 0x62: // 'b'
strBuf.push('\b');
break;
case 0x66: // 'f'
strBuf.push('\f');
break;
case 0x5C: // '\'
case 0x28: // '('
case 0x29: // ')'
strBuf.push(String.fromCharCode(ch));
break;
case 0x30: case 0x31: case 0x32: case 0x33: // '0'-'3'
case 0x34: case 0x35: case 0x36: case 0x37: // '4'-'7'
var x = ch & 0x0F;
ch = this.nextChar();
charBuffered = true;
if (ch >= 0x30 && ch <= 0x37) { // '0'-'7'
x = (x << 3) + (ch & 0x0F);
ch = this.nextChar();
if (ch >= 0x30 && ch <= 0x37) { // '0'-'7'
charBuffered = false;
x = (x << 3) + (ch & 0x0F);
}
}
strBuf.push(String.fromCharCode(x));
break;
case 0x0D: // CR
if (this.peekChar() === 0x0A) { // LF
this.nextChar();
}
break;
case 0x0A: // LF
break;
default:
strBuf.push(String.fromCharCode(ch));
break;
}
break;
default:
strBuf.push(String.fromCharCode(ch));
break;
}
if (done) {
break;
}
if (!charBuffered) {
ch = this.nextChar();
}
}
return strBuf.join('');
},
getName: function Lexer_getName() {
var ch;
var strBuf = this.strBuf;
strBuf.length = 0;
while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
if (ch === 0x23) { // '#'
ch = this.nextChar();
var x = toHexDigit(ch);
if (x !== -1) {
var x2 = toHexDigit(this.nextChar());
if (x2 === -1) {
error('Illegal digit in hex char in name: ' + x2);
}
strBuf.push(String.fromCharCode((x << 4) | x2));
} else {
strBuf.push('#', String.fromCharCode(ch));
}
} else {
strBuf.push(String.fromCharCode(ch));
}
}
if (strBuf.length > 128) {
error('Warning: name token is longer than allowed by the spec: ' +
strBuf.length);
}
return Name.get(strBuf.join(''));
},
getHexString: function Lexer_getHexString() {
var strBuf = this.strBuf;
strBuf.length = 0;
var ch = this.currentChar;
var isFirstHex = true;
var firstDigit;
var secondDigit;
while (true) {
if (ch < 0) {
warn('Unterminated hex string');
break;
} else if (ch === 0x3E) { // '>'
this.nextChar();
break;
} else if (specialChars[ch] === 1) {
ch = this.nextChar();
continue;
} else {
if (isFirstHex) {
firstDigit = toHexDigit(ch);
if (firstDigit === -1) {
warn('Ignoring invalid character "' + ch + '" in hex string');
ch = this.nextChar();
continue;
}
} else {
secondDigit = toHexDigit(ch);
if (secondDigit === -1) {
warn('Ignoring invalid character "' + ch + '" in hex string');
ch = this.nextChar();
continue;
}
strBuf.push(String.fromCharCode((firstDigit << 4) | secondDigit));
}
isFirstHex = !isFirstHex;
ch = this.nextChar();
}
}
return strBuf.join('');
},
getObj: function Lexer_getObj() {
// skip whitespace and comments
var comment = false;
var ch = this.currentChar;
while (true) {
if (ch < 0) {
return EOF;
}
if (comment) {
if (ch === 0x0A || ch === 0x0D) { // LF, CR
comment = false;
}
} else if (ch === 0x25) { // '%'
comment = true;
} else if (specialChars[ch] !== 1) {
break;
}
ch = this.nextChar();
}
// start reading token
switch (ch | 0) {
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4'
case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9'
case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.'
return this.getNumber();
case 0x28: // '('
return this.getString();
case 0x2F: // '/'
return this.getName();
// array punctuation
case 0x5B: // '['
this.nextChar();
return Cmd.get('[');
case 0x5D: // ']'
this.nextChar();
return Cmd.get(']');
// hex string or dict punctuation
case 0x3C: // '<'
ch = this.nextChar();
if (ch === 0x3C) {
// dict punctuation
this.nextChar();
return Cmd.get('<<');
}
return this.getHexString();
// dict punctuation
case 0x3E: // '>'
ch = this.nextChar();
if (ch === 0x3E) {
this.nextChar();
return Cmd.get('>>');
}
return Cmd.get('>');
case 0x7B: // '{'
this.nextChar();
return Cmd.get('{');
case 0x7D: // '}'
this.nextChar();
return Cmd.get('}');
case 0x29: // ')'
error('Illegal character: ' + ch);
break;
}
// command
var str = String.fromCharCode(ch);
var knownCommands = this.knownCommands;
var knownCommandFound = knownCommands && knownCommands[str] !== undefined;
while ((ch = this.nextChar()) >= 0 && !specialChars[ch]) {
// stop if known command is found and next character does not make
// the str a command
var possibleCommand = str + String.fromCharCode(ch);
if (knownCommandFound && knownCommands[possibleCommand] === undefined) {
break;
}
if (str.length === 128) {
error('Command token too long: ' + str.length);
}
str = possibleCommand;
knownCommandFound = knownCommands && knownCommands[str] !== undefined;
}
if (str === 'true') {
return true;
}
if (str === 'false') {
return false;
}
if (str === 'null') {
return null;
}
return Cmd.get(str);
},
skipToNextLine: function Lexer_skipToNextLine() {
var ch = this.currentChar;
while (ch >= 0) {
if (ch === 0x0D) { // CR
ch = this.nextChar();
if (ch === 0x0A) { // LF
this.nextChar();
}
break;
} else if (ch === 0x0A) { // LF
this.nextChar();
break;
}
ch = this.nextChar();
}
}
};
return Lexer;
})();
var Linearization = {
create: function LinearizationCreate(stream) {
function getInt(name, allowZeroValue) {
var obj = linDict.get(name);
if (isInt(obj) && (allowZeroValue ? obj >= 0 : obj > 0)) {
return obj;
}
throw new Error('The "' + name + '" parameter in the linearization ' +
'dictionary is invalid.');
}
function getHints() {
var hints = linDict.get('H'), hintsLength, item;
if (isArray(hints) &&
((hintsLength = hints.length) === 2 || hintsLength === 4)) {
for (var index = 0; index < hintsLength; index++) {
if (!(isInt(item = hints[index]) && item > 0)) {
throw new Error('Hint (' + index +
') in the linearization dictionary is invalid.');
}
}
return hints;
}
throw new Error('Hint array in the linearization dictionary is invalid.');
}
var parser = new Parser(new Lexer(stream), false, null);
var obj1 = parser.getObj();
var obj2 = parser.getObj();
var obj3 = parser.getObj();
var linDict = parser.getObj();
var obj, length;
if (!(isInt(obj1) && isInt(obj2) && isCmd(obj3, 'obj') && isDict(linDict) &&
isNum(obj = linDict.get('Linearized')) && obj > 0)) {
return null; // No valid linearization dictionary found.
} else if ((length = getInt('L')) !== stream.length) {
throw new Error('The "L" parameter in the linearization dictionary ' +
'does not equal the stream length.');
}
return {
length: length,
hints: getHints(),
objectNumberFirst: getInt('O'),
endFirst: getInt('E'),
numPages: getInt('N'),
mainXRefEntriesOffset: getInt('T'),
pageFirst: (linDict.has('P') ? getInt('P', true) : 0)
};
}
};

View File

@@ -0,0 +1,795 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals ColorSpace, PDFFunction, Util, error, warn, info, isArray, isStream,
assert, isPDFFunction, UnsupportedManager, UNSUPPORTED_FEATURES */
'use strict';
var PatternType = {
FUNCTION_BASED: 1,
AXIAL: 2,
RADIAL: 3,
FREE_FORM_MESH: 4,
LATTICE_FORM_MESH: 5,
COONS_PATCH_MESH: 6,
TENSOR_PATCH_MESH: 7
};
var Pattern = (function PatternClosure() {
// Constructor should define this.getPattern
function Pattern() {
error('should not call Pattern constructor');
}
Pattern.prototype = {
// Input: current Canvas context
// Output: the appropriate fillStyle or strokeStyle
getPattern: function Pattern_getPattern(ctx) {
error('Should not call Pattern.getStyle: ' + ctx);
}
};
Pattern.parseShading = function Pattern_parseShading(shading, matrix, xref,
res) {
var dict = isStream(shading) ? shading.dict : shading;
var type = dict.get('ShadingType');
switch (type) {
case PatternType.AXIAL:
case PatternType.RADIAL:
// Both radial and axial shadings are handled by RadialAxial shading.
return new Shadings.RadialAxial(dict, matrix, xref, res);
case PatternType.FREE_FORM_MESH:
case PatternType.LATTICE_FORM_MESH:
case PatternType.COONS_PATCH_MESH:
case PatternType.TENSOR_PATCH_MESH:
return new Shadings.Mesh(shading, matrix, xref, res);
default:
UnsupportedManager.notify(UNSUPPORTED_FEATURES.shadingPattern);
return new Shadings.Dummy();
}
};
return Pattern;
})();
var Shadings = {};
// A small number to offset the first/last color stops so we can insert ones to
// support extend. Number.MIN_VALUE appears to be too small and breaks the
// extend. 1e-7 works in FF but chrome seems to use an even smaller sized number
// internally so we have to go bigger.
Shadings.SMALL_NUMBER = 1e-2;
// Radial and axial shading have very similar implementations
// If needed, the implementations can be broken into two classes
Shadings.RadialAxial = (function RadialAxialClosure() {
function RadialAxial(dict, matrix, xref, res) {
this.matrix = matrix;
this.coordsArr = dict.get('Coords');
this.shadingType = dict.get('ShadingType');
this.type = 'Pattern';
var cs = dict.get('ColorSpace', 'CS');
cs = ColorSpace.parse(cs, xref, res);
this.cs = cs;
var t0 = 0.0, t1 = 1.0;
if (dict.has('Domain')) {
var domainArr = dict.get('Domain');
t0 = domainArr[0];
t1 = domainArr[1];
}
var extendStart = false, extendEnd = false;
if (dict.has('Extend')) {
var extendArr = dict.get('Extend');
extendStart = extendArr[0];
extendEnd = extendArr[1];
}
if (this.shadingType === PatternType.RADIAL &&
(!extendStart || !extendEnd)) {
// Radial gradient only currently works if either circle is fully within
// the other circle.
var x1 = this.coordsArr[0];
var y1 = this.coordsArr[1];
var r1 = this.coordsArr[2];
var x2 = this.coordsArr[3];
var y2 = this.coordsArr[4];
var r2 = this.coordsArr[5];
var distance = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
if (r1 <= r2 + distance &&
r2 <= r1 + distance) {
warn('Unsupported radial gradient.');
}
}
this.extendStart = extendStart;
this.extendEnd = extendEnd;
var fnObj = dict.get('Function');
var fn = PDFFunction.parseArray(xref, fnObj);
// 10 samples seems good enough for now, but probably won't work
// if there are sharp color changes. Ideally, we would implement
// the spec faithfully and add lossless optimizations.
var diff = t1 - t0;
var step = diff / 10;
var colorStops = this.colorStops = [];
// Protect against bad domains so we don't end up in an infinte loop below.
if (t0 >= t1 || step <= 0) {
// Acrobat doesn't seem to handle these cases so we'll ignore for
// now.
info('Bad shading domain.');
return;
}
var color = new Float32Array(cs.numComps), ratio = new Float32Array(1);
var rgbColor;
for (var i = t0; i <= t1; i += step) {
ratio[0] = i;
fn(ratio, 0, color, 0);
rgbColor = cs.getRgb(color, 0);
var cssColor = Util.makeCssRgb(rgbColor);
colorStops.push([(i - t0) / diff, cssColor]);
}
var background = 'transparent';
if (dict.has('Background')) {
rgbColor = cs.getRgb(dict.get('Background'), 0);
background = Util.makeCssRgb(rgbColor);
}
if (!extendStart) {
// Insert a color stop at the front and offset the first real color stop
// so it doesn't conflict with the one we insert.
colorStops.unshift([0, background]);
colorStops[1][0] += Shadings.SMALL_NUMBER;
}
if (!extendEnd) {
// Same idea as above in extendStart but for the end.
colorStops[colorStops.length - 1][0] -= Shadings.SMALL_NUMBER;
colorStops.push([1, background]);
}
this.colorStops = colorStops;
}
RadialAxial.prototype = {
getIR: function RadialAxial_getIR() {
var coordsArr = this.coordsArr;
var shadingType = this.shadingType;
var type, p0, p1, r0, r1;
if (shadingType === PatternType.AXIAL) {
p0 = [coordsArr[0], coordsArr[1]];
p1 = [coordsArr[2], coordsArr[3]];
r0 = null;
r1 = null;
type = 'axial';
} else if (shadingType === PatternType.RADIAL) {
p0 = [coordsArr[0], coordsArr[1]];
p1 = [coordsArr[3], coordsArr[4]];
r0 = coordsArr[2];
r1 = coordsArr[5];
type = 'radial';
} else {
error('getPattern type unknown: ' + shadingType);
}
var matrix = this.matrix;
if (matrix) {
p0 = Util.applyTransform(p0, matrix);
p1 = Util.applyTransform(p1, matrix);
}
return ['RadialAxial', type, this.colorStops, p0, p1, r0, r1];
}
};
return RadialAxial;
})();
// All mesh shading. For now, they will be presented as set of the triangles
// to be drawn on the canvas and rgb color for each vertex.
Shadings.Mesh = (function MeshClosure() {
function MeshStreamReader(stream, context) {
this.stream = stream;
this.context = context;
this.buffer = 0;
this.bufferLength = 0;
var numComps = context.numComps;
this.tmpCompsBuf = new Float32Array(numComps);
var csNumComps = context.colorSpace;
this.tmpCsCompsBuf = context.colorFn ? new Float32Array(csNumComps) :
this.tmpCompsBuf;
}
MeshStreamReader.prototype = {
get hasData() {
if (this.stream.end) {
return this.stream.pos < this.stream.end;
}
if (this.bufferLength > 0) {
return true;
}
var nextByte = this.stream.getByte();
if (nextByte < 0) {
return false;
}
this.buffer = nextByte;
this.bufferLength = 8;
return true;
},
readBits: function MeshStreamReader_readBits(n) {
var buffer = this.buffer;
var bufferLength = this.bufferLength;
if (n === 32) {
if (bufferLength === 0) {
return ((this.stream.getByte() << 24) |
(this.stream.getByte() << 16) | (this.stream.getByte() << 8) |
this.stream.getByte()) >>> 0;
}
buffer = (buffer << 24) | (this.stream.getByte() << 16) |
(this.stream.getByte() << 8) | this.stream.getByte();
var nextByte = this.stream.getByte();
this.buffer = nextByte & ((1 << bufferLength) - 1);
return ((buffer << (8 - bufferLength)) |
((nextByte & 0xFF) >> bufferLength)) >>> 0;
}
if (n === 8 && bufferLength === 0) {
return this.stream.getByte();
}
while (bufferLength < n) {
buffer = (buffer << 8) | this.stream.getByte();
bufferLength += 8;
}
bufferLength -= n;
this.bufferLength = bufferLength;
this.buffer = buffer & ((1 << bufferLength) - 1);
return buffer >> bufferLength;
},
align: function MeshStreamReader_align() {
this.buffer = 0;
this.bufferLength = 0;
},
readFlag: function MeshStreamReader_readFlag() {
return this.readBits(this.context.bitsPerFlag);
},
readCoordinate: function MeshStreamReader_readCoordinate() {
var bitsPerCoordinate = this.context.bitsPerCoordinate;
var xi = this.readBits(bitsPerCoordinate);
var yi = this.readBits(bitsPerCoordinate);
var decode = this.context.decode;
var scale = bitsPerCoordinate < 32 ? 1 / ((1 << bitsPerCoordinate) - 1) :
2.3283064365386963e-10; // 2 ^ -32
return [
xi * scale * (decode[1] - decode[0]) + decode[0],
yi * scale * (decode[3] - decode[2]) + decode[2]
];
},
readComponents: function MeshStreamReader_readComponents() {
var numComps = this.context.numComps;
var bitsPerComponent = this.context.bitsPerComponent;
var scale = bitsPerComponent < 32 ? 1 / ((1 << bitsPerComponent) - 1) :
2.3283064365386963e-10; // 2 ^ -32
var decode = this.context.decode;
var components = this.tmpCompsBuf;
for (var i = 0, j = 4; i < numComps; i++, j += 2) {
var ci = this.readBits(bitsPerComponent);
components[i] = ci * scale * (decode[j + 1] - decode[j]) + decode[j];
}
var color = this.tmpCsCompsBuf;
if (this.context.colorFn) {
this.context.colorFn(components, 0, color, 0);
}
return this.context.colorSpace.getRgb(color, 0);
}
};
function decodeType4Shading(mesh, reader) {
var coords = mesh.coords;
var colors = mesh.colors;
var operators = [];
var ps = []; // not maintaining cs since that will match ps
var verticesLeft = 0; // assuming we have all data to start a new triangle
while (reader.hasData) {
var f = reader.readFlag();
var coord = reader.readCoordinate();
var color = reader.readComponents();
if (verticesLeft === 0) { // ignoring flags if we started a triangle
assert(0 <= f && f <= 2, 'Unknown type4 flag');
switch (f) {
case 0:
verticesLeft = 3;
break;
case 1:
ps.push(ps[ps.length - 2], ps[ps.length - 1]);
verticesLeft = 1;
break;
case 2:
ps.push(ps[ps.length - 3], ps[ps.length - 1]);
verticesLeft = 1;
break;
}
operators.push(f);
}
ps.push(coords.length);
coords.push(coord);
colors.push(color);
verticesLeft--;
reader.align();
}
var psPacked = new Int32Array(ps);
mesh.figures.push({
type: 'triangles',
coords: psPacked,
colors: psPacked
});
}
function decodeType5Shading(mesh, reader, verticesPerRow) {
var coords = mesh.coords;
var colors = mesh.colors;
var ps = []; // not maintaining cs since that will match ps
while (reader.hasData) {
var coord = reader.readCoordinate();
var color = reader.readComponents();
ps.push(coords.length);
coords.push(coord);
colors.push(color);
}
var psPacked = new Int32Array(ps);
mesh.figures.push({
type: 'lattice',
coords: psPacked,
colors: psPacked,
verticesPerRow: verticesPerRow
});
}
var MIN_SPLIT_PATCH_CHUNKS_AMOUNT = 3;
var MAX_SPLIT_PATCH_CHUNKS_AMOUNT = 20;
var TRIANGLE_DENSITY = 20; // count of triangles per entire mesh bounds
var getB = (function getBClosure() {
function buildB(count) {
var lut = [];
for (var i = 0; i <= count; i++) {
var t = i / count, t_ = 1 - t;
lut.push(new Float32Array([t_ * t_ * t_, 3 * t * t_ * t_,
3 * t * t * t_, t * t * t]));
}
return lut;
}
var cache = [];
return function getB(count) {
if (!cache[count]) {
cache[count] = buildB(count);
}
return cache[count];
};
})();
function buildFigureFromPatch(mesh, index) {
var figure = mesh.figures[index];
assert(figure.type === 'patch', 'Unexpected patch mesh figure');
var coords = mesh.coords, colors = mesh.colors;
var pi = figure.coords;
var ci = figure.colors;
var figureMinX = Math.min(coords[pi[0]][0], coords[pi[3]][0],
coords[pi[12]][0], coords[pi[15]][0]);
var figureMinY = Math.min(coords[pi[0]][1], coords[pi[3]][1],
coords[pi[12]][1], coords[pi[15]][1]);
var figureMaxX = Math.max(coords[pi[0]][0], coords[pi[3]][0],
coords[pi[12]][0], coords[pi[15]][0]);
var figureMaxY = Math.max(coords[pi[0]][1], coords[pi[3]][1],
coords[pi[12]][1], coords[pi[15]][1]);
var splitXBy = Math.ceil((figureMaxX - figureMinX) * TRIANGLE_DENSITY /
(mesh.bounds[2] - mesh.bounds[0]));
splitXBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitXBy));
var splitYBy = Math.ceil((figureMaxY - figureMinY) * TRIANGLE_DENSITY /
(mesh.bounds[3] - mesh.bounds[1]));
splitYBy = Math.max(MIN_SPLIT_PATCH_CHUNKS_AMOUNT,
Math.min(MAX_SPLIT_PATCH_CHUNKS_AMOUNT, splitYBy));
var verticesPerRow = splitXBy + 1;
var figureCoords = new Int32Array((splitYBy + 1) * verticesPerRow);
var figureColors = new Int32Array((splitYBy + 1) * verticesPerRow);
var k = 0;
var cl = new Uint8Array(3), cr = new Uint8Array(3);
var c0 = colors[ci[0]], c1 = colors[ci[1]],
c2 = colors[ci[2]], c3 = colors[ci[3]];
var bRow = getB(splitYBy), bCol = getB(splitXBy);
for (var row = 0; row <= splitYBy; row++) {
cl[0] = ((c0[0] * (splitYBy - row) + c2[0] * row) / splitYBy) | 0;
cl[1] = ((c0[1] * (splitYBy - row) + c2[1] * row) / splitYBy) | 0;
cl[2] = ((c0[2] * (splitYBy - row) + c2[2] * row) / splitYBy) | 0;
cr[0] = ((c1[0] * (splitYBy - row) + c3[0] * row) / splitYBy) | 0;
cr[1] = ((c1[1] * (splitYBy - row) + c3[1] * row) / splitYBy) | 0;
cr[2] = ((c1[2] * (splitYBy - row) + c3[2] * row) / splitYBy) | 0;
for (var col = 0; col <= splitXBy; col++, k++) {
if ((row === 0 || row === splitYBy) &&
(col === 0 || col === splitXBy)) {
continue;
}
var x = 0, y = 0;
var q = 0;
for (var i = 0; i <= 3; i++) {
for (var j = 0; j <= 3; j++, q++) {
var m = bRow[row][i] * bCol[col][j];
x += coords[pi[q]][0] * m;
y += coords[pi[q]][1] * m;
}
}
figureCoords[k] = coords.length;
coords.push([x, y]);
figureColors[k] = colors.length;
var newColor = new Uint8Array(3);
newColor[0] = ((cl[0] * (splitXBy - col) + cr[0] * col) / splitXBy) | 0;
newColor[1] = ((cl[1] * (splitXBy - col) + cr[1] * col) / splitXBy) | 0;
newColor[2] = ((cl[2] * (splitXBy - col) + cr[2] * col) / splitXBy) | 0;
colors.push(newColor);
}
}
figureCoords[0] = pi[0];
figureColors[0] = ci[0];
figureCoords[splitXBy] = pi[3];
figureColors[splitXBy] = ci[1];
figureCoords[verticesPerRow * splitYBy] = pi[12];
figureColors[verticesPerRow * splitYBy] = ci[2];
figureCoords[verticesPerRow * splitYBy + splitXBy] = pi[15];
figureColors[verticesPerRow * splitYBy + splitXBy] = ci[3];
mesh.figures[index] = {
type: 'lattice',
coords: figureCoords,
colors: figureColors,
verticesPerRow: verticesPerRow
};
}
function decodeType6Shading(mesh, reader) {
// A special case of Type 7. The p11, p12, p21, p22 automatically filled
var coords = mesh.coords;
var colors = mesh.colors;
var ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
var cs = new Int32Array(4); // c00, c30, c03, c33
while (reader.hasData) {
var f = reader.readFlag();
assert(0 <= f && f <= 3, 'Unknown type6 flag');
var i, ii;
var pi = coords.length;
for (i = 0, ii = (f !== 0 ? 8 : 12); i < ii; i++) {
coords.push(reader.readCoordinate());
}
var ci = colors.length;
for (i = 0, ii = (f !== 0 ? 2 : 4); i < ii; i++) {
colors.push(reader.readComponents());
}
var tmp1, tmp2, tmp3, tmp4;
switch (f) {
case 0:
ps[12] = pi + 3; ps[13] = pi + 4; ps[14] = pi + 5; ps[15] = pi + 6;
ps[ 8] = pi + 2; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 7;
ps[ 4] = pi + 1; /* calculated below */ ps[ 7] = pi + 8;
ps[ 0] = pi; ps[ 1] = pi + 11; ps[ 2] = pi + 10; ps[ 3] = pi + 9;
cs[2] = ci + 1; cs[3] = ci + 2;
cs[0] = ci; cs[1] = ci + 3;
break;
case 1:
tmp1 = ps[12]; tmp2 = ps[13]; tmp3 = ps[14]; tmp4 = ps[15];
ps[12] = pi + 5; ps[13] = pi + 4; ps[14] = pi + 3; ps[15] = pi + 2;
ps[ 8] = pi + 6; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 1;
ps[ 4] = pi + 7; /* calculated below */ ps[ 7] = pi;
ps[ 0] = tmp1; ps[ 1] = tmp2; ps[ 2] = tmp3; ps[ 3] = tmp4;
tmp1 = cs[2]; tmp2 = cs[3];
cs[2] = ci + 1; cs[3] = ci;
cs[0] = tmp1; cs[1] = tmp2;
break;
case 2:
ps[12] = ps[15]; ps[13] = pi + 7; ps[14] = pi + 6; ps[15] = pi + 5;
ps[ 8] = ps[11]; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 4;
ps[ 4] = ps[7]; /* calculated below */ ps[ 7] = pi + 3;
ps[ 0] = ps[3]; ps[ 1] = pi; ps[ 2] = pi + 1; ps[ 3] = pi + 2;
cs[2] = cs[3]; cs[3] = ci + 1;
cs[0] = cs[1]; cs[1] = ci;
break;
case 3:
ps[12] = ps[0]; ps[13] = ps[1]; ps[14] = ps[2]; ps[15] = ps[3];
ps[ 8] = pi; /* values for 5, 6, 9, 10 are */ ps[11] = pi + 7;
ps[ 4] = pi + 1; /* calculated below */ ps[ 7] = pi + 6;
ps[ 0] = pi + 2; ps[ 1] = pi + 3; ps[ 2] = pi + 4; ps[ 3] = pi + 5;
cs[2] = cs[0]; cs[3] = cs[1];
cs[0] = ci; cs[1] = ci + 1;
break;
}
// set p11, p12, p21, p22
ps[5] = coords.length;
coords.push([
(-4 * coords[ps[0]][0] - coords[ps[15]][0] +
6 * (coords[ps[4]][0] + coords[ps[1]][0]) -
2 * (coords[ps[12]][0] + coords[ps[3]][0]) +
3 * (coords[ps[13]][0] + coords[ps[7]][0])) / 9,
(-4 * coords[ps[0]][1] - coords[ps[15]][1] +
6 * (coords[ps[4]][1] + coords[ps[1]][1]) -
2 * (coords[ps[12]][1] + coords[ps[3]][1]) +
3 * (coords[ps[13]][1] + coords[ps[7]][1])) / 9
]);
ps[6] = coords.length;
coords.push([
(-4 * coords[ps[3]][0] - coords[ps[12]][0] +
6 * (coords[ps[2]][0] + coords[ps[7]][0]) -
2 * (coords[ps[0]][0] + coords[ps[15]][0]) +
3 * (coords[ps[4]][0] + coords[ps[14]][0])) / 9,
(-4 * coords[ps[3]][1] - coords[ps[12]][1] +
6 * (coords[ps[2]][1] + coords[ps[7]][1]) -
2 * (coords[ps[0]][1] + coords[ps[15]][1]) +
3 * (coords[ps[4]][1] + coords[ps[14]][1])) / 9
]);
ps[9] = coords.length;
coords.push([
(-4 * coords[ps[12]][0] - coords[ps[3]][0] +
6 * (coords[ps[8]][0] + coords[ps[13]][0]) -
2 * (coords[ps[0]][0] + coords[ps[15]][0]) +
3 * (coords[ps[11]][0] + coords[ps[1]][0])) / 9,
(-4 * coords[ps[12]][1] - coords[ps[3]][1] +
6 * (coords[ps[8]][1] + coords[ps[13]][1]) -
2 * (coords[ps[0]][1] + coords[ps[15]][1]) +
3 * (coords[ps[11]][1] + coords[ps[1]][1])) / 9
]);
ps[10] = coords.length;
coords.push([
(-4 * coords[ps[15]][0] - coords[ps[0]][0] +
6 * (coords[ps[11]][0] + coords[ps[14]][0]) -
2 * (coords[ps[12]][0] + coords[ps[3]][0]) +
3 * (coords[ps[2]][0] + coords[ps[8]][0])) / 9,
(-4 * coords[ps[15]][1] - coords[ps[0]][1] +
6 * (coords[ps[11]][1] + coords[ps[14]][1]) -
2 * (coords[ps[12]][1] + coords[ps[3]][1]) +
3 * (coords[ps[2]][1] + coords[ps[8]][1])) / 9
]);
mesh.figures.push({
type: 'patch',
coords: new Int32Array(ps), // making copies of ps and cs
colors: new Int32Array(cs)
});
}
}
function decodeType7Shading(mesh, reader) {
var coords = mesh.coords;
var colors = mesh.colors;
var ps = new Int32Array(16); // p00, p10, ..., p30, p01, ..., p33
var cs = new Int32Array(4); // c00, c30, c03, c33
while (reader.hasData) {
var f = reader.readFlag();
assert(0 <= f && f <= 3, 'Unknown type7 flag');
var i, ii;
var pi = coords.length;
for (i = 0, ii = (f !== 0 ? 12 : 16); i < ii; i++) {
coords.push(reader.readCoordinate());
}
var ci = colors.length;
for (i = 0, ii = (f !== 0 ? 2 : 4); i < ii; i++) {
colors.push(reader.readComponents());
}
var tmp1, tmp2, tmp3, tmp4;
switch (f) {
case 0:
ps[12] = pi + 3; ps[13] = pi + 4; ps[14] = pi + 5; ps[15] = pi + 6;
ps[ 8] = pi + 2; ps[ 9] = pi + 13; ps[10] = pi + 14; ps[11] = pi + 7;
ps[ 4] = pi + 1; ps[ 5] = pi + 12; ps[ 6] = pi + 15; ps[ 7] = pi + 8;
ps[ 0] = pi; ps[ 1] = pi + 11; ps[ 2] = pi + 10; ps[ 3] = pi + 9;
cs[2] = ci + 1; cs[3] = ci + 2;
cs[0] = ci; cs[1] = ci + 3;
break;
case 1:
tmp1 = ps[12]; tmp2 = ps[13]; tmp3 = ps[14]; tmp4 = ps[15];
ps[12] = pi + 5; ps[13] = pi + 4; ps[14] = pi + 3; ps[15] = pi + 2;
ps[ 8] = pi + 6; ps[ 9] = pi + 11; ps[10] = pi + 10; ps[11] = pi + 1;
ps[ 4] = pi + 7; ps[ 5] = pi + 8; ps[ 6] = pi + 9; ps[ 7] = pi;
ps[ 0] = tmp1; ps[ 1] = tmp2; ps[ 2] = tmp3; ps[ 3] = tmp4;
tmp1 = cs[2]; tmp2 = cs[3];
cs[2] = ci + 1; cs[3] = ci;
cs[0] = tmp1; cs[1] = tmp2;
break;
case 2:
ps[12] = ps[15]; ps[13] = pi + 7; ps[14] = pi + 6; ps[15] = pi + 5;
ps[ 8] = ps[11]; ps[ 9] = pi + 8; ps[10] = pi + 11; ps[11] = pi + 4;
ps[ 4] = ps[7]; ps[ 5] = pi + 9; ps[ 6] = pi + 10; ps[ 7] = pi + 3;
ps[ 0] = ps[3]; ps[ 1] = pi; ps[ 2] = pi + 1; ps[ 3] = pi + 2;
cs[2] = cs[3]; cs[3] = ci + 1;
cs[0] = cs[1]; cs[1] = ci;
break;
case 3:
ps[12] = ps[0]; ps[13] = ps[1]; ps[14] = ps[2]; ps[15] = ps[3];
ps[ 8] = pi; ps[ 9] = pi + 9; ps[10] = pi + 8; ps[11] = pi + 7;
ps[ 4] = pi + 1; ps[ 5] = pi + 10; ps[ 6] = pi + 11; ps[ 7] = pi + 6;
ps[ 0] = pi + 2; ps[ 1] = pi + 3; ps[ 2] = pi + 4; ps[ 3] = pi + 5;
cs[2] = cs[0]; cs[3] = cs[1];
cs[0] = ci; cs[1] = ci + 1;
break;
}
mesh.figures.push({
type: 'patch',
coords: new Int32Array(ps), // making copies of ps and cs
colors: new Int32Array(cs)
});
}
}
function updateBounds(mesh) {
var minX = mesh.coords[0][0], minY = mesh.coords[0][1],
maxX = minX, maxY = minY;
for (var i = 1, ii = mesh.coords.length; i < ii; i++) {
var x = mesh.coords[i][0], y = mesh.coords[i][1];
minX = minX > x ? x : minX;
minY = minY > y ? y : minY;
maxX = maxX < x ? x : maxX;
maxY = maxY < y ? y : maxY;
}
mesh.bounds = [minX, minY, maxX, maxY];
}
function packData(mesh) {
var i, ii, j, jj;
var coords = mesh.coords;
var coordsPacked = new Float32Array(coords.length * 2);
for (i = 0, j = 0, ii = coords.length; i < ii; i++) {
var xy = coords[i];
coordsPacked[j++] = xy[0];
coordsPacked[j++] = xy[1];
}
mesh.coords = coordsPacked;
var colors = mesh.colors;
var colorsPacked = new Uint8Array(colors.length * 3);
for (i = 0, j = 0, ii = colors.length; i < ii; i++) {
var c = colors[i];
colorsPacked[j++] = c[0];
colorsPacked[j++] = c[1];
colorsPacked[j++] = c[2];
}
mesh.colors = colorsPacked;
var figures = mesh.figures;
for (i = 0, ii = figures.length; i < ii; i++) {
var figure = figures[i], ps = figure.coords, cs = figure.colors;
for (j = 0, jj = ps.length; j < jj; j++) {
ps[j] *= 2;
cs[j] *= 3;
}
}
}
function Mesh(stream, matrix, xref, res) {
assert(isStream(stream), 'Mesh data is not a stream');
var dict = stream.dict;
this.matrix = matrix;
this.shadingType = dict.get('ShadingType');
this.type = 'Pattern';
this.bbox = dict.get('BBox');
var cs = dict.get('ColorSpace', 'CS');
cs = ColorSpace.parse(cs, xref, res);
this.cs = cs;
this.background = dict.has('Background') ?
cs.getRgb(dict.get('Background'), 0) : null;
var fnObj = dict.get('Function');
var fn = fnObj ? PDFFunction.parseArray(xref, fnObj) : null;
this.coords = [];
this.colors = [];
this.figures = [];
var decodeContext = {
bitsPerCoordinate: dict.get('BitsPerCoordinate'),
bitsPerComponent: dict.get('BitsPerComponent'),
bitsPerFlag: dict.get('BitsPerFlag'),
decode: dict.get('Decode'),
colorFn: fn,
colorSpace: cs,
numComps: fn ? 1 : cs.numComps
};
var reader = new MeshStreamReader(stream, decodeContext);
var patchMesh = false;
switch (this.shadingType) {
case PatternType.FREE_FORM_MESH:
decodeType4Shading(this, reader);
break;
case PatternType.LATTICE_FORM_MESH:
var verticesPerRow = dict.get('VerticesPerRow') | 0;
assert(verticesPerRow >= 2, 'Invalid VerticesPerRow');
decodeType5Shading(this, reader, verticesPerRow);
break;
case PatternType.COONS_PATCH_MESH:
decodeType6Shading(this, reader);
patchMesh = true;
break;
case PatternType.TENSOR_PATCH_MESH:
decodeType7Shading(this, reader);
patchMesh = true;
break;
default:
error('Unsupported mesh type.');
break;
}
if (patchMesh) {
// dirty bounds calculation for determining, how dense shall be triangles
updateBounds(this);
for (var i = 0, ii = this.figures.length; i < ii; i++) {
buildFigureFromPatch(this, i);
}
}
// calculate bounds
updateBounds(this);
packData(this);
}
Mesh.prototype = {
getIR: function Mesh_getIR() {
return ['Mesh', this.shadingType, this.coords, this.colors, this.figures,
this.bounds, this.matrix, this.bbox, this.background];
}
};
return Mesh;
})();
Shadings.Dummy = (function DummyClosure() {
function Dummy() {
this.type = 'Pattern';
}
Dummy.prototype = {
getIR: function Dummy_getIR() {
return ['Dummy'];
}
};
return Dummy;
})();
function getTilingPatternIR(operatorList, dict, args) {
var matrix = dict.get('Matrix');
var bbox = dict.get('BBox');
var xstep = dict.get('XStep');
var ystep = dict.get('YStep');
var paintType = dict.get('PaintType');
var tilingType = dict.get('TilingType');
return [
'TilingPattern', args, operatorList, matrix, bbox, xstep, ystep,
paintType, tilingType
];
}

View File

@@ -0,0 +1,225 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals NotImplementedException, MissingDataException, Promise, Stream,
PDFDocument, ChunkedStreamManager, createPromiseCapability */
'use strict';
// The maximum number of bytes fetched per range request
var RANGE_CHUNK_SIZE = 65536;
// TODO(mack): Make use of PDFJS.Util.inherit() when it becomes available
var BasePdfManager = (function BasePdfManagerClosure() {
function BasePdfManager() {
throw new Error('Cannot initialize BaseManagerManager');
}
BasePdfManager.prototype = {
onLoadedStream: function BasePdfManager_onLoadedStream() {
throw new NotImplementedException();
},
ensureDoc: function BasePdfManager_ensureDoc(prop, args) {
return this.ensure(this.pdfDocument, prop, args);
},
ensureXRef: function BasePdfManager_ensureXRef(prop, args) {
return this.ensure(this.pdfDocument.xref, prop, args);
},
ensureCatalog: function BasePdfManager_ensureCatalog(prop, args) {
return this.ensure(this.pdfDocument.catalog, prop, args);
},
getPage: function BasePdfManager_pagePage(pageIndex) {
return this.pdfDocument.getPage(pageIndex);
},
cleanup: function BasePdfManager_cleanup() {
return this.pdfDocument.cleanup();
},
ensure: function BasePdfManager_ensure(obj, prop, args) {
return new NotImplementedException();
},
requestRange: function BasePdfManager_ensure(begin, end) {
return new NotImplementedException();
},
requestLoadedStream: function BasePdfManager_requestLoadedStream() {
return new NotImplementedException();
},
sendProgressiveData: function BasePdfManager_sendProgressiveData(chunk) {
return new NotImplementedException();
},
updatePassword: function BasePdfManager_updatePassword(password) {
this.pdfDocument.xref.password = this.password = password;
if (this._passwordChangedCapability) {
this._passwordChangedCapability.resolve();
}
},
passwordChanged: function BasePdfManager_passwordChanged() {
this._passwordChangedCapability = createPromiseCapability();
return this._passwordChangedCapability.promise;
},
terminate: function BasePdfManager_terminate() {
return new NotImplementedException();
}
};
return BasePdfManager;
})();
var LocalPdfManager = (function LocalPdfManagerClosure() {
function LocalPdfManager(data, password) {
var stream = new Stream(data);
this.pdfDocument = new PDFDocument(this, stream, password);
this._loadedStreamCapability = createPromiseCapability();
this._loadedStreamCapability.resolve(stream);
}
LocalPdfManager.prototype = Object.create(BasePdfManager.prototype);
LocalPdfManager.prototype.constructor = LocalPdfManager;
LocalPdfManager.prototype.ensure =
function LocalPdfManager_ensure(obj, prop, args) {
return new Promise(function (resolve, reject) {
try {
var value = obj[prop];
var result;
if (typeof value === 'function') {
result = value.apply(obj, args);
} else {
result = value;
}
resolve(result);
} catch (e) {
reject(e);
}
});
};
LocalPdfManager.prototype.requestRange =
function LocalPdfManager_requestRange(begin, end) {
return Promise.resolve();
};
LocalPdfManager.prototype.requestLoadedStream =
function LocalPdfManager_requestLoadedStream() {
};
LocalPdfManager.prototype.onLoadedStream =
function LocalPdfManager_getLoadedStream() {
return this._loadedStreamCapability.promise;
};
LocalPdfManager.prototype.terminate =
function LocalPdfManager_terminate() {
return;
};
return LocalPdfManager;
})();
var NetworkPdfManager = (function NetworkPdfManagerClosure() {
function NetworkPdfManager(args, msgHandler) {
this.msgHandler = msgHandler;
var params = {
msgHandler: msgHandler,
httpHeaders: args.httpHeaders,
withCredentials: args.withCredentials,
chunkedViewerLoading: args.chunkedViewerLoading,
disableAutoFetch: args.disableAutoFetch,
initialData: args.initialData
};
this.streamManager = new ChunkedStreamManager(args.length, RANGE_CHUNK_SIZE,
args.url, params);
this.pdfDocument = new PDFDocument(this, this.streamManager.getStream(),
args.password);
}
NetworkPdfManager.prototype = Object.create(BasePdfManager.prototype);
NetworkPdfManager.prototype.constructor = NetworkPdfManager;
NetworkPdfManager.prototype.ensure =
function NetworkPdfManager_ensure(obj, prop, args) {
var pdfManager = this;
return new Promise(function (resolve, reject) {
function ensureHelper() {
try {
var result;
var value = obj[prop];
if (typeof value === 'function') {
result = value.apply(obj, args);
} else {
result = value;
}
resolve(result);
} catch(e) {
if (!(e instanceof MissingDataException)) {
reject(e);
return;
}
pdfManager.streamManager.requestRange(e.begin, e.end, ensureHelper);
}
}
ensureHelper();
});
};
NetworkPdfManager.prototype.requestRange =
function NetworkPdfManager_requestRange(begin, end) {
return new Promise(function (resolve) {
this.streamManager.requestRange(begin, end, function() {
resolve();
});
}.bind(this));
};
NetworkPdfManager.prototype.requestLoadedStream =
function NetworkPdfManager_requestLoadedStream() {
this.streamManager.requestAllChunks();
};
NetworkPdfManager.prototype.sendProgressiveData =
function NetworkPdfManager_sendProgressiveData(chunk) {
this.streamManager.onReceiveData({ chunk: chunk });
};
NetworkPdfManager.prototype.onLoadedStream =
function NetworkPdfManager_getLoadedStream() {
return this.streamManager.onLoadedStream();
};
NetworkPdfManager.prototype.terminate =
function NetworkPdfManager_terminate() {
this.streamManager.networkManager.abortAllRequests();
};
return NetworkPdfManager;
})();

View File

@@ -0,0 +1,222 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2014 Mozilla Foundation
*
* 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.
*/
/* globals EOF, error, Lexer */
'use strict';
var PostScriptParser = (function PostScriptParserClosure() {
function PostScriptParser(lexer) {
this.lexer = lexer;
this.operators = [];
this.token = null;
this.prev = null;
}
PostScriptParser.prototype = {
nextToken: function PostScriptParser_nextToken() {
this.prev = this.token;
this.token = this.lexer.getToken();
},
accept: function PostScriptParser_accept(type) {
if (this.token.type === type) {
this.nextToken();
return true;
}
return false;
},
expect: function PostScriptParser_expect(type) {
if (this.accept(type)) {
return true;
}
error('Unexpected symbol: found ' + this.token.type + ' expected ' +
type + '.');
},
parse: function PostScriptParser_parse() {
this.nextToken();
this.expect(PostScriptTokenTypes.LBRACE);
this.parseBlock();
this.expect(PostScriptTokenTypes.RBRACE);
return this.operators;
},
parseBlock: function PostScriptParser_parseBlock() {
while (true) {
if (this.accept(PostScriptTokenTypes.NUMBER)) {
this.operators.push(this.prev.value);
} else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
this.operators.push(this.prev.value);
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
this.parseCondition();
} else {
return;
}
}
},
parseCondition: function PostScriptParser_parseCondition() {
// Add two place holders that will be updated later
var conditionLocation = this.operators.length;
this.operators.push(null, null);
this.parseBlock();
this.expect(PostScriptTokenTypes.RBRACE);
if (this.accept(PostScriptTokenTypes.IF)) {
// The true block is right after the 'if' so it just falls through on
// true else it jumps and skips the true block.
this.operators[conditionLocation] = this.operators.length;
this.operators[conditionLocation + 1] = 'jz';
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
var jumpLocation = this.operators.length;
this.operators.push(null, null);
var endOfTrue = this.operators.length;
this.parseBlock();
this.expect(PostScriptTokenTypes.RBRACE);
this.expect(PostScriptTokenTypes.IFELSE);
// The jump is added at the end of the true block to skip the false
// block.
this.operators[jumpLocation] = this.operators.length;
this.operators[jumpLocation + 1] = 'j';
this.operators[conditionLocation] = endOfTrue;
this.operators[conditionLocation + 1] = 'jz';
} else {
error('PS Function: error parsing conditional.');
}
}
};
return PostScriptParser;
})();
var PostScriptTokenTypes = {
LBRACE: 0,
RBRACE: 1,
NUMBER: 2,
OPERATOR: 3,
IF: 4,
IFELSE: 5
};
var PostScriptToken = (function PostScriptTokenClosure() {
function PostScriptToken(type, value) {
this.type = type;
this.value = value;
}
var opCache = {};
PostScriptToken.getOperator = function PostScriptToken_getOperator(op) {
var opValue = opCache[op];
if (opValue) {
return opValue;
}
return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
};
PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE,
'{');
PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE,
'}');
PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF');
PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE,
'IFELSE');
return PostScriptToken;
})();
var PostScriptLexer = (function PostScriptLexerClosure() {
function PostScriptLexer(stream) {
this.stream = stream;
this.nextChar();
this.strBuf = [];
}
PostScriptLexer.prototype = {
nextChar: function PostScriptLexer_nextChar() {
return (this.currentChar = this.stream.getByte());
},
getToken: function PostScriptLexer_getToken() {
var comment = false;
var ch = this.currentChar;
// skip comments
while (true) {
if (ch < 0) {
return EOF;
}
if (comment) {
if (ch === 0x0A || ch === 0x0D) {
comment = false;
}
} else if (ch === 0x25) { // '%'
comment = true;
} else if (!Lexer.isSpace(ch)) {
break;
}
ch = this.nextChar();
}
switch (ch | 0) {
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: // '0'-'4'
case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: // '5'-'9'
case 0x2B: case 0x2D: case 0x2E: // '+', '-', '.'
return new PostScriptToken(PostScriptTokenTypes.NUMBER,
this.getNumber());
case 0x7B: // '{'
this.nextChar();
return PostScriptToken.LBRACE;
case 0x7D: // '}'
this.nextChar();
return PostScriptToken.RBRACE;
}
// operator
var strBuf = this.strBuf;
strBuf.length = 0;
strBuf[0] = String.fromCharCode(ch);
while ((ch = this.nextChar()) >= 0 && // and 'A'-'Z', 'a'-'z'
((ch >= 0x41 && ch <= 0x5A) || (ch >= 0x61 && ch <= 0x7A))) {
strBuf.push(String.fromCharCode(ch));
}
var str = strBuf.join('');
switch (str.toLowerCase()) {
case 'if':
return PostScriptToken.IF;
case 'ifelse':
return PostScriptToken.IFELSE;
default:
return PostScriptToken.getOperator(str);
}
},
getNumber: function PostScriptLexer_getNumber() {
var ch = this.currentChar;
var strBuf = this.strBuf;
strBuf.length = 0;
strBuf[0] = String.fromCharCode(ch);
while ((ch = this.nextChar()) >= 0) {
if ((ch >= 0x30 && ch <= 0x39) || // '0'-'9'
ch === 0x2D || ch === 0x2E) { // '-', '.'
strBuf.push(String.fromCharCode(ch));
} else {
break;
}
}
var value = parseFloat(strBuf.join(''));
if (isNaN(value)) {
error('Invalid floating point number: ' + value);
}
return value;
}
};
return PostScriptLexer;
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,499 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals PDFJS, createPromiseCapability, LocalPdfManager, NetworkPdfManager,
NetworkManager, isInt, RANGE_CHUNK_SIZE, MissingPDFException,
UnexpectedResponseException, PasswordException, Promise, warn,
PasswordResponses, InvalidPDFException, UnknownErrorException,
XRefParseException, Ref, info, globalScope, error, MessageHandler */
'use strict';
var WorkerMessageHandler = PDFJS.WorkerMessageHandler = {
setup: function wphSetup(handler) {
var pdfManager;
function loadDocument(recoveryMode) {
var loadDocumentCapability = createPromiseCapability();
var parseSuccess = function parseSuccess() {
var numPagesPromise = pdfManager.ensureDoc('numPages');
var fingerprintPromise = pdfManager.ensureDoc('fingerprint');
var encryptedPromise = pdfManager.ensureXRef('encrypt');
Promise.all([numPagesPromise, fingerprintPromise,
encryptedPromise]).then(function onDocReady(results) {
var doc = {
numPages: results[0],
fingerprint: results[1],
encrypted: !!results[2],
};
loadDocumentCapability.resolve(doc);
},
parseFailure);
};
var parseFailure = function parseFailure(e) {
loadDocumentCapability.reject(e);
};
pdfManager.ensureDoc('checkHeader', []).then(function() {
pdfManager.ensureDoc('parseStartXRef', []).then(function() {
pdfManager.ensureDoc('parse', [recoveryMode]).then(
parseSuccess, parseFailure);
}, parseFailure);
}, parseFailure);
return loadDocumentCapability.promise;
}
function getPdfManager(data) {
var pdfManagerCapability = createPromiseCapability();
var source = data.source;
var disableRange = data.disableRange;
if (source.data) {
try {
pdfManager = new LocalPdfManager(source.data, source.password);
pdfManagerCapability.resolve();
} catch (ex) {
pdfManagerCapability.reject(ex);
}
return pdfManagerCapability.promise;
} else if (source.chunkedViewerLoading) {
try {
pdfManager = new NetworkPdfManager(source, handler);
pdfManagerCapability.resolve();
} catch (ex) {
pdfManagerCapability.reject(ex);
}
return pdfManagerCapability.promise;
}
var networkManager = new NetworkManager(source.url, {
httpHeaders: source.httpHeaders,
withCredentials: source.withCredentials
});
var cachedChunks = [];
var fullRequestXhrId = networkManager.requestFull({
onHeadersReceived: function onHeadersReceived() {
if (disableRange) {
return;
}
var fullRequestXhr = networkManager.getRequestXhr(fullRequestXhrId);
if (fullRequestXhr.getResponseHeader('Accept-Ranges') !== 'bytes') {
return;
}
var contentEncoding =
fullRequestXhr.getResponseHeader('Content-Encoding') || 'identity';
if (contentEncoding !== 'identity') {
return;
}
var length = fullRequestXhr.getResponseHeader('Content-Length');
length = parseInt(length, 10);
if (!isInt(length)) {
return;
}
source.length = length;
if (length <= 2 * RANGE_CHUNK_SIZE) {
// The file size is smaller than the size of two chunks, so it does
// not make any sense to abort the request and retry with a range
// request.
return;
}
if (networkManager.isStreamingRequest(fullRequestXhrId)) {
// We can continue fetching when progressive loading is enabled,
// and we don't need the autoFetch feature.
source.disableAutoFetch = true;
} else {
// NOTE: by cancelling the full request, and then issuing range
// requests, there will be an issue for sites where you can only
// request the pdf once. However, if this is the case, then the
// server should not be returning that it can support range
// requests.
networkManager.abortRequest(fullRequestXhrId);
}
try {
pdfManager = new NetworkPdfManager(source, handler);
pdfManagerCapability.resolve(pdfManager);
} catch (ex) {
pdfManagerCapability.reject(ex);
}
},
onProgressiveData: source.disableStream ? null :
function onProgressiveData(chunk) {
if (!pdfManager) {
cachedChunks.push(chunk);
return;
}
pdfManager.sendProgressiveData(chunk);
},
onDone: function onDone(args) {
if (pdfManager) {
return; // already processed
}
var pdfFile;
if (args === null) {
// TODO add some streaming manager, e.g. for unknown length files.
// The data was returned in the onProgressiveData, combining...
var pdfFileLength = 0, pos = 0;
cachedChunks.forEach(function (chunk) {
pdfFileLength += chunk.byteLength;
});
if (source.length && pdfFileLength !== source.length) {
warn('reported HTTP length is different from actual');
}
var pdfFileArray = new Uint8Array(pdfFileLength);
cachedChunks.forEach(function (chunk) {
pdfFileArray.set(new Uint8Array(chunk), pos);
pos += chunk.byteLength;
});
pdfFile = pdfFileArray.buffer;
} else {
pdfFile = args.chunk;
}
// the data is array, instantiating directly from it
try {
pdfManager = new LocalPdfManager(pdfFile, source.password);
pdfManagerCapability.resolve();
} catch (ex) {
pdfManagerCapability.reject(ex);
}
},
onError: function onError(status) {
var exception;
if (status === 404) {
exception = new MissingPDFException('Missing PDF "' +
source.url + '".');
handler.send('MissingPDF', exception);
} else {
exception = new UnexpectedResponseException(
'Unexpected server response (' + status +
') while retrieving PDF "' + source.url + '".', status);
handler.send('UnexpectedResponse', exception);
}
},
onProgress: function onProgress(evt) {
handler.send('DocProgress', {
loaded: evt.loaded,
total: evt.lengthComputable ? evt.total : source.length
});
}
});
return pdfManagerCapability.promise;
}
handler.on('test', function wphSetupTest(data) {
// check if Uint8Array can be sent to worker
if (!(data instanceof Uint8Array)) {
handler.send('test', false);
return;
}
// making sure postMessage transfers are working
var supportTransfers = data[0] === 255;
handler.postMessageTransfers = supportTransfers;
// check if the response property is supported by xhr
var xhr = new XMLHttpRequest();
var responseExists = 'response' in xhr;
// check if the property is actually implemented
try {
var dummy = xhr.responseType;
} catch (e) {
responseExists = false;
}
if (!responseExists) {
handler.send('test', false);
return;
}
handler.send('test', {
supportTypedArray: true,
supportTransfers: supportTransfers
});
});
handler.on('GetDocRequest', function wphSetupDoc(data) {
var onSuccess = function(doc) {
handler.send('GetDoc', { pdfInfo: doc });
};
var onFailure = function(e) {
if (e instanceof PasswordException) {
if (e.code === PasswordResponses.NEED_PASSWORD) {
handler.send('NeedPassword', e);
} else if (e.code === PasswordResponses.INCORRECT_PASSWORD) {
handler.send('IncorrectPassword', e);
}
} else if (e instanceof InvalidPDFException) {
handler.send('InvalidPDF', e);
} else if (e instanceof MissingPDFException) {
handler.send('MissingPDF', e);
} else if (e instanceof UnexpectedResponseException) {
handler.send('UnexpectedResponse', e);
} else {
handler.send('UnknownError',
new UnknownErrorException(e.message, e.toString()));
}
};
PDFJS.maxImageSize = data.maxImageSize === undefined ?
-1 : data.maxImageSize;
PDFJS.disableFontFace = data.disableFontFace;
PDFJS.disableCreateObjectURL = data.disableCreateObjectURL;
PDFJS.verbosity = data.verbosity;
PDFJS.cMapUrl = data.cMapUrl === undefined ?
null : data.cMapUrl;
PDFJS.cMapPacked = data.cMapPacked === true;
getPdfManager(data).then(function () {
handler.send('PDFManagerReady', null);
pdfManager.onLoadedStream().then(function(stream) {
handler.send('DataLoaded', { length: stream.bytes.byteLength });
});
}).then(function pdfManagerReady() {
loadDocument(false).then(onSuccess, function loadFailure(ex) {
// Try again with recoveryMode == true
if (!(ex instanceof XRefParseException)) {
if (ex instanceof PasswordException) {
// after password exception prepare to receive a new password
// to repeat loading
pdfManager.passwordChanged().then(pdfManagerReady);
}
onFailure(ex);
return;
}
pdfManager.requestLoadedStream();
pdfManager.onLoadedStream().then(function() {
loadDocument(true).then(onSuccess, onFailure);
});
}, onFailure);
}, onFailure);
});
handler.on('GetPage', function wphSetupGetPage(data) {
return pdfManager.getPage(data.pageIndex).then(function(page) {
var rotatePromise = pdfManager.ensure(page, 'rotate');
var refPromise = pdfManager.ensure(page, 'ref');
var viewPromise = pdfManager.ensure(page, 'view');
return Promise.all([rotatePromise, refPromise, viewPromise]).then(
function(results) {
return {
rotate: results[0],
ref: results[1],
view: results[2]
};
});
});
});
handler.on('GetPageIndex', function wphSetupGetPageIndex(data) {
var ref = new Ref(data.ref.num, data.ref.gen);
var catalog = pdfManager.pdfDocument.catalog;
return catalog.getPageIndex(ref);
});
handler.on('GetDestinations',
function wphSetupGetDestinations(data) {
return pdfManager.ensureCatalog('destinations');
}
);
handler.on('GetDestination',
function wphSetupGetDestination(data) {
return pdfManager.ensureCatalog('getDestination', [ data.id ]);
}
);
handler.on('GetAttachments',
function wphSetupGetAttachments(data) {
return pdfManager.ensureCatalog('attachments');
}
);
handler.on('GetJavaScript',
function wphSetupGetJavaScript(data) {
return pdfManager.ensureCatalog('javaScript');
}
);
handler.on('GetOutline',
function wphSetupGetOutline(data) {
return pdfManager.ensureCatalog('documentOutline');
}
);
handler.on('GetMetadata',
function wphSetupGetMetadata(data) {
return Promise.all([pdfManager.ensureDoc('documentInfo'),
pdfManager.ensureCatalog('metadata')]);
}
);
handler.on('GetData', function wphSetupGetData(data) {
pdfManager.requestLoadedStream();
return pdfManager.onLoadedStream().then(function(stream) {
return stream.bytes;
});
});
handler.on('GetStats',
function wphSetupGetStats(data) {
return pdfManager.pdfDocument.xref.stats;
}
);
handler.on('UpdatePassword', function wphSetupUpdatePassword(data) {
pdfManager.updatePassword(data);
});
handler.on('GetAnnotations', function wphSetupGetAnnotations(data) {
return pdfManager.getPage(data.pageIndex).then(function(page) {
return pdfManager.ensure(page, 'getAnnotationsData', []);
});
});
handler.on('RenderPageRequest', function wphSetupRenderPage(data) {
pdfManager.getPage(data.pageIndex).then(function(page) {
var pageNum = data.pageIndex + 1;
var start = Date.now();
// Pre compile the pdf page and fetch the fonts/images.
page.getOperatorList(handler, data.intent).then(function(operatorList) {
info('page=' + pageNum + ' - getOperatorList: time=' +
(Date.now() - start) + 'ms, len=' + operatorList.fnArray.length);
}, function(e) {
var minimumStackMessage =
'worker.js: while trying to getPage() and getOperatorList()';
var wrappedException;
// Turn the error into an obj that can be serialized
if (typeof e === 'string') {
wrappedException = {
message: e,
stack: minimumStackMessage
};
} else if (typeof e === 'object') {
wrappedException = {
message: e.message || e.toString(),
stack: e.stack || minimumStackMessage
};
} else {
wrappedException = {
message: 'Unknown exception type: ' + (typeof e),
stack: minimumStackMessage
};
}
handler.send('PageError', {
pageNum: pageNum,
error: wrappedException,
intent: data.intent
});
});
});
}, this);
handler.on('GetTextContent', function wphExtractText(data) {
return pdfManager.getPage(data.pageIndex).then(function(page) {
var pageNum = data.pageIndex + 1;
var start = Date.now();
return page.extractTextContent().then(function(textContent) {
info('text indexing: page=' + pageNum + ' - time=' +
(Date.now() - start) + 'ms');
return textContent;
});
});
});
handler.on('Cleanup', function wphCleanup(data) {
return pdfManager.cleanup();
});
handler.on('Terminate', function wphTerminate(data) {
pdfManager.terminate();
});
}
};
var consoleTimer = {};
var workerConsole = {
log: function log() {
var args = Array.prototype.slice.call(arguments);
globalScope.postMessage({
action: 'console_log',
data: args
});
},
error: function error() {
var args = Array.prototype.slice.call(arguments);
globalScope.postMessage({
action: 'console_error',
data: args
});
throw 'pdf.js execution error';
},
time: function time(name) {
consoleTimer[name] = Date.now();
},
timeEnd: function timeEnd(name) {
var time = consoleTimer[name];
if (!time) {
error('Unknown timer name ' + name);
}
this.log('Timer:', name, Date.now() - time);
}
};
// Worker thread?
if (typeof window === 'undefined') {
if (!('console' in globalScope)) {
globalScope.console = workerConsole;
}
// Listen for unsupported features so we can pass them on to the main thread.
PDFJS.UnsupportedManager.listen(function (msg) {
globalScope.postMessage({
action: '_unsupported_feature',
data: msg
});
});
var handler = new MessageHandler('worker_processor', this);
WorkerMessageHandler.setup(handler);
}

View File

@@ -0,0 +1,252 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2014 Mozilla Foundation
*
* 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.
*/
/* globals PDFJS, Util, AnnotationType */
'use strict';
var HIGHLIGHT_OFFSET = 4; // px
var ANNOT_MIN_SIZE = 10; // px
var AnnotationUtils = (function AnnotationUtilsClosure() {
// TODO(mack): This dupes some of the logic in CanvasGraphics.setFont()
function setTextStyles(element, item, fontObj) {
var style = element.style;
style.fontSize = item.fontSize + 'px';
style.direction = item.fontDirection < 0 ? 'rtl': 'ltr';
if (!fontObj) {
return;
}
style.fontWeight = fontObj.black ?
(fontObj.bold ? 'bolder' : 'bold') :
(fontObj.bold ? 'bold' : 'normal');
style.fontStyle = fontObj.italic ? 'italic' : 'normal';
var fontName = fontObj.loadedName;
var fontFamily = fontName ? '"' + fontName + '", ' : '';
// Use a reasonable default font if the font doesn't specify a fallback
var fallbackName = fontObj.fallbackName || 'Helvetica, sans-serif';
style.fontFamily = fontFamily + fallbackName;
}
// TODO(mack): Remove this, it's not really that helpful.
function getEmptyContainer(tagName, rect, borderWidth) {
var bWidth = borderWidth || 0;
var element = document.createElement(tagName);
element.style.borderWidth = bWidth + 'px';
var width = rect[2] - rect[0] - 2 * bWidth;
var height = rect[3] - rect[1] - 2 * bWidth;
element.style.width = width + 'px';
element.style.height = height + 'px';
return element;
}
function initContainer(item) {
var container = getEmptyContainer('section', item.rect, item.borderWidth);
container.style.backgroundColor = item.color;
var color = item.color;
var rgb = [];
for (var i = 0; i < 3; ++i) {
rgb[i] = Math.round(color[i] * 255);
}
item.colorCssRgb = Util.makeCssRgb(rgb);
var highlight = document.createElement('div');
highlight.className = 'annotationHighlight';
highlight.style.left = highlight.style.top = -HIGHLIGHT_OFFSET + 'px';
highlight.style.right = highlight.style.bottom = -HIGHLIGHT_OFFSET + 'px';
highlight.setAttribute('hidden', true);
item.highlightElement = highlight;
container.appendChild(item.highlightElement);
return container;
}
function getHtmlElementForTextWidgetAnnotation(item, commonObjs) {
var element = getEmptyContainer('div', item.rect, 0);
element.style.display = 'table';
var content = document.createElement('div');
content.textContent = item.fieldValue;
var textAlignment = item.textAlignment;
content.style.textAlign = ['left', 'center', 'right'][textAlignment];
content.style.verticalAlign = 'middle';
content.style.display = 'table-cell';
var fontObj = item.fontRefName ?
commonObjs.getData(item.fontRefName) : null;
setTextStyles(content, item, fontObj);
element.appendChild(content);
return element;
}
function getHtmlElementForTextAnnotation(item) {
var rect = item.rect;
// sanity check because of OOo-generated PDFs
if ((rect[3] - rect[1]) < ANNOT_MIN_SIZE) {
rect[3] = rect[1] + ANNOT_MIN_SIZE;
}
if ((rect[2] - rect[0]) < ANNOT_MIN_SIZE) {
rect[2] = rect[0] + (rect[3] - rect[1]); // make it square
}
var container = initContainer(item);
container.className = 'annotText';
var image = document.createElement('img');
image.style.height = container.style.height;
image.style.width = container.style.width;
var iconName = item.name;
image.src = PDFJS.imageResourcesPath + 'annotation-' +
iconName.toLowerCase() + '.svg';
image.alt = '[{{type}} Annotation]';
image.dataset.l10nId = 'text_annotation_type';
image.dataset.l10nArgs = JSON.stringify({type: iconName});
var contentWrapper = document.createElement('div');
contentWrapper.className = 'annotTextContentWrapper';
contentWrapper.style.left = Math.floor(rect[2] - rect[0] + 5) + 'px';
contentWrapper.style.top = '-10px';
var content = document.createElement('div');
content.className = 'annotTextContent';
content.setAttribute('hidden', true);
var i, ii;
if (item.hasBgColor) {
var color = item.color;
var rgb = [];
for (i = 0; i < 3; ++i) {
// Enlighten the color (70%)
var c = Math.round(color[i] * 255);
rgb[i] = Math.round((255 - c) * 0.7) + c;
}
content.style.backgroundColor = Util.makeCssRgb(rgb);
}
var title = document.createElement('h1');
var text = document.createElement('p');
title.textContent = item.title;
if (!item.content && !item.title) {
content.setAttribute('hidden', true);
} else {
var e = document.createElement('span');
var lines = item.content.split(/(?:\r\n?|\n)/);
for (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);
var pinned = false;
var showAnnotation = function showAnnotation(pin) {
if (pin) {
pinned = true;
}
if (content.hasAttribute('hidden')) {
container.style.zIndex += 1;
content.removeAttribute('hidden');
}
};
var hideAnnotation = function hideAnnotation(unpin) {
if (unpin) {
pinned = false;
}
if (!content.hasAttribute('hidden') && !pinned) {
container.style.zIndex -= 1;
content.setAttribute('hidden', true);
}
};
var toggleAnnotation = function toggleAnnotation() {
if (pinned) {
hideAnnotation(true);
} else {
showAnnotation(true);
}
};
image.addEventListener('click', function image_clickHandler() {
toggleAnnotation();
}, false);
image.addEventListener('mouseover', function image_mouseOverHandler() {
showAnnotation();
}, false);
image.addEventListener('mouseout', function image_mouseOutHandler() {
hideAnnotation();
}, false);
content.addEventListener('click', function content_clickHandler() {
hideAnnotation(true);
}, false);
}
content.appendChild(title);
content.appendChild(text);
contentWrapper.appendChild(content);
container.appendChild(image);
container.appendChild(contentWrapper);
return container;
}
function getHtmlElementForLinkAnnotation(item) {
var container = initContainer(item);
container.className = 'annotLink';
container.style.borderColor = item.colorCssRgb;
container.style.borderStyle = 'solid';
var link = document.createElement('a');
link.href = link.title = item.url || '';
container.appendChild(link);
return container;
}
function getHtmlElement(data, objs) {
switch (data.annotationType) {
case AnnotationType.WIDGET:
return getHtmlElementForTextWidgetAnnotation(data, objs);
case AnnotationType.TEXT:
return getHtmlElementForTextAnnotation(data);
case AnnotationType.LINK:
return getHtmlElementForLinkAnnotation(data);
default:
throw new Error('Unsupported annotationType: ' + data.annotationType);
}
}
return {
getHtmlElement: getHtmlElement
};
})();
PDFJS.AnnotationUtils = AnnotationUtils;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,375 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals PDFJS, shadow, isWorker, assert, warn, bytesToString, string32,
globalScope, FontFace, Promise */
'use strict';
PDFJS.disableFontFace = false;
var FontLoader = {
insertRule: function fontLoaderInsertRule(rule) {
var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
if (!styleElement) {
styleElement = document.createElement('style');
styleElement.id = 'PDFJS_FONT_STYLE_TAG';
document.documentElement.getElementsByTagName('head')[0].appendChild(
styleElement);
}
var styleSheet = styleElement.sheet;
styleSheet.insertRule(rule, styleSheet.cssRules.length);
},
clear: function fontLoaderClear() {
var styleElement = document.getElementById('PDFJS_FONT_STYLE_TAG');
if (styleElement) {
styleElement.parentNode.removeChild(styleElement);
}
//#if !(MOZCENTRAL)
this.nativeFontFaces.forEach(function(nativeFontFace) {
document.fonts.delete(nativeFontFace);
});
this.nativeFontFaces.length = 0;
//#endif
},
//#if !(MOZCENTRAL)
get loadTestFont() {
// This is a CFF font with 1 glyph for '.' that fills its entire width and
// height.
return shadow(this, 'loadTestFont', atob(
'T1RUTwALAIAAAwAwQ0ZGIDHtZg4AAAOYAAAAgUZGVE1lkzZwAAAEHAAAABxHREVGABQAFQ' +
'AABDgAAAAeT1MvMlYNYwkAAAEgAAAAYGNtYXABDQLUAAACNAAAAUJoZWFk/xVFDQAAALwA' +
'AAA2aGhlYQdkA+oAAAD0AAAAJGhtdHgD6AAAAAAEWAAAAAZtYXhwAAJQAAAAARgAAAAGbm' +
'FtZVjmdH4AAAGAAAAAsXBvc3T/hgAzAAADeAAAACAAAQAAAAEAALZRFsRfDzz1AAsD6AAA' +
'AADOBOTLAAAAAM4KHDwAAAAAA+gDIQAAAAgAAgAAAAAAAAABAAADIQAAAFoD6AAAAAAD6A' +
'ABAAAAAAAAAAAAAAAAAAAAAQAAUAAAAgAAAAQD6AH0AAUAAAKKArwAAACMAooCvAAAAeAA' +
'MQECAAACAAYJAAAAAAAAAAAAAQAAAAAAAAAAAAAAAFBmRWQAwAAuAC4DIP84AFoDIQAAAA' +
'AAAQAAAAAAAAAAACAAIAABAAAADgCuAAEAAAAAAAAAAQAAAAEAAAAAAAEAAQAAAAEAAAAA' +
'AAIAAQAAAAEAAAAAAAMAAQAAAAEAAAAAAAQAAQAAAAEAAAAAAAUAAQAAAAEAAAAAAAYAAQ' +
'AAAAMAAQQJAAAAAgABAAMAAQQJAAEAAgABAAMAAQQJAAIAAgABAAMAAQQJAAMAAgABAAMA' +
'AQQJAAQAAgABAAMAAQQJAAUAAgABAAMAAQQJAAYAAgABWABYAAAAAAAAAwAAAAMAAAAcAA' +
'EAAAAAADwAAwABAAAAHAAEACAAAAAEAAQAAQAAAC7//wAAAC7////TAAEAAAAAAAABBgAA' +
'AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' +
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAA' +
'AAAAD/gwAyAAAAAQAAAAAAAAAAAAAAAAAAAAABAAQEAAEBAQJYAAEBASH4DwD4GwHEAvgc' +
'A/gXBIwMAYuL+nz5tQXkD5j3CBLnEQACAQEBIVhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF' +
'hYWFhYWFhYAAABAQAADwACAQEEE/t3Dov6fAH6fAT+fPp8+nwHDosMCvm1Cvm1DAz6fBQA' +
'AAAAAAABAAAAAMmJbzEAAAAAzgTjFQAAAADOBOQpAAEAAAAAAAAADAAUAAQAAAABAAAAAg' +
'ABAAAAAAAAAAAD6AAAAAAAAA=='
));
},
loadTestFontId: 0,
loadingContext: {
requests: [],
nextRequestId: 0
},
isSyncFontLoadingSupported: (function detectSyncFontLoadingSupport() {
if (isWorker) {
return false;
}
// User agent string sniffing is bad, but there is no reliable way to tell
// if font is fully loaded and ready to be used with canvas.
var userAgent = window.navigator.userAgent;
var m = /Mozilla\/5.0.*?rv:(\d+).*? Gecko/.exec(userAgent);
if (m && m[1] >= 14) {
return true;
}
// TODO other browsers
if (userAgent === 'node') {
return true;
}
return false;
})(),
nativeFontFaces: [],
isFontLoadingAPISupported: !isWorker && !!document.fonts,
addNativeFontFace: function fontLoader_addNativeFontFace(nativeFontFace) {
this.nativeFontFaces.push(nativeFontFace);
document.fonts.add(nativeFontFace);
},
bind: function fontLoaderBind(fonts, callback) {
assert(!isWorker, 'bind() shall be called from main thread');
var rules = [];
var fontsToLoad = [];
var fontLoadPromises = [];
for (var i = 0, ii = fonts.length; i < ii; i++) {
var font = fonts[i];
// 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;
if (this.isFontLoadingAPISupported) {
var nativeFontFace = font.createNativeFontFace();
if (nativeFontFace) {
fontLoadPromises.push(nativeFontFace.loaded);
}
} else {
var rule = font.bindDOM();
if (rule) {
rules.push(rule);
fontsToLoad.push(font);
}
}
}
var request = FontLoader.queueLoadingCallback(callback);
if (this.isFontLoadingAPISupported) {
Promise.all(fontsToLoad).then(function() {
request.complete();
});
} else if (rules.length > 0 && !this.isSyncFontLoadingSupported) {
FontLoader.prepareFontLoadEvent(rules, fontsToLoad, request);
} else {
request.complete();
}
},
queueLoadingCallback: function FontLoader_queueLoadingCallback(callback) {
function LoadLoader_completeRequest() {
assert(!request.end, 'completeRequest() cannot be called twice');
request.end = Date.now();
// sending all completed requests in order how they were queued
while (context.requests.length > 0 && context.requests[0].end) {
var otherRequest = context.requests.shift();
setTimeout(otherRequest.callback, 0);
}
}
var context = FontLoader.loadingContext;
var requestId = 'pdfjs-font-loading-' + (context.nextRequestId++);
var request = {
id: requestId,
complete: LoadLoader_completeRequest,
callback: callback,
started: Date.now()
};
context.requests.push(request);
return request;
},
prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules,
fonts,
request) {
/** Hack begin */
// There's currently no event when a font has finished downloading so the
// following code is a dirty hack to 'guess' when a font is
// ready. It's assumed fonts are loaded in order, so add a known test
// font after the desired fonts and then test for the loading of that
// test font.
function int32(data, offset) {
return (data.charCodeAt(offset) << 24) |
(data.charCodeAt(offset + 1) << 16) |
(data.charCodeAt(offset + 2) << 8) |
(data.charCodeAt(offset + 3) & 0xff);
}
function spliceString(s, offset, remove, insert) {
var chunk1 = s.substr(0, offset);
var chunk2 = s.substr(offset + remove);
return chunk1 + insert + chunk2;
}
var i, ii;
var canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
var ctx = canvas.getContext('2d');
var called = 0;
function isFontReady(name, callback) {
called++;
// With setTimeout clamping this gives the font ~100ms to load.
if(called > 30) {
warn('Load test font never loaded.');
callback();
return;
}
ctx.font = '30px ' + name;
ctx.fillText('.', 0, 20);
var imageData = ctx.getImageData(0, 0, 1, 1);
if (imageData.data[3] > 0) {
callback();
return;
}
setTimeout(isFontReady.bind(null, name, callback));
}
var loadTestFontId = 'lt' + Date.now() + this.loadTestFontId++;
// Chromium seems to cache fonts based on a hash of the actual font data,
// so the font must be modified for each load test else it will appear to
// be loaded already.
// TODO: This could maybe be made faster by avoiding the btoa of the full
// font by splitting it in chunks before hand and padding the font id.
var data = this.loadTestFont;
var COMMENT_OFFSET = 976; // has to be on 4 byte boundary (for checksum)
data = spliceString(data, COMMENT_OFFSET, loadTestFontId.length,
loadTestFontId);
// CFF checksum is important for IE, adjusting it
var CFF_CHECKSUM_OFFSET = 16;
var XXXX_VALUE = 0x58585858; // the "comment" filled with 'X'
var checksum = int32(data, CFF_CHECKSUM_OFFSET);
for (i = 0, ii = loadTestFontId.length - 3; i < ii; i += 4) {
checksum = (checksum - XXXX_VALUE + int32(loadTestFontId, i)) | 0;
}
if (i < loadTestFontId.length) { // align to 4 bytes boundary
checksum = (checksum - XXXX_VALUE +
int32(loadTestFontId + 'XXX', i)) | 0;
}
data = spliceString(data, CFF_CHECKSUM_OFFSET, 4, string32(checksum));
var url = 'url(data:font/opentype;base64,' + btoa(data) + ');';
var rule = '@font-face { font-family:"' + loadTestFontId + '";src:' +
url + '}';
FontLoader.insertRule(rule);
var names = [];
for (i = 0, ii = fonts.length; i < ii; i++) {
names.push(fonts[i].loadedName);
}
names.push(loadTestFontId);
var div = document.createElement('div');
div.setAttribute('style',
'visibility: hidden;' +
'width: 10px; height: 10px;' +
'position: absolute; top: 0px; left: 0px;');
for (i = 0, ii = names.length; i < ii; ++i) {
var span = document.createElement('span');
span.textContent = 'Hi';
span.style.fontFamily = names[i];
div.appendChild(span);
}
document.body.appendChild(div);
isFontReady(loadTestFontId, function() {
document.body.removeChild(div);
request.complete();
});
/** Hack end */
}
//#else
//bind: function fontLoaderBind(fonts, callback) {
// assert(!isWorker, 'bind() shall be called from main thread');
//
// for (var i = 0, ii = fonts.length; i < ii; i++) {
// var font = fonts[i];
// if (font.attached) {
// continue;
// }
//
// font.attached = true;
// font.bindDOM()
// }
//
// setTimeout(callback);
//}
//#endif
};
var FontFaceObject = (function FontFaceObjectClosure() {
function FontFaceObject(name, file, properties) {
this.compiledGlyphs = {};
if (arguments.length === 1) {
// importing translated data
var data = arguments[0];
for (var i in data) {
this[i] = data[i];
}
return;
}
}
FontFaceObject.prototype = {
//#if !(MOZCENTRAL)
createNativeFontFace: function FontFaceObject_createNativeFontFace() {
if (!this.data) {
return null;
}
if (PDFJS.disableFontFace) {
this.disableFontFace = true;
return null;
}
var nativeFontFace = new FontFace(this.loadedName, this.data, {});
FontLoader.addNativeFontFace(nativeFontFace);
if (PDFJS.pdfBug && 'FontInspector' in globalScope &&
globalScope['FontInspector'].enabled) {
globalScope['FontInspector'].fontAdded(this);
}
return nativeFontFace;
},
//#endif
bindDOM: function FontFaceObject_bindDOM() {
if (!this.data) {
return null;
}
if (PDFJS.disableFontFace) {
this.disableFontFace = true;
return null;
}
var data = bytesToString(new Uint8Array(this.data));
var fontName = this.loadedName;
// Add the font-face rule to the document
var url = ('url(data:' + this.mimetype + ';base64,' +
window.btoa(data) + ');');
var rule = '@font-face { font-family:"' + fontName + '";src:' + url + '}';
FontLoader.insertRule(rule);
if (PDFJS.pdfBug && 'FontInspector' in globalScope &&
globalScope['FontInspector'].enabled) {
globalScope['FontInspector'].fontAdded(this, url);
}
return rule;
},
getPathGenerator: function FontLoader_getPathGenerator(objs, character) {
if (!(character in this.compiledGlyphs)) {
var js = objs.get(this.loadedName + '_path_' + character);
/*jshint -W054 */
this.compiledGlyphs[character] = new Function('c', 'size', js);
}
return this.compiledGlyphs[character];
}
};
return FontFaceObject;
})();

View File

@@ -0,0 +1,99 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* globals Document, error, PDFJS */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
'use strict';
var Metadata = PDFJS.Metadata = (function MetadataClosure() {
function fixMetadata(meta) {
return meta.replace(/>\\376\\377([^<]+)/g, function(all, codes) {
var bytes = codes.replace(/\\([0-3])([0-7])([0-7])/g,
function(code, d1, d2, d3) {
return String.fromCharCode(d1 * 64 + d2 * 8 + d3 * 1);
});
var chars = '';
for (var i = 0; i < bytes.length; i += 2) {
var code = bytes.charCodeAt(i) * 256 + bytes.charCodeAt(i + 1);
chars += code >= 32 && code < 127 && code !== 60 && code !== 62 &&
code !== 38 && false ? String.fromCharCode(code) :
'&#x' + (0x10000 + code).toString(16).substring(1) + ';';
}
return '>' + chars;
});
}
function Metadata(meta) {
if (typeof meta === 'string') {
// Ghostscript produces invalid metadata
meta = fixMetadata(meta);
var parser = new DOMParser();
meta = parser.parseFromString(meta, 'application/xml');
} else if (!(meta instanceof Document)) {
error('Metadata: Invalid metadata object');
}
this.metaDocument = meta;
this.metadata = {};
this.parse();
}
Metadata.prototype = {
parse: function Metadata_parse() {
var doc = this.metaDocument;
var rdf = doc.documentElement;
if (rdf.nodeName.toLowerCase() !== 'rdf:rdf') { // Wrapped in <xmpmeta>
rdf = rdf.firstChild;
while (rdf && rdf.nodeName.toLowerCase() !== 'rdf:rdf') {
rdf = rdf.nextSibling;
}
}
var nodeName = (rdf) ? rdf.nodeName.toLowerCase() : null;
if (!rdf || nodeName !== 'rdf:rdf' || !rdf.hasChildNodes()) {
return;
}
var children = rdf.childNodes, desc, entry, name, i, ii, length, iLength;
for (i = 0, length = children.length; i < length; i++) {
desc = children[i];
if (desc.nodeName.toLowerCase() !== 'rdf:description') {
continue;
}
for (ii = 0, iLength = desc.childNodes.length; ii < iLength; ii++) {
if (desc.childNodes[ii].nodeName.toLowerCase() !== '#text') {
entry = desc.childNodes[ii];
name = entry.nodeName.toLowerCase();
this.metadata[name] = entry.textContent.trim();
}
}
}
},
get: function Metadata_get(name) {
return this.metadata[name] || null;
},
has: function Metadata_has(name) {
return typeof this.metadata[name] !== 'undefined';
}
};
return Metadata;
})();

View File

@@ -0,0 +1,425 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2014 Mozilla Foundation
*
* 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.
*/
/* globals CanvasGraphics, CachedCanvases, ColorSpace, Util, error, info,
isArray, makeCssRgb, WebGLUtils */
'use strict';
var ShadingIRs = {};
ShadingIRs.RadialAxial = {
fromIR: function RadialAxial_fromIR(raw) {
var type = raw[1];
var colorStops = raw[2];
var p0 = raw[3];
var p1 = raw[4];
var r0 = raw[5];
var r1 = raw[6];
return {
type: 'Pattern',
getPattern: function RadialAxial_getPattern(ctx) {
var grad;
if (type === 'axial') {
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
} else if (type === 'radial') {
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
}
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
var c = colorStops[i];
grad.addColorStop(c[0], c[1]);
}
return grad;
}
};
}
};
var createMeshCanvas = (function createMeshCanvasClosure() {
function drawTriangle(data, context, p1, p2, p3, c1, c2, c3) {
// Very basic Gouraud-shaded triangle rasterization algorithm.
var coords = context.coords, colors = context.colors;
var bytes = data.data, rowSize = data.width * 4;
var tmp;
if (coords[p1 + 1] > coords[p2 + 1]) {
tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp;
}
if (coords[p2 + 1] > coords[p3 + 1]) {
tmp = p2; p2 = p3; p3 = tmp; tmp = c2; c2 = c3; c3 = tmp;
}
if (coords[p1 + 1] > coords[p2 + 1]) {
tmp = p1; p1 = p2; p2 = tmp; tmp = c1; c1 = c2; c2 = tmp;
}
var x1 = (coords[p1] + context.offsetX) * context.scaleX;
var y1 = (coords[p1 + 1] + context.offsetY) * context.scaleY;
var x2 = (coords[p2] + context.offsetX) * context.scaleX;
var y2 = (coords[p2 + 1] + context.offsetY) * context.scaleY;
var x3 = (coords[p3] + context.offsetX) * context.scaleX;
var y3 = (coords[p3 + 1] + context.offsetY) * context.scaleY;
if (y1 >= y3) {
return;
}
var c1r = colors[c1], c1g = colors[c1 + 1], c1b = colors[c1 + 2];
var c2r = colors[c2], c2g = colors[c2 + 1], c2b = colors[c2 + 2];
var c3r = colors[c3], c3g = colors[c3 + 1], c3b = colors[c3 + 2];
var minY = Math.round(y1), maxY = Math.round(y3);
var xa, car, cag, cab;
var xb, cbr, cbg, cbb;
var k;
for (var y = minY; y <= maxY; y++) {
if (y < y2) {
k = y < y1 ? 0 : y1 === y2 ? 1 : (y1 - y) / (y1 - y2);
xa = x1 - (x1 - x2) * k;
car = c1r - (c1r - c2r) * k;
cag = c1g - (c1g - c2g) * k;
cab = c1b - (c1b - c2b) * k;
} else {
k = y > y3 ? 1 : y2 === y3 ? 0 : (y2 - y) / (y2 - y3);
xa = x2 - (x2 - x3) * k;
car = c2r - (c2r - c3r) * k;
cag = c2g - (c2g - c3g) * k;
cab = c2b - (c2b - c3b) * k;
}
k = y < y1 ? 0 : y > y3 ? 1 : (y1 - y) / (y1 - y3);
xb = x1 - (x1 - x3) * k;
cbr = c1r - (c1r - c3r) * k;
cbg = c1g - (c1g - c3g) * k;
cbb = c1b - (c1b - c3b) * k;
var x1_ = Math.round(Math.min(xa, xb));
var x2_ = Math.round(Math.max(xa, xb));
var j = rowSize * y + x1_ * 4;
for (var x = x1_; x <= x2_; x++) {
k = (xa - x) / (xa - xb);
k = k < 0 ? 0 : k > 1 ? 1 : k;
bytes[j++] = (car - (car - cbr) * k) | 0;
bytes[j++] = (cag - (cag - cbg) * k) | 0;
bytes[j++] = (cab - (cab - cbb) * k) | 0;
bytes[j++] = 255;
}
}
}
function drawFigure(data, figure, context) {
var ps = figure.coords;
var cs = figure.colors;
var i, ii;
switch (figure.type) {
case 'lattice':
var verticesPerRow = figure.verticesPerRow;
var rows = Math.floor(ps.length / verticesPerRow) - 1;
var cols = verticesPerRow - 1;
for (i = 0; i < rows; i++) {
var q = i * verticesPerRow;
for (var j = 0; j < cols; j++, q++) {
drawTriangle(data, context,
ps[q], ps[q + 1], ps[q + verticesPerRow],
cs[q], cs[q + 1], cs[q + verticesPerRow]);
drawTriangle(data, context,
ps[q + verticesPerRow + 1], ps[q + 1], ps[q + verticesPerRow],
cs[q + verticesPerRow + 1], cs[q + 1], cs[q + verticesPerRow]);
}
}
break;
case 'triangles':
for (i = 0, ii = ps.length; i < ii; i += 3) {
drawTriangle(data, context,
ps[i], ps[i + 1], ps[i + 2],
cs[i], cs[i + 1], cs[i + 2]);
}
break;
default:
error('illigal figure');
break;
}
}
function createMeshCanvas(bounds, combinesScale, coords, colors, figures,
backgroundColor) {
// we will increase scale on some weird factor to let antialiasing take
// care of "rough" edges
var EXPECTED_SCALE = 1.1;
// MAX_PATTERN_SIZE is used to avoid OOM situation.
var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
var offsetX = Math.floor(bounds[0]);
var offsetY = Math.floor(bounds[1]);
var boundsWidth = Math.ceil(bounds[2]) - offsetX;
var boundsHeight = Math.ceil(bounds[3]) - offsetY;
var width = Math.min(Math.ceil(Math.abs(boundsWidth * combinesScale[0] *
EXPECTED_SCALE)), MAX_PATTERN_SIZE);
var height = Math.min(Math.ceil(Math.abs(boundsHeight * combinesScale[1] *
EXPECTED_SCALE)), MAX_PATTERN_SIZE);
var scaleX = boundsWidth / width;
var scaleY = boundsHeight / height;
var context = {
coords: coords,
colors: colors,
offsetX: -offsetX,
offsetY: -offsetY,
scaleX: 1 / scaleX,
scaleY: 1 / scaleY
};
var canvas, tmpCanvas, i, ii;
if (WebGLUtils.isEnabled) {
canvas = WebGLUtils.drawFigures(width, height, backgroundColor,
figures, context);
// https://bugzilla.mozilla.org/show_bug.cgi?id=972126
tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false);
tmpCanvas.context.drawImage(canvas, 0, 0);
canvas = tmpCanvas.canvas;
} else {
tmpCanvas = CachedCanvases.getCanvas('mesh', width, height, false);
var tmpCtx = tmpCanvas.context;
var data = tmpCtx.createImageData(width, height);
if (backgroundColor) {
var bytes = data.data;
for (i = 0, ii = bytes.length; i < ii; i += 4) {
bytes[i] = backgroundColor[0];
bytes[i + 1] = backgroundColor[1];
bytes[i + 2] = backgroundColor[2];
bytes[i + 3] = 255;
}
}
for (i = 0; i < figures.length; i++) {
drawFigure(data, figures[i], context);
}
tmpCtx.putImageData(data, 0, 0);
canvas = tmpCanvas.canvas;
}
return {canvas: canvas, offsetX: offsetX, offsetY: offsetY,
scaleX: scaleX, scaleY: scaleY};
}
return createMeshCanvas;
})();
ShadingIRs.Mesh = {
fromIR: function Mesh_fromIR(raw) {
//var type = raw[1];
var coords = raw[2];
var colors = raw[3];
var figures = raw[4];
var bounds = raw[5];
var matrix = raw[6];
//var bbox = raw[7];
var background = raw[8];
return {
type: 'Pattern',
getPattern: function Mesh_getPattern(ctx, owner, shadingFill) {
var scale;
if (shadingFill) {
scale = Util.singularValueDecompose2dScale(ctx.mozCurrentTransform);
} else {
// Obtain scale from matrix and current transformation matrix.
scale = Util.singularValueDecompose2dScale(owner.baseTransform);
if (matrix) {
var matrixScale = Util.singularValueDecompose2dScale(matrix);
scale = [scale[0] * matrixScale[0],
scale[1] * matrixScale[1]];
}
}
// Rasterizing on the main thread since sending/queue large canvases
// might cause OOM.
var temporaryPatternCanvas = createMeshCanvas(bounds, scale, coords,
colors, figures, shadingFill ? null : background);
if (!shadingFill) {
ctx.setTransform.apply(ctx, owner.baseTransform);
if (matrix) {
ctx.transform.apply(ctx, matrix);
}
}
ctx.translate(temporaryPatternCanvas.offsetX,
temporaryPatternCanvas.offsetY);
ctx.scale(temporaryPatternCanvas.scaleX,
temporaryPatternCanvas.scaleY);
return ctx.createPattern(temporaryPatternCanvas.canvas, 'no-repeat');
}
};
}
};
ShadingIRs.Dummy = {
fromIR: function Dummy_fromIR() {
return {
type: 'Pattern',
getPattern: function Dummy_fromIR_getPattern() {
return 'hotpink';
}
};
}
};
function getShadingPatternFromIR(raw) {
var shadingIR = ShadingIRs[raw[0]];
if (!shadingIR) {
error('Unknown IR type: ' + raw[0]);
}
return shadingIR.fromIR(raw);
}
var TilingPattern = (function TilingPatternClosure() {
var PaintType = {
COLORED: 1,
UNCOLORED: 2
};
var MAX_PATTERN_SIZE = 3000; // 10in @ 300dpi shall be enough
function TilingPattern(IR, color, ctx, objs, commonObjs, baseTransform) {
this.operatorList = IR[2];
this.matrix = IR[3] || [1, 0, 0, 1, 0, 0];
this.bbox = IR[4];
this.xstep = IR[5];
this.ystep = IR[6];
this.paintType = IR[7];
this.tilingType = IR[8];
this.color = color;
this.objs = objs;
this.commonObjs = commonObjs;
this.baseTransform = baseTransform;
this.type = 'Pattern';
this.ctx = ctx;
}
TilingPattern.prototype = {
createPatternCanvas: function TilinPattern_createPatternCanvas(owner) {
var operatorList = this.operatorList;
var bbox = this.bbox;
var xstep = this.xstep;
var ystep = this.ystep;
var paintType = this.paintType;
var tilingType = this.tilingType;
var color = this.color;
var objs = this.objs;
var commonObjs = this.commonObjs;
info('TilingType: ' + tilingType);
var x0 = bbox[0], y0 = bbox[1], x1 = bbox[2], y1 = bbox[3];
var topLeft = [x0, y0];
// we want the canvas to be as large as the step size
var botRight = [x0 + xstep, y0 + ystep];
var width = botRight[0] - topLeft[0];
var height = botRight[1] - topLeft[1];
// Obtain scale from matrix and current transformation matrix.
var matrixScale = Util.singularValueDecompose2dScale(this.matrix);
var curMatrixScale = Util.singularValueDecompose2dScale(
this.baseTransform);
var combinedScale = [matrixScale[0] * curMatrixScale[0],
matrixScale[1] * curMatrixScale[1]];
// MAX_PATTERN_SIZE is used to avoid OOM situation.
// Use width and height values that are as close as possible to the end
// result when the pattern is used. Too low value makes the pattern look
// blurry. Too large value makes it look too crispy.
width = Math.min(Math.ceil(Math.abs(width * combinedScale[0])),
MAX_PATTERN_SIZE);
height = Math.min(Math.ceil(Math.abs(height * combinedScale[1])),
MAX_PATTERN_SIZE);
var tmpCanvas = CachedCanvases.getCanvas('pattern', width, height, true);
var tmpCtx = tmpCanvas.context;
var graphics = new CanvasGraphics(tmpCtx, commonObjs, objs);
graphics.groupLevel = owner.groupLevel;
this.setFillAndStrokeStyleToContext(tmpCtx, paintType, color);
this.setScale(width, height, xstep, ystep);
this.transformToScale(graphics);
// transform coordinates to pattern space
var tmpTranslate = [1, 0, 0, 1, -topLeft[0], -topLeft[1]];
graphics.transform.apply(graphics, tmpTranslate);
this.clipBbox(graphics, bbox, x0, y0, x1, y1);
graphics.executeOperatorList(operatorList);
return tmpCanvas.canvas;
},
setScale: function TilingPattern_setScale(width, height, xstep, ystep) {
this.scale = [width / xstep, height / ystep];
},
transformToScale: function TilingPattern_transformToScale(graphics) {
var scale = this.scale;
var tmpScale = [scale[0], 0, 0, scale[1], 0, 0];
graphics.transform.apply(graphics, tmpScale);
},
scaleToContext: function TilingPattern_scaleToContext() {
var scale = this.scale;
this.ctx.scale(1 / scale[0], 1 / scale[1]);
},
clipBbox: function clipBbox(graphics, bbox, x0, y0, x1, y1) {
if (bbox && isArray(bbox) && bbox.length === 4) {
var bboxWidth = x1 - x0;
var bboxHeight = y1 - y0;
graphics.ctx.rect(x0, y0, bboxWidth, bboxHeight);
graphics.clip();
graphics.endPath();
}
},
setFillAndStrokeStyleToContext:
function setFillAndStrokeStyleToContext(context, paintType, color) {
switch (paintType) {
case PaintType.COLORED:
var ctx = this.ctx;
context.fillStyle = ctx.fillStyle;
context.strokeStyle = ctx.strokeStyle;
break;
case PaintType.UNCOLORED:
var cssColor = Util.makeCssRgb(color);
context.fillStyle = cssColor;
context.strokeStyle = cssColor;
break;
default:
error('Unsupported paint type: ' + paintType);
}
},
getPattern: function TilingPattern_getPattern(ctx, owner) {
var temporaryPatternCanvas = this.createPatternCanvas(owner);
ctx = this.ctx;
ctx.setTransform.apply(ctx, this.baseTransform);
ctx.transform.apply(ctx, this.matrix);
this.scaleToContext();
return ctx.createPattern(temporaryPatternCanvas, 'repeat');
}
};
return TilingPattern;
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,437 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2014 Mozilla Foundation
*
* 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.
*/
/* globals PDFJS, shadow */
/* jshint -W043 */
'use strict';
var WebGLUtils = (function WebGLUtilsClosure() {
function loadShader(gl, code, shaderType) {
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, code);
gl.compileShader(shader);
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
var errorMsg = gl.getShaderInfoLog(shader);
throw new Error('Error during shader compilation: ' + errorMsg);
}
return shader;
}
function createVertexShader(gl, code) {
return loadShader(gl, code, gl.VERTEX_SHADER);
}
function createFragmentShader(gl, code) {
return loadShader(gl, code, gl.FRAGMENT_SHADER);
}
function createProgram(gl, shaders) {
var program = gl.createProgram();
for (var i = 0, ii = shaders.length; i < ii; ++i) {
gl.attachShader(program, shaders[i]);
}
gl.linkProgram(program);
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
var errorMsg = gl.getProgramInfoLog(program);
throw new Error('Error during program linking: ' + errorMsg);
}
return program;
}
function createTexture(gl, image, textureId) {
gl.activeTexture(textureId);
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we can render any size image.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// Upload the image into the texture.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
return texture;
}
var currentGL, currentCanvas;
function generageGL() {
if (currentGL) {
return;
}
currentCanvas = document.createElement('canvas');
currentGL = currentCanvas.getContext('webgl',
{ premultipliedalpha: false });
}
var smaskVertexShaderCode = '\
attribute vec2 a_position; \
attribute vec2 a_texCoord; \
\
uniform vec2 u_resolution; \
\
varying vec2 v_texCoord; \
\
void main() { \
vec2 clipSpace = (a_position / u_resolution) * 2.0 - 1.0; \
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \
\
v_texCoord = a_texCoord; \
} ';
var smaskFragmentShaderCode = '\
precision mediump float; \
\
uniform vec4 u_backdrop; \
uniform int u_subtype; \
uniform sampler2D u_image; \
uniform sampler2D u_mask; \
\
varying vec2 v_texCoord; \
\
void main() { \
vec4 imageColor = texture2D(u_image, v_texCoord); \
vec4 maskColor = texture2D(u_mask, v_texCoord); \
if (u_backdrop.a > 0.0) { \
maskColor.rgb = maskColor.rgb * maskColor.a + \
u_backdrop.rgb * (1.0 - maskColor.a); \
} \
float lum; \
if (u_subtype == 0) { \
lum = maskColor.a; \
} else { \
lum = maskColor.r * 0.3 + maskColor.g * 0.59 + \
maskColor.b * 0.11; \
} \
imageColor.a *= lum; \
imageColor.rgb *= imageColor.a; \
gl_FragColor = imageColor; \
} ';
var smaskCache = null;
function initSmaskGL() {
var canvas, gl;
generageGL();
canvas = currentCanvas;
currentCanvas = null;
gl = currentGL;
currentGL = null;
// setup a GLSL program
var vertexShader = createVertexShader(gl, smaskVertexShaderCode);
var fragmentShader = createFragmentShader(gl, smaskFragmentShaderCode);
var program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
var cache = {};
cache.gl = gl;
cache.canvas = canvas;
cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
cache.positionLocation = gl.getAttribLocation(program, 'a_position');
cache.backdropLocation = gl.getUniformLocation(program, 'u_backdrop');
cache.subtypeLocation = gl.getUniformLocation(program, 'u_subtype');
var texCoordLocation = gl.getAttribLocation(program, 'a_texCoord');
var texLayerLocation = gl.getUniformLocation(program, 'u_image');
var texMaskLocation = gl.getUniformLocation(program, 'u_mask');
// provide texture coordinates for the rectangle.
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(texCoordLocation);
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
gl.uniform1i(texLayerLocation, 0);
gl.uniform1i(texMaskLocation, 1);
smaskCache = cache;
}
function composeSMask(layer, mask, properties) {
var width = layer.width, height = layer.height;
if (!smaskCache) {
initSmaskGL();
}
var cache = smaskCache,canvas = cache.canvas, gl = cache.gl;
canvas.width = width;
canvas.height = height;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.uniform2f(cache.resolutionLocation, width, height);
if (properties.backdrop) {
gl.uniform4f(cache.resolutionLocation, properties.backdrop[0],
properties.backdrop[1], properties.backdrop[2], 1);
} else {
gl.uniform4f(cache.resolutionLocation, 0, 0, 0, 0);
}
gl.uniform1i(cache.subtypeLocation,
properties.subtype === 'Luminosity' ? 1 : 0);
// Create a textures
var texture = createTexture(gl, layer, gl.TEXTURE0);
var maskTexture = createTexture(gl, mask, gl.TEXTURE1);
// Create a buffer and put a single clipspace rectangle in
// it (2 triangles)
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
width, 0,
0, height,
0, height,
width, 0,
width, height]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cache.positionLocation);
gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
// draw
gl.clearColor(0, 0, 0, 0);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.flush();
gl.deleteTexture(texture);
gl.deleteTexture(maskTexture);
gl.deleteBuffer(buffer);
return canvas;
}
var figuresVertexShaderCode = '\
attribute vec2 a_position; \
attribute vec3 a_color; \
\
uniform vec2 u_resolution; \
uniform vec2 u_scale; \
uniform vec2 u_offset; \
\
varying vec4 v_color; \
\
void main() { \
vec2 position = (a_position + u_offset) * u_scale; \
vec2 clipSpace = (position / u_resolution) * 2.0 - 1.0; \
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); \
\
v_color = vec4(a_color / 255.0, 1.0); \
} ';
var figuresFragmentShaderCode = '\
precision mediump float; \
\
varying vec4 v_color; \
\
void main() { \
gl_FragColor = v_color; \
} ';
var figuresCache = null;
function initFiguresGL() {
var canvas, gl;
generageGL();
canvas = currentCanvas;
currentCanvas = null;
gl = currentGL;
currentGL = null;
// setup a GLSL program
var vertexShader = createVertexShader(gl, figuresVertexShaderCode);
var fragmentShader = createFragmentShader(gl, figuresFragmentShaderCode);
var program = createProgram(gl, [vertexShader, fragmentShader]);
gl.useProgram(program);
var cache = {};
cache.gl = gl;
cache.canvas = canvas;
cache.resolutionLocation = gl.getUniformLocation(program, 'u_resolution');
cache.scaleLocation = gl.getUniformLocation(program, 'u_scale');
cache.offsetLocation = gl.getUniformLocation(program, 'u_offset');
cache.positionLocation = gl.getAttribLocation(program, 'a_position');
cache.colorLocation = gl.getAttribLocation(program, 'a_color');
figuresCache = cache;
}
function drawFigures(width, height, backgroundColor, figures, context) {
if (!figuresCache) {
initFiguresGL();
}
var cache = figuresCache, canvas = cache.canvas, gl = cache.gl;
canvas.width = width;
canvas.height = height;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.uniform2f(cache.resolutionLocation, width, height);
// count triangle points
var count = 0;
var i, ii, rows;
for (i = 0, ii = figures.length; i < ii; i++) {
switch (figures[i].type) {
case 'lattice':
rows = (figures[i].coords.length / figures[i].verticesPerRow) | 0;
count += (rows - 1) * (figures[i].verticesPerRow - 1) * 6;
break;
case 'triangles':
count += figures[i].coords.length;
break;
}
}
// transfer data
var coords = new Float32Array(count * 2);
var colors = new Uint8Array(count * 3);
var coordsMap = context.coords, colorsMap = context.colors;
var pIndex = 0, cIndex = 0;
for (i = 0, ii = figures.length; i < ii; i++) {
var figure = figures[i], ps = figure.coords, cs = figure.colors;
switch (figure.type) {
case 'lattice':
var cols = figure.verticesPerRow;
rows = (ps.length / cols) | 0;
for (var row = 1; row < rows; row++) {
var offset = row * cols + 1;
for (var col = 1; col < cols; col++, offset++) {
coords[pIndex] = coordsMap[ps[offset - cols - 1]];
coords[pIndex + 1] = coordsMap[ps[offset - cols - 1] + 1];
coords[pIndex + 2] = coordsMap[ps[offset - cols]];
coords[pIndex + 3] = coordsMap[ps[offset - cols] + 1];
coords[pIndex + 4] = coordsMap[ps[offset - 1]];
coords[pIndex + 5] = coordsMap[ps[offset - 1] + 1];
colors[cIndex] = colorsMap[cs[offset - cols - 1]];
colors[cIndex + 1] = colorsMap[cs[offset - cols - 1] + 1];
colors[cIndex + 2] = colorsMap[cs[offset - cols - 1] + 2];
colors[cIndex + 3] = colorsMap[cs[offset - cols]];
colors[cIndex + 4] = colorsMap[cs[offset - cols] + 1];
colors[cIndex + 5] = colorsMap[cs[offset - cols] + 2];
colors[cIndex + 6] = colorsMap[cs[offset - 1]];
colors[cIndex + 7] = colorsMap[cs[offset - 1] + 1];
colors[cIndex + 8] = colorsMap[cs[offset - 1] + 2];
coords[pIndex + 6] = coords[pIndex + 2];
coords[pIndex + 7] = coords[pIndex + 3];
coords[pIndex + 8] = coords[pIndex + 4];
coords[pIndex + 9] = coords[pIndex + 5];
coords[pIndex + 10] = coordsMap[ps[offset]];
coords[pIndex + 11] = coordsMap[ps[offset] + 1];
colors[cIndex + 9] = colors[cIndex + 3];
colors[cIndex + 10] = colors[cIndex + 4];
colors[cIndex + 11] = colors[cIndex + 5];
colors[cIndex + 12] = colors[cIndex + 6];
colors[cIndex + 13] = colors[cIndex + 7];
colors[cIndex + 14] = colors[cIndex + 8];
colors[cIndex + 15] = colorsMap[cs[offset]];
colors[cIndex + 16] = colorsMap[cs[offset] + 1];
colors[cIndex + 17] = colorsMap[cs[offset] + 2];
pIndex += 12;
cIndex += 18;
}
}
break;
case 'triangles':
for (var j = 0, jj = ps.length; j < jj; j++) {
coords[pIndex] = coordsMap[ps[j]];
coords[pIndex + 1] = coordsMap[ps[j] + 1];
colors[cIndex] = colorsMap[cs[i]];
colors[cIndex + 1] = colorsMap[cs[j] + 1];
colors[cIndex + 2] = colorsMap[cs[j] + 2];
pIndex += 2;
cIndex += 3;
}
break;
}
}
// draw
if (backgroundColor) {
gl.clearColor(backgroundColor[0] / 255, backgroundColor[1] / 255,
backgroundColor[2] / 255, 1.0);
} else {
gl.clearColor(0, 0, 0, 0);
}
gl.clear(gl.COLOR_BUFFER_BIT);
var coordsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, coordsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, coords, gl.STATIC_DRAW);
gl.enableVertexAttribArray(cache.positionLocation);
gl.vertexAttribPointer(cache.positionLocation, 2, gl.FLOAT, false, 0, 0);
var colorsBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorsBuffer);
gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
gl.enableVertexAttribArray(cache.colorLocation);
gl.vertexAttribPointer(cache.colorLocation, 3, gl.UNSIGNED_BYTE, false,
0, 0);
gl.uniform2f(cache.scaleLocation, context.scaleX, context.scaleY);
gl.uniform2f(cache.offsetLocation, context.offsetX, context.offsetY);
gl.drawArrays(gl.TRIANGLES, 0, count);
gl.flush();
gl.deleteBuffer(coordsBuffer);
gl.deleteBuffer(colorsBuffer);
return canvas;
}
function cleanup() {
if (smaskCache && smaskCache.canvas) {
smaskCache.canvas.width = 0;
smaskCache.canvas.height = 0;
}
if (figuresCache && figuresCache.canvas) {
figuresCache.canvas.width = 0;
figuresCache.canvas.height = 0;
}
smaskCache = null;
figuresCache = null;
}
return {
get isEnabled() {
if (PDFJS.disableWebGL) {
return false;
}
var enabled = false;
try {
generageGL();
enabled = !!currentGL;
} catch (e) { }
return shadow(this, 'isEnabled', enabled);
},
composeSMask: composeSMask,
drawFigures: drawFigures,
clear: cleanup
};
})();

View File

@@ -0,0 +1,34 @@
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/*
NOTE: This file is created as a helper to assist with JSDoc html files.
It is not for use in the executable code.
*/
/**
* PDFJS scope object that contains all functions, objects and variables related
* to the PDF.js.
* @constructor
*/
function PDFJS() {
// Mock class constructor. See src/display/api.js.
}
/**
* Represents the eventual result of an asynchronous operation.
* @external Promise
* @see {@link http://promisesaplus.com/ Promise/A+}
*/

View File

@@ -0,0 +1,174 @@
/**
* PDF.js extension that extracts highlighted text and annotations from pdf files.
* Based on modified version of pdf.js available here https://github.com/jlegewie/pdf.js
* (see various extract branches). See 'PDF Reference Manual 1.7' section 8.4 for details on
* annotations in pdf files.
*/
'use strict';
/**
* @return {Promise} A promise that is resolved with an Object
* that includes elements for path, time, and annotations.
*/
PDFJS.getPDFAnnotations = function(url, removeHyphens, progress, debug) {
// set default values
removeHyphens = typeof removeHyphens !== 'undefined' ? removeHyphens : true;
progress = typeof progress !== 'undefined' ? progress : function(x, y) {};
debug = typeof debug !== 'undefined' ? debug : false;
var legacyPromise = PDFJS.Promise!==undefined;
// Return a new promise (with support for legacy pdf.js promises)
/* http://www.html5rocks.com/en/tutorials/es6/promises*/
var extract = function(resolve, reject) {
// var SUPPORTED_ANNOTS = ['Text','Highlight','Underline'],
var SUPPORTED_ANNOTS = ['Highlight'],
obj = {annotations: [],
time:null,
url: typeof url=='string' ? url : ''
};
// Fetch the PDF document from the URL using promices
PDFJS.getDocument(url).then(function(pdf) {
var n_annos = 0,
numPages = pdf.numPages,
time_start = performance.now();
// function to handle page (render and extract annotations)
var getAnnotationsFromPage = function(page) {
var scale = 1;
var viewport = page.getViewport(scale);
// Prepare canvas using PDF page dimensions
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
// console.log(canvas.height);
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
// error handler
var errorHandler = function(error) {
progress(page.pageNumber, numPages);
console.log(error);
// continue with next page
if(numPages>page.pageNumber)
pdf.getPage(page.pageNumber+1).then(
getAnnotationsFromPage,
function(err) {legacyPromise ? promise.reject(obj) : reject(err);});
else {
var end = performance.now();
obj.time = end-time_start;
legacyPromise ? promise.resolve(obj) : resolve(obj);
}
};
// function to convert deviceRGB to RGB
var convertDeviceRGBtoRGB = function(dr, dg, db) {
var r = Math.round(dr*255);
var g = Math.round(dg*255);
var b = Math.round(db*255);
return [r, g, b];
};
// get annotations
page.getAnnotations().then(function extractAnno(annos) {
// compatibility for old pdf.js version and filter for supported annotations
annos = annos
.map(function(anno) {
if (anno.subtype===undefined) anno.subtype=anno.type;
return anno;
})
.filter(function(anno) {
return SUPPORTED_ANNOTS.indexOf(anno.subtype) >= 0;
});
// skip page if there is nothing interesting
if (annos.length===0) {
progress(page.pageNumber,numPages);
if(numPages>page.pageNumber)
pdf.getPage(page.pageNumber+1).then(
getAnnotationsFromPage,
function(err) {legacyPromise ? promise.reject(obj) : reject(err);});
else {
var end = performance.now();
obj.time = end-time_start;
legacyPromise ? promise.resolve(obj) : resolve(obj);
}
return;
}
// render page
var render = page.render(renderContext, annos);
if (render.promise!==undefined) render = render.promise;
render.then(function() {
// clean markup
annos = annos.map(function(anno) {
anno.page = page.pageNumber;
if('color' in anno)
anno.color = convertDeviceRGBtoRGB(anno.color[0],anno.color[1],anno.color[2]);
// clean markup
if(anno.markup) {
anno.markup = anno.markup
.map(function(part) {return part.trim();})
.join(' ').trim()
// translate ligatures (e.g. 'fi')
.replace('\ufb00','ff').replace('\ufb01','fi').replace('\ufb02','fl')
.replace(/\ufb03/g,'ffi').replace(/\ufb04/g,'ffl').replace(/\ufb05/g,'ft')
.replace(/\ufb06/g,'st').replace(/\uFB00/g,'ff').replace(/\uFB01/g,'fi')
.replace(/\uFB02/g,'fl').replace(/\u201D/g,'"').replace(/\u201C/g,'"')
.replace(/\u2019/g,"'").replace(/\u2018/g,"'").replace(/\u2013/g,'-').
replace(/''/g,'"').replace(/`/g,"'");
if(removeHyphens)
anno.markup = anno.markup.replace(/([a-zA-Z])- ([a-zA-Z])/g, '$1$2');
}
// clean anno
if(!debug) {
delete anno.annotationFlags;
delete anno.borderWidth;
delete anno.chars;
delete anno.hasAppearance;
delete anno.markupGeom;
delete anno.quadPoints;
delete anno.rect;
delete anno.rect;
delete anno.spaceSize;
delete anno.name;
}
// return
return anno;
});
// add annotations to return object
obj.annotations.push.apply(obj.annotations, annos);
// render next page
progress(page.pageNumber, numPages);
if(numPages>page.pageNumber)
pdf.getPage(page.pageNumber+1).then(
getAnnotationsFromPage,
function(err) {legacyPromise ? promise.reject(obj) : reject(err);} );
else {
var end = performance.now();
obj.time = end-time_start;
legacyPromise ? promise.resolve(obj) : resolve(obj);
}
}, errorHandler);
}, errorHandler);
};
// Using promise to fetch the page
pdf.getPage(1).then(
getAnnotationsFromPage,
function(err) {console.log('error getting the page:' + err);}
);
},
function(err) {
console.log('unable to open pdf: ' + err);
legacyPromise ? promise.reject(obj) : reject(err);
});
};
if (legacyPromise) {
var promise = new PDFJS.Promise();
extract();
return promise;
}
else
return new Promise(extract);
};

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64">
<path
d="M 4.8364028,0.4891005 32.096378,4.5726641 59.163597,0.4891005 54.680408,57.805097 32.096378,63.510899 8.3116209,57.805097 z"
style="fill:#e5e7e8;fill-opacity:1;fill-rule:nonzero;stroke:#cccccc" />
<path
d="M 32.096378,10.745857 53.925414,6.8301117 51.016574,53.81906 32.096378,58.517955 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" />
<rect
width="34.027256"
height="19.136194"
x="3.7557135"
y="22.431904"
style="fill:#ff2600;fill-opacity:1;fill-rule:nonzero;stroke:none" />
<rect
width="23.480518"
height="19.136194"
x="36.763767"
y="22.431904"
style="fill:#ff501a;fill-opacity:1;fill-rule:nonzero;stroke:none" />
<g transform="matrix(0.42778543,0,0,0.42778543,58.617711,9.6737064)">
<path
d="m -120.53125,34.59375 0,35.1875 6.53125,0 0,-5.9375 0,-5.96875 8.875,0 4.15625,-3.71875 0,-7.71875 0,-7.71875 -4.21875,-4.125 -15.34375,0 z m 6.53125,6.8125 6.21875,0 0,10.21875 -6.21875,0 0,-10.21875 z"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m -98.125,34.59375 0,35.1875 16.125,0 3.75,-3.625 0,-27.96875 -3.96875,-3.59375 -15.90625,0 z m 6.8125,6.8125 6.8125,0 0,21.5625 -6.8125,0 0,-21.5625 z" id="path3056"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m -74.856072,34.602929 c 5.485697,0 10.971394,0 16.457091,0 0,2.269943 0,4.539887 0,6.80983 -3.404915,0 -6.809831,0 -10.214746,0 0,2.472069 0,4.944138 0,7.416206 2.93201,0.110496 5.864021,-0.110494 8.796031,0 l 0,3.366025 0,3.375914 c -2.93201,0.110495 -5.864021,-0.110495 -8.796031,0 l 0,7.146446 0,7.069702 c -2.080782,0 -4.161563,0 -6.242345,0 0,-11.728041 0,-23.456082 0,-35.184123 z"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m -42.8813,67.942723 -2.181762,-1.844329 c 0,-1.986871 0,-3.973742 0,-5.960613 2.175363,6.7e-4 4.350725,0.0013 6.526088,0.002 0,0.94581 0,1.891619 0,2.837429 1.891619,0 3.783239,0 5.674858,0 0,-9.458098 0,-18.916195 0,-28.374293 2.175363,-1.26e-4 4.350725,-2.52e-4 6.526088,-3.78e-4 0,10.498614 0,20.997229 0,31.495843 -1.454508,1.229554 -2.909019,2.459106 -4.363529,3.688658 -3.333325,0 -6.666651,0 -9.999976,0 -0.727237,-0.614768 -1.454563,-1.229595 -2.181767,-1.844308 z"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m -21.316836,67.942723 -2.109122,-1.844329 0,-5.978379 c 2.061011,0 4.392437,0.01978 6.453448,0.01978 0,0.94581 0,1.891619 0,2.837429 2.269943,0 4.539887,0 6.80983,0 0,-2.459105 0,-4.918211 0,-7.377316 -3.196954,0 -6.393909,0 -9.590863,0 l -3.745055,-3.688659 0,-6.80983 0,-6.80983 3.745055,-3.688658 c 3.863156,0 7.726313,0 11.5894692,0 l 4.2511106,3.688658 c -0.00204,1.797039 -0.00472,3.594077 -0.00737,5.391115 -2.0807816,0 -4.1615632,0 -6.2423448,0 0,-0.756648 0,-1.513295 0,-2.269943 -2.269943,0 -4.539887,0 -6.80983,0 0,2.459105 0,4.918211 0,7.377316 2.990126,0 5.980253,0 8.9703792,0 1.3605964,1.204585 4.0817956,3.613749 4.0817956,3.613749 0,4.555246 0,9.110492 0,13.665738 l -2.0892845,1.858745 -2.0892901,1.858745 -5.5180792,0 -5.51808,0 z"
style="fill:#ffffff;fill-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,51 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/*jshint globalstrict: false */
/* globals PDFJS */
// Initializing PDFJS global object (if still undefined)
if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
//#if BUNDLE_VERSION
//#expand PDFJS.version = '__BUNDLE_VERSION__';
//#endif
//#if BUNDLE_BUILD
//#expand PDFJS.build = '__BUNDLE_BUILD__';
//#endif
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it
'use strict';
//#expand __BUNDLE__
}).call((typeof window === 'undefined') ? this : window);
//#if !(MOZCENTRAL || FIREFOX)
if (!PDFJS.workerSrc && typeof document !== 'undefined') {
// workerSrc is not set -- using last script url to define default location
PDFJS.workerSrc = (function () {
'use strict';
var scriptTagContainer = document.body ||
document.getElementsByTagName('head')[0];
var pdfjsSrc = scriptTagContainer.lastChild.src;
return pdfjsSrc && pdfjsSrc.replace(/\.js$/i, '.worker.js');
})();
}
//#endif

View File

@@ -0,0 +1,314 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
'use strict';
var CFFEncodingMap = {
'0': '-reserved-',
'1': 'hstem',
'2': '-reserved-',
'3': 'vstem',
'4': 'vmoveto',
'5': 'rlineto',
'6': 'hlineto',
'7': 'vlineto',
'8': 'rrcurveto',
'9': '-reserved-',
'10': 'callsubr',
'11': 'return',
'12': {
'3': 'and',
'4': 'or',
'5': 'not',
'9': 'abs',
'10': 'add',
'11': 'div',
'12': 'sub',
'14': 'neg',
'15': 'eq',
'18': 'drop',
'20': 'put',
'21': 'get',
'22': 'ifelse',
'23': 'random',
'24': 'mul',
'26': 'sqrt',
'27': 'dup',
'28': 'exch',
'29': 'index',
'30': 'roll',
'34': 'hflex',
'35': 'flex',
'36': 'hflex1',
'37': 'flex1'
},
'13': '-reserved-',
'14': 'endchar',
'15': '-reserved-',
'16': '-reserved-',
'17': '-reserved-',
'18': 'hstemhm',
'19': 'hintmask',
'20': 'cntrmask',
'21': 'rmoveto',
'22': 'hmoveto',
'23': 'vstemhm',
'24': 'rcurveline',
'25': 'rlivecurve',
'26': 'vvcurveto',
'27': 'hhcurveto',
'29': 'callgsubr',
'30': 'vhcurveto',
'31': 'hvcurveto'
};
var CFFDictDataMap = {
'0': {
name: 'version',
operand: 'SID'
},
'1': {
name: 'Notice',
operand: 'SID'
},
'2': {
name: 'FullName',
operand: 'SID'
},
'3': {
name: 'FamilyName',
operand: 'SID'
},
'4': {
name: 'Weight',
operand: 'SID'
},
'5': {
name: 'FontBBox',
operand: [0, 0, 0, 0]
},
'6': {
name: 'BlueValues'
},
'7': {
name: 'OtherBlues'
},
'8': {
name: 'FamilyBlues'
},
'9': {
name: 'FamilyOtherBlues'
},
'10': {
name: 'StdHW'
},
'11': {
name: 'StdVW'
},
'12': {
'0': {
name: 'Copyright',
operand: 'SID'
},
'1': {
name: 'IsFixedPitch',
operand: false
},
'2': {
name: 'ItalicAngle',
operand: 0
},
'3': {
name: 'UnderlinePosition',
operand: -100
},
'4': {
name: 'UnderlineThickness',
operand: 50
},
'5': {
name: 'PaintType',
operand: 0
},
'6': {
name: 'CharstringType',
operand: 2
},
'7': {
name: 'FontMatrix',
operand: [0.001, 0, 0, 0.001, 0 , 0]
},
'8': {
name: 'StrokeWidth',
operand: 0
},
'9': {
name: 'BlueScale'
},
'10': {
name: 'BlueShift'
},
'11': {
name: 'BlueFuzz'
},
'12': {
name: 'StemSnapH'
},
'13': {
name: 'StemSnapV'
},
'14': {
name: 'ForceBold'
},
'17': {
name: 'LanguageGroup'
},
'18': {
name: 'ExpansionFactor'
},
'19': {
name: 'initialRandomSeed'
},
'20': {
name: 'SyntheticBase',
operand: null
},
'21': {
name: 'PostScript',
operand: 'SID'
},
'22': {
name: 'BaseFontName',
operand: 'SID'
},
'23': {
name: 'BaseFontBlend',
operand: 'delta'
}
},
'13': {
name: 'UniqueID',
operand: null
},
'14': {
name: 'XUID',
operand: []
},
'15': {
name: 'charset',
operand: 0
},
'16': {
name: 'Encoding',
operand: 0
},
'17': {
name: 'CharStrings',
operand: null
},
'18': {
name: 'Private',
operand: 'number number'
},
'19': {
name: 'Subrs'
},
'20': {
name: 'defaultWidthX'
},
'21': {
name: 'nominalWidthX'
}
};
var CFFDictPrivateDataMap = {
'6': {
name: 'BluesValues',
operand: 'delta'
},
'7': {
name: 'OtherBlues',
operand: 'delta'
},
'8': {
name: 'FamilyBlues',
operand: 'delta'
},
'9': {
name: 'FamilyOtherBlues',
operand: 'delta'
},
'10': {
name: 'StdHW',
operand: null
},
'11': {
name: 'StdVW',
operand: null
},
'12': {
'9': {
name: 'BlueScale',
operand: 0.039625
},
'10': {
name: 'BlueShift',
operand: 7
},
'11': {
name: 'BlueFuzz',
operand: 1
},
'12': {
name: 'StemSnapH',
operand: 'delta'
},
'13': {
name: 'StemSnapV',
operand: 'delta'
},
'14': {
name: 'ForceBold',
operand: 'boolean'
},
'17': {
name: 'LanguageGroup',
operand: 0
},
'18': {
name: 'ExpansionFactor',
operand: 0.06
},
'19': {
name: 'initialRandomSeed',
operand: 0
}
},
'19': {
name: 'Subrs',
operand: null
},
'20': {
name: 'defaultWidthX',
operand: 0
},
'21': {
name: 'nominalWidthX',
operand: 0
}
};

View File

@@ -0,0 +1,434 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals CFFDictDataMap, CFFDictPrivateDataMap, CFFEncodingMap, CFFStrings,
Components, Dict, dump, error, isNum, netscape, Stream */
'use strict';
/*
* The Type2 reader code below is only used for debugging purpose since Type2
* is only a CharString format and is never used directly as a Font file.
*
* So the code here is useful for dumping the data content of a .cff file in
* order to investigate the similarity between a Type1 CharString and a Type2
* CharString or to understand the structure of the CFF format.
*/
/*
* Build a charset by assigning the glyph name and the human readable form
* of the glyph data.
*/
function readCharset(aStream, aCharstrings) {
var charset = {};
var format = aStream.getByte();
var count = aCharstrings.length - 1;
var i, sid;
if (format === 0) {
charset['.notdef'] = readCharstringEncoding(aCharstrings[0]);
for (i = 1; i < count + 1; i++) {
sid = aStream.getByte() << 8 | aStream.getByte();
charset[CFFStrings[sid]] = readCharstringEncoding(aCharstrings[i]);
}
} else if (format === 1) {
for (i = 1; i < count + 1; i++) {
var first = aStream.getByte();
first = (first << 8) | aStream.getByte();
var numLeft = aStream.getByte();
for (var j = 0; j <= numLeft; j++) {
sid = first++;
charset[CFFStrings[sid]] = readCharstringEncoding(aCharstrings[j]);
}
}
} else {
error('Invalid charset format');
}
return charset;
}
/*
* Take a Type2 binary charstring as input and transform it to a human
* readable representation as specified by the 'The Type 2 Charstring Format',
* chapter 3.1.
*/
function readCharstringEncoding(aString) {
if (!aString) {
return '';
}
var charstringTokens = [];
var count = aString.length;
for (var i = 0; i < count; ) {
var value = aString[i++] | 0;
var token = null;
if (value < 0) {
continue;
} else if (value <= 11) {
token = CFFEncodingMap[value];
} else if (value === 12) {
token = CFFEncodingMap[value][aString[i++]];
} else if (value <= 18) {
token = CFFEncodingMap[value];
} else if (value <= 20) {
++i; // var mask = aString[i++];
token = CFFEncodingMap[value];
} else if (value <= 27) {
token = CFFEncodingMap[value];
} else if (value === 28) {
token = aString[i++] << 8 | aString[i++];
} else if (value <= 31) {
token = CFFEncodingMap[value];
} else if (value < 247) {
token = parseInt(value, 10) - 139;
} else if (value < 251) {
token = (value - 247) * 256 + aString[i++] + 108;
} else if (value < 255) {
token = -(value - 251) * 256 - aString[i++] - 108;
} else { // value === 255
token = aString[i++] << 24 | aString[i++] << 16 |
aString[i++] << 8 | aString[i];
}
charstringTokens.push(token);
}
return charstringTokens;
}
/*
* Take a binary DICT Data as input and transform it into a human readable
* form as specified by 'The Compact Font Format Specification', chapter 5.
*/
function readFontDictData(aString, aMap) {
var fontDictDataTokens = [];
var count = aString.length;
for (var i = 0; i < count; i) {
var value = aString[i++] | 0;
var token = null;
if (value === 12) {
token = aMap[value][aString[i++]];
} else if (value === 28) {
token = aString[i++] << 8 | aString[i++];
} else if (value === 29) {
token = aString[i++] << 24 |
aString[i++] << 16 |
aString[i++] << 8 |
aString[i++];
} else if (value === 30) {
token = '';
var parsed = false;
while (!parsed) {
var octet = aString[i++];
var nibbles = [parseInt(octet / 16, 10), parseInt(octet % 16, 10)];
for (var j = 0; j < nibbles.length; j++) {
var nibble = nibbles[j];
switch (nibble) {
case 0xA:
token += '.';
break;
case 0xB:
token += 'E';
break;
case 0xC:
token += 'E-';
break;
case 0xD:
break;
case 0xE:
token += '-';
break;
case 0xF:
parsed = true;
break;
default:
token += nibble;
break;
}
}
}
token = parseFloat(token);
} else if (value <= 31) {
token = aMap[value];
} else if (value <= 246) {
token = parseInt(value, 10) - 139;
} else if (value <= 250) {
token = (value - 247) * 256 + aString[i++] + 108;
} else if (value <= 254) {
token = -(value - 251) * 256 - aString[i++] - 108;
} else if (value === 255) {
error('255 is not a valid DICT command');
}
fontDictDataTokens.push(token);
}
return fontDictDataTokens;
}
/*
* Take a stream as input and return an array of objects.
* In CFF an INDEX is a structure with the following format:
* {
* count: 2 bytes (Number of objects stored in INDEX),
* offsize: 1 byte (Offset array element size),
* offset: [count + 1] bytes (Offsets array),
* data: - (Objects data)
* }
*
* More explanation are given in the 'CFF Font Format Specification',
* chapter 5.
*/
function readFontIndexData(aStream, aIsByte) {
var count = aStream.getByte() << 8 | aStream.getByte();
var offsize = aStream.getByte();
function getNextOffset() {
switch (offsize) {
case 0:
return 0;
case 1:
return aStream.getByte();
case 2:
return aStream.getByte() << 8 | aStream.getByte();
case 3:
return aStream.getByte() << 16 | aStream.getByte() << 8 |
aStream.getByte();
case 4:
return aStream.getByte() << 24 | aStream.getByte() << 16 |
aStream.getByte() << 8 | aStream.getByte();
}
error(offsize + ' is not a valid offset size');
return null;
}
var offsets = [];
var i;
for (i = 0; i < count + 1; i++) {
offsets.push(getNextOffset());
}
dump('Found ' + count + ' objects at offsets :' +
offsets + ' (offsize: ' + offsize + ')');
// Now extract the objects
var relativeOffset = aStream.pos;
var objects = [];
for (i = 0; i < count; i++) {
var offset = offsets[i];
aStream.pos = relativeOffset + offset - 1;
var data = [];
var length = offsets[i + 1] - 1;
for (var j = offset - 1; j < length; j++) {
data.push(aIsByte ? aStream.getByte() : aStream.getChar());
}
objects.push(data);
}
return objects;
}
var Type2Parser = function type2Parser(aFilePath) {
var font = new Dict(null);
var xhr = new XMLHttpRequest();
xhr.open('GET', aFilePath, false);
xhr.responseType = 'arraybuffer';
xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
xhr.send(null);
this.data = new Stream(xhr.response);
// Turn on this flag for additional debugging logs
var debug = false;
function dump(aStr) {
if (debug) {
console.log(aStr);
}
}
function parseAsToken(aString, aMap) {
var decoded = readFontDictData(aString, aMap);
var stack = [];
var count = decoded.length;
for (var i = 0; i < count; i++) {
var token = decoded[i];
if (isNum(token)) {
stack.push(token);
} else {
switch (token.operand) {
case 'SID':
font.set(token.name, CFFStrings[stack.pop()]);
break;
case 'number number':
font.set(token.name, {
offset: stack.pop(),
size: stack.pop()
});
break;
case 'boolean':
font.set(token.name, stack.pop());
break;
case 'delta':
font.set(token.name, stack.pop());
break;
default:
if (token.operand && token.operand.length) {
var array = [];
for (var j = 0; j < token.operand.length; j++) {
array.push(stack.pop());
}
font.set(token.name, array);
} else {
font.set(token.name, stack.pop());
}
break;
}
}
}
}
this.parse = function type2ParserParse(aStream) {
font.set('major', aStream.getByte());
font.set('minor', aStream.getByte());
font.set('hdrSize', aStream.getByte());
font.set('offsize', aStream.getByte());
// Read the NAME Index
dump('Reading Index: Names');
font.set('Names', readFontIndexData(aStream));
dump('Names: ' + font.get('Names'));
// Read the Top Dict Index
dump('Reading Index: TopDict');
var topDict = readFontIndexData(aStream, true);
dump('TopDict: ' + topDict);
// Read the String Index
dump('Reading Index: Strings');
var strings = readFontIndexData(aStream);
dump('strings: ' + strings);
// Fill up the Strings dictionary with the new unique strings
var i;
for (i = 0; i < strings.length; i++) {
CFFStrings.push(strings[i].join(''));
}
// Parse the TopDict operator
var count = topDict.length;
for (i = 0; i < count; i++) {
parseAsToken(topDict[i], CFFDictDataMap);
}
// Read the Global Subr Index that comes just after the Strings Index
// (cf. "The Compact Font Format Specification" Chapter 16)
dump('Reading Global Subr Index');
var subrs = readFontIndexData(aStream, true);
dump(subrs);
// Reading Private Dict
var priv = font.get('Private');
dump('Reading Private Dict (offset: ' + priv.offset +
' size: ' + priv.size + ')');
aStream.pos = priv.offset;
var privateDict = [];
for (i = 0; i < priv.size; i++) {
privateDict.push(aStream.getByte());
}
dump('privateData:' + privateDict);
parseAsToken(privateDict, CFFDictPrivateDataMap);
for (var p in font.map) {
dump(p + '::' + font.get(p));
}
// Read CharStrings Index
var charStringsOffset = font.get('CharStrings');
dump('Read CharStrings Index (offset: ' + charStringsOffset + ')');
aStream.pos = charStringsOffset;
var charStrings = readFontIndexData(aStream, true);
// Read Charset
dump('Read Charset for ' + charStrings.length + ' glyphs');
var charsetEntry = font.get('charset');
if (charsetEntry === 0) {
error('Need to support CFFISOAdobeCharset');
} else if (charsetEntry === 1) {
error('Need to support CFFExpert');
} else if (charsetEntry === 2) {
error('Need to support CFFExpertSubsetCharset');
} else {
aStream.pos = charsetEntry;
readCharset(aStream, charStrings);
}
};
};
/*
* To try the Type2 decoder on a local file in the current directory:
*
* var cff = new Type2Parser("file.cff");
* cff.parse(this.data);
*
* To try the Type2 decoder on a custom built CFF array:
*
* var file = new Uint8Array(cffFileArray, 0, cffFileSize);
* var parser = new Type2Parser();
* parser.parse(new Stream(file));
*
*/
/*
* Write to a file to the disk (works only on Firefox in privilege mode)
* but this is useful for dumping a font file to the disk and check with
* fontforge or the ots program what's wrong with the file.
*
* writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
*/
function writeToFile(aBytes, aFilePath) {
if (!('netscape' in window)) {
return;
}
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var Cc = Components.classes,
Ci = Components.interfaces;
var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
file.initWithPath(aFilePath);
var stream = Cc['@mozilla.org/network/file-output-stream;1']
.createInstance(Ci.nsIFileOutputStream);
stream.init(file, 0x04 | 0x08 | 0x20, 0x180, 0);
var bos = Cc['@mozilla.org/binaryoutputstream;1']
.createInstance(Ci.nsIBinaryOutputStream);
bos.setOutputStream(stream);
bos.writeByteArray(aBytes, aBytes.length);
stream.close();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
*
* 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.
*/
/* globals PDFJS, Util */
'use strict';
// List of shared files to include;
var sharedFiles = [
'shared/util.js'
];
// List of other files to include;
var otherFiles = [
'core/network.js',
'core/chunked_stream.js',
'core/pdf_manager.js',
'core/core.js',
'core/obj.js',
'core/charsets.js',
'core/annotation.js',
'core/function.js',
'core/colorspace.js',
'core/crypto.js',
'core/pattern.js',
'core/evaluator.js',
'core/cmap.js',
'core/fonts.js',
'core/font_renderer.js',
'core/glyphlist.js',
'core/image.js',
'core/metrics.js',
'core/parser.js',
'core/ps_parser.js',
'core/stream.js',
'core/worker.js',
'core/arithmetic_decoder.js',
'core/jpg.js',
'core/jpx.js',
'core/jbig2.js',
'core/bidi.js',
'core/murmurhash3.js'
];
function loadInOrder(index, path, files) {
if (index >= files.length) {
PDFJS.fakeWorkerFilesLoadedCapability.resolve();
return;
}
PDFJS.Util.loadScript(path + files[index],
loadInOrder.bind(null, ++index, path, files));
}
// Load all the files.
if (typeof PDFJS === 'undefined' || !PDFJS.fakeWorkerFilesLoadedCapability) {
var files = sharedFiles.concat(otherFiles);
for (var i = 0; i < files.length; i++) {
importScripts(files[i]);
}
} else {
var src = PDFJS.workerSrc;
var path = src.substr(0, src.indexOf('worker_loader.js'));
// If Util is available, we assume that shared files are already loaded. Can
// happen that they are not if PDF.js is bundled inside a special namespace.
var skipShared = typeof Util !== 'undefined';
var files = skipShared ? otherFiles : sharedFiles.concat(otherFiles);
loadInOrder(0, path, files);
}