Now we have a full list of all possible annotation types and the numbering corresponds to the order in the specification. Not only is this more consistent and complete, it also prevents having to add these constants when a new annotation type is implemented. Additionally fix an issue where a regular Widget annotation would not have `data.annotationType` set. It was only set for a TextWidgetAnnotation, but instead move it to the base Widget annotation class to add it for all Widget annotations (since TextWidgetAnnotation inherits from WidgetAnnotation it will have it too).
1672 lines
47 KiB
JavaScript
1672 lines
47 KiB
JavaScript
/* 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 Cmd, ColorSpace, Dict, MozBlobBuilder, Name, PDFJS, Ref, URL,
|
|
Promise */
|
|
|
|
'use strict';
|
|
|
|
var globalScope = (typeof window === 'undefined') ? this : window;
|
|
|
|
var isWorker = (typeof window === 'undefined');
|
|
|
|
var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
|
|
|
|
var TextRenderingMode = {
|
|
FILL: 0,
|
|
STROKE: 1,
|
|
FILL_STROKE: 2,
|
|
INVISIBLE: 3,
|
|
FILL_ADD_TO_PATH: 4,
|
|
STROKE_ADD_TO_PATH: 5,
|
|
FILL_STROKE_ADD_TO_PATH: 6,
|
|
ADD_TO_PATH: 7,
|
|
FILL_STROKE_MASK: 3,
|
|
ADD_TO_PATH_FLAG: 4
|
|
};
|
|
|
|
var ImageKind = {
|
|
GRAYSCALE_1BPP: 1,
|
|
RGB_24BPP: 2,
|
|
RGBA_32BPP: 3
|
|
};
|
|
|
|
var AnnotationType = {
|
|
TEXT: 1,
|
|
LINK: 2,
|
|
FREETEXT: 3,
|
|
LINE: 4,
|
|
SQUARE: 5,
|
|
CIRCLE: 6,
|
|
POLYGON: 7,
|
|
POLYLINE: 8,
|
|
HIGHLIGHT: 9,
|
|
UNDERLINE: 10,
|
|
SQUIGGLY: 11,
|
|
STRIKEOUT: 12,
|
|
STAMP: 13,
|
|
CARET: 14,
|
|
INK: 15,
|
|
POPUP: 16,
|
|
FILEATTACHMENT: 17,
|
|
SOUND: 18,
|
|
MOVIE: 19,
|
|
WIDGET: 20,
|
|
SCREEN: 21,
|
|
PRINTERMARK: 22,
|
|
TRAPNET: 23,
|
|
WATERMARK: 24,
|
|
THREED: 25,
|
|
REDACT: 26
|
|
};
|
|
|
|
var AnnotationFlag = {
|
|
INVISIBLE: 0x01,
|
|
HIDDEN: 0x02,
|
|
PRINT: 0x04,
|
|
NOZOOM: 0x08,
|
|
NOROTATE: 0x10,
|
|
NOVIEW: 0x20,
|
|
READONLY: 0x40,
|
|
LOCKED: 0x80,
|
|
TOGGLENOVIEW: 0x100,
|
|
LOCKEDCONTENTS: 0x200
|
|
};
|
|
|
|
var AnnotationBorderStyleType = {
|
|
SOLID: 1,
|
|
DASHED: 2,
|
|
BEVELED: 3,
|
|
INSET: 4,
|
|
UNDERLINE: 5
|
|
};
|
|
|
|
var StreamType = {
|
|
UNKNOWN: 0,
|
|
FLATE: 1,
|
|
LZW: 2,
|
|
DCT: 3,
|
|
JPX: 4,
|
|
JBIG: 5,
|
|
A85: 6,
|
|
AHX: 7,
|
|
CCF: 8,
|
|
RL: 9
|
|
};
|
|
|
|
var FontType = {
|
|
UNKNOWN: 0,
|
|
TYPE1: 1,
|
|
TYPE1C: 2,
|
|
CIDFONTTYPE0: 3,
|
|
CIDFONTTYPE0C: 4,
|
|
TRUETYPE: 5,
|
|
CIDFONTTYPE2: 6,
|
|
TYPE3: 7,
|
|
OPENTYPE: 8,
|
|
TYPE0: 9,
|
|
MMTYPE1: 10
|
|
};
|
|
|
|
// The global PDFJS object exposes the API
|
|
// In production, it will be declared outside a global wrapper
|
|
// In development, it will be declared here
|
|
if (!globalScope.PDFJS) {
|
|
globalScope.PDFJS = {};
|
|
}
|
|
|
|
globalScope.PDFJS.pdfBug = false;
|
|
|
|
PDFJS.VERBOSITY_LEVELS = {
|
|
errors: 0,
|
|
warnings: 1,
|
|
infos: 5
|
|
};
|
|
|
|
// All the possible operations for an operator list.
|
|
var OPS = PDFJS.OPS = {
|
|
// Intentionally start from 1 so it is easy to spot bad operators that will be
|
|
// 0's.
|
|
dependency: 1,
|
|
setLineWidth: 2,
|
|
setLineCap: 3,
|
|
setLineJoin: 4,
|
|
setMiterLimit: 5,
|
|
setDash: 6,
|
|
setRenderingIntent: 7,
|
|
setFlatness: 8,
|
|
setGState: 9,
|
|
save: 10,
|
|
restore: 11,
|
|
transform: 12,
|
|
moveTo: 13,
|
|
lineTo: 14,
|
|
curveTo: 15,
|
|
curveTo2: 16,
|
|
curveTo3: 17,
|
|
closePath: 18,
|
|
rectangle: 19,
|
|
stroke: 20,
|
|
closeStroke: 21,
|
|
fill: 22,
|
|
eoFill: 23,
|
|
fillStroke: 24,
|
|
eoFillStroke: 25,
|
|
closeFillStroke: 26,
|
|
closeEOFillStroke: 27,
|
|
endPath: 28,
|
|
clip: 29,
|
|
eoClip: 30,
|
|
beginText: 31,
|
|
endText: 32,
|
|
setCharSpacing: 33,
|
|
setWordSpacing: 34,
|
|
setHScale: 35,
|
|
setLeading: 36,
|
|
setFont: 37,
|
|
setTextRenderingMode: 38,
|
|
setTextRise: 39,
|
|
moveText: 40,
|
|
setLeadingMoveText: 41,
|
|
setTextMatrix: 42,
|
|
nextLine: 43,
|
|
showText: 44,
|
|
showSpacedText: 45,
|
|
nextLineShowText: 46,
|
|
nextLineSetSpacingShowText: 47,
|
|
setCharWidth: 48,
|
|
setCharWidthAndBounds: 49,
|
|
setStrokeColorSpace: 50,
|
|
setFillColorSpace: 51,
|
|
setStrokeColor: 52,
|
|
setStrokeColorN: 53,
|
|
setFillColor: 54,
|
|
setFillColorN: 55,
|
|
setStrokeGray: 56,
|
|
setFillGray: 57,
|
|
setStrokeRGBColor: 58,
|
|
setFillRGBColor: 59,
|
|
setStrokeCMYKColor: 60,
|
|
setFillCMYKColor: 61,
|
|
shadingFill: 62,
|
|
beginInlineImage: 63,
|
|
beginImageData: 64,
|
|
endInlineImage: 65,
|
|
paintXObject: 66,
|
|
markPoint: 67,
|
|
markPointProps: 68,
|
|
beginMarkedContent: 69,
|
|
beginMarkedContentProps: 70,
|
|
endMarkedContent: 71,
|
|
beginCompat: 72,
|
|
endCompat: 73,
|
|
paintFormXObjectBegin: 74,
|
|
paintFormXObjectEnd: 75,
|
|
beginGroup: 76,
|
|
endGroup: 77,
|
|
beginAnnotations: 78,
|
|
endAnnotations: 79,
|
|
beginAnnotation: 80,
|
|
endAnnotation: 81,
|
|
paintJpegXObject: 82,
|
|
paintImageMaskXObject: 83,
|
|
paintImageMaskXObjectGroup: 84,
|
|
paintImageXObject: 85,
|
|
paintInlineImageXObject: 86,
|
|
paintInlineImageXObjectGroup: 87,
|
|
paintImageXObjectRepeat: 88,
|
|
paintImageMaskXObjectRepeat: 89,
|
|
paintSolidColorImageMask: 90,
|
|
constructPath: 91
|
|
};
|
|
|
|
// A notice for devs. These are good for things that are helpful to devs, such
|
|
// as warning that Workers were disabled, which is important to devs but not
|
|
// end users.
|
|
function info(msg) {
|
|
if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.infos) {
|
|
console.log('Info: ' + msg);
|
|
}
|
|
}
|
|
|
|
// Non-fatal warnings.
|
|
function warn(msg) {
|
|
if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.warnings) {
|
|
console.log('Warning: ' + msg);
|
|
}
|
|
}
|
|
|
|
// Deprecated API function -- treated as warnings.
|
|
function deprecated(details) {
|
|
warn('Deprecated API usage: ' + details);
|
|
}
|
|
|
|
// Fatal errors that should trigger the fallback UI and halt execution by
|
|
// throwing an exception.
|
|
function error(msg) {
|
|
if (PDFJS.verbosity >= PDFJS.VERBOSITY_LEVELS.errors) {
|
|
console.log('Error: ' + msg);
|
|
console.log(backtrace());
|
|
}
|
|
throw new Error(msg);
|
|
}
|
|
|
|
function backtrace() {
|
|
try {
|
|
throw new Error();
|
|
} catch (e) {
|
|
return e.stack ? e.stack.split('\n').slice(2).join('\n') : '';
|
|
}
|
|
}
|
|
|
|
function assert(cond, msg) {
|
|
if (!cond) {
|
|
error(msg);
|
|
}
|
|
}
|
|
|
|
var UNSUPPORTED_FEATURES = PDFJS.UNSUPPORTED_FEATURES = {
|
|
unknown: 'unknown',
|
|
forms: 'forms',
|
|
javaScript: 'javaScript',
|
|
smask: 'smask',
|
|
shadingPattern: 'shadingPattern',
|
|
font: 'font'
|
|
};
|
|
|
|
// Combines two URLs. The baseUrl shall be absolute URL. If the url is an
|
|
// absolute URL, it will be returned as is.
|
|
function combineUrl(baseUrl, url) {
|
|
if (!url) {
|
|
return baseUrl;
|
|
}
|
|
if (/^[a-z][a-z0-9+\-.]*:/i.test(url)) {
|
|
return url;
|
|
}
|
|
var i;
|
|
if (url.charAt(0) === '/') {
|
|
// absolute path
|
|
i = baseUrl.indexOf('://');
|
|
if (url.charAt(1) === '/') {
|
|
++i;
|
|
} else {
|
|
i = baseUrl.indexOf('/', i + 3);
|
|
}
|
|
return baseUrl.substring(0, i) + url;
|
|
} else {
|
|
// relative path
|
|
var pathLength = baseUrl.length;
|
|
i = baseUrl.lastIndexOf('#');
|
|
pathLength = i >= 0 ? i : pathLength;
|
|
i = baseUrl.lastIndexOf('?', pathLength);
|
|
pathLength = i >= 0 ? i : pathLength;
|
|
var prefixLength = baseUrl.lastIndexOf('/', pathLength);
|
|
return baseUrl.substring(0, prefixLength + 1) + url;
|
|
}
|
|
}
|
|
|
|
// Validates if URL is safe and allowed, e.g. to avoid XSS.
|
|
function isValidUrl(url, allowRelative) {
|
|
if (!url) {
|
|
return false;
|
|
}
|
|
// RFC 3986 (http://tools.ietf.org/html/rfc3986#section-3.1)
|
|
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
var protocol = /^[a-z][a-z0-9+\-.]*(?=:)/i.exec(url);
|
|
if (!protocol) {
|
|
return allowRelative;
|
|
}
|
|
protocol = protocol[0].toLowerCase();
|
|
switch (protocol) {
|
|
case 'http':
|
|
case 'https':
|
|
case 'ftp':
|
|
case 'mailto':
|
|
case 'tel':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
PDFJS.isValidUrl = isValidUrl;
|
|
|
|
function shadow(obj, prop, value) {
|
|
Object.defineProperty(obj, prop, { value: value,
|
|
enumerable: true,
|
|
configurable: true,
|
|
writable: false });
|
|
return value;
|
|
}
|
|
PDFJS.shadow = shadow;
|
|
|
|
var LinkTarget = PDFJS.LinkTarget = {
|
|
NONE: 0, // Default value.
|
|
SELF: 1,
|
|
BLANK: 2,
|
|
PARENT: 3,
|
|
TOP: 4,
|
|
};
|
|
var LinkTargetStringMap = [
|
|
'',
|
|
'_self',
|
|
'_blank',
|
|
'_parent',
|
|
'_top'
|
|
];
|
|
|
|
function isExternalLinkTargetSet() {
|
|
//#if !MOZCENTRAL
|
|
if (PDFJS.openExternalLinksInNewWindow) {
|
|
deprecated('PDFJS.openExternalLinksInNewWindow, please use ' +
|
|
'"PDFJS.externalLinkTarget = PDFJS.LinkTarget.BLANK" instead.');
|
|
if (PDFJS.externalLinkTarget === LinkTarget.NONE) {
|
|
PDFJS.externalLinkTarget = LinkTarget.BLANK;
|
|
}
|
|
// Reset the deprecated parameter, to suppress further warnings.
|
|
PDFJS.openExternalLinksInNewWindow = false;
|
|
}
|
|
//#endif
|
|
switch (PDFJS.externalLinkTarget) {
|
|
case LinkTarget.NONE:
|
|
return false;
|
|
case LinkTarget.SELF:
|
|
case LinkTarget.BLANK:
|
|
case LinkTarget.PARENT:
|
|
case LinkTarget.TOP:
|
|
return true;
|
|
}
|
|
warn('PDFJS.externalLinkTarget is invalid: ' + PDFJS.externalLinkTarget);
|
|
// Reset the external link target, to suppress further warnings.
|
|
PDFJS.externalLinkTarget = LinkTarget.NONE;
|
|
return false;
|
|
}
|
|
PDFJS.isExternalLinkTargetSet = isExternalLinkTargetSet;
|
|
|
|
var PasswordResponses = PDFJS.PasswordResponses = {
|
|
NEED_PASSWORD: 1,
|
|
INCORRECT_PASSWORD: 2
|
|
};
|
|
|
|
var PasswordException = (function PasswordExceptionClosure() {
|
|
function PasswordException(msg, code) {
|
|
this.name = 'PasswordException';
|
|
this.message = msg;
|
|
this.code = code;
|
|
}
|
|
|
|
PasswordException.prototype = new Error();
|
|
PasswordException.constructor = PasswordException;
|
|
|
|
return PasswordException;
|
|
})();
|
|
PDFJS.PasswordException = PasswordException;
|
|
|
|
var UnknownErrorException = (function UnknownErrorExceptionClosure() {
|
|
function UnknownErrorException(msg, details) {
|
|
this.name = 'UnknownErrorException';
|
|
this.message = msg;
|
|
this.details = details;
|
|
}
|
|
|
|
UnknownErrorException.prototype = new Error();
|
|
UnknownErrorException.constructor = UnknownErrorException;
|
|
|
|
return UnknownErrorException;
|
|
})();
|
|
PDFJS.UnknownErrorException = UnknownErrorException;
|
|
|
|
var InvalidPDFException = (function InvalidPDFExceptionClosure() {
|
|
function InvalidPDFException(msg) {
|
|
this.name = 'InvalidPDFException';
|
|
this.message = msg;
|
|
}
|
|
|
|
InvalidPDFException.prototype = new Error();
|
|
InvalidPDFException.constructor = InvalidPDFException;
|
|
|
|
return InvalidPDFException;
|
|
})();
|
|
PDFJS.InvalidPDFException = InvalidPDFException;
|
|
|
|
var MissingPDFException = (function MissingPDFExceptionClosure() {
|
|
function MissingPDFException(msg) {
|
|
this.name = 'MissingPDFException';
|
|
this.message = msg;
|
|
}
|
|
|
|
MissingPDFException.prototype = new Error();
|
|
MissingPDFException.constructor = MissingPDFException;
|
|
|
|
return MissingPDFException;
|
|
})();
|
|
PDFJS.MissingPDFException = MissingPDFException;
|
|
|
|
var UnexpectedResponseException =
|
|
(function UnexpectedResponseExceptionClosure() {
|
|
function UnexpectedResponseException(msg, status) {
|
|
this.name = 'UnexpectedResponseException';
|
|
this.message = msg;
|
|
this.status = status;
|
|
}
|
|
|
|
UnexpectedResponseException.prototype = new Error();
|
|
UnexpectedResponseException.constructor = UnexpectedResponseException;
|
|
|
|
return UnexpectedResponseException;
|
|
})();
|
|
PDFJS.UnexpectedResponseException = UnexpectedResponseException;
|
|
|
|
var NotImplementedException = (function NotImplementedExceptionClosure() {
|
|
function NotImplementedException(msg) {
|
|
this.message = msg;
|
|
}
|
|
|
|
NotImplementedException.prototype = new Error();
|
|
NotImplementedException.prototype.name = 'NotImplementedException';
|
|
NotImplementedException.constructor = NotImplementedException;
|
|
|
|
return NotImplementedException;
|
|
})();
|
|
|
|
var MissingDataException = (function MissingDataExceptionClosure() {
|
|
function MissingDataException(begin, end) {
|
|
this.begin = begin;
|
|
this.end = end;
|
|
this.message = 'Missing data [' + begin + ', ' + end + ')';
|
|
}
|
|
|
|
MissingDataException.prototype = new Error();
|
|
MissingDataException.prototype.name = 'MissingDataException';
|
|
MissingDataException.constructor = MissingDataException;
|
|
|
|
return MissingDataException;
|
|
})();
|
|
|
|
var XRefParseException = (function XRefParseExceptionClosure() {
|
|
function XRefParseException(msg) {
|
|
this.message = msg;
|
|
}
|
|
|
|
XRefParseException.prototype = new Error();
|
|
XRefParseException.prototype.name = 'XRefParseException';
|
|
XRefParseException.constructor = XRefParseException;
|
|
|
|
return XRefParseException;
|
|
})();
|
|
|
|
|
|
function bytesToString(bytes) {
|
|
assert(bytes !== null && typeof bytes === 'object' &&
|
|
bytes.length !== undefined, 'Invalid argument for bytesToString');
|
|
var length = bytes.length;
|
|
var MAX_ARGUMENT_COUNT = 8192;
|
|
if (length < MAX_ARGUMENT_COUNT) {
|
|
return String.fromCharCode.apply(null, bytes);
|
|
}
|
|
var strBuf = [];
|
|
for (var i = 0; i < length; i += MAX_ARGUMENT_COUNT) {
|
|
var chunkEnd = Math.min(i + MAX_ARGUMENT_COUNT, length);
|
|
var chunk = bytes.subarray(i, chunkEnd);
|
|
strBuf.push(String.fromCharCode.apply(null, chunk));
|
|
}
|
|
return strBuf.join('');
|
|
}
|
|
|
|
function stringToBytes(str) {
|
|
assert(typeof str === 'string', 'Invalid argument for stringToBytes');
|
|
var length = str.length;
|
|
var bytes = new Uint8Array(length);
|
|
for (var i = 0; i < length; ++i) {
|
|
bytes[i] = str.charCodeAt(i) & 0xFF;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
function string32(value) {
|
|
return String.fromCharCode((value >> 24) & 0xff, (value >> 16) & 0xff,
|
|
(value >> 8) & 0xff, value & 0xff);
|
|
}
|
|
|
|
function log2(x) {
|
|
var n = 1, i = 0;
|
|
while (x > n) {
|
|
n <<= 1;
|
|
i++;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
function readInt8(data, start) {
|
|
return (data[start] << 24) >> 24;
|
|
}
|
|
|
|
function readUint16(data, offset) {
|
|
return (data[offset] << 8) | data[offset + 1];
|
|
}
|
|
|
|
function readUint32(data, offset) {
|
|
return ((data[offset] << 24) | (data[offset + 1] << 16) |
|
|
(data[offset + 2] << 8) | data[offset + 3]) >>> 0;
|
|
}
|
|
|
|
// Lazy test the endianness of the platform
|
|
// NOTE: This will be 'true' for simulated TypedArrays
|
|
function isLittleEndian() {
|
|
var buffer8 = new Uint8Array(2);
|
|
buffer8[0] = 1;
|
|
var buffer16 = new Uint16Array(buffer8.buffer);
|
|
return (buffer16[0] === 1);
|
|
}
|
|
|
|
Object.defineProperty(PDFJS, 'isLittleEndian', {
|
|
configurable: true,
|
|
get: function PDFJS_isLittleEndian() {
|
|
return shadow(PDFJS, 'isLittleEndian', isLittleEndian());
|
|
}
|
|
});
|
|
|
|
//#if !(FIREFOX || MOZCENTRAL || CHROME)
|
|
//// Lazy test if the userAgent support CanvasTypedArrays
|
|
function hasCanvasTypedArrays() {
|
|
var canvas = document.createElement('canvas');
|
|
canvas.width = canvas.height = 1;
|
|
var ctx = canvas.getContext('2d');
|
|
var imageData = ctx.createImageData(1, 1);
|
|
return (typeof imageData.data.buffer !== 'undefined');
|
|
}
|
|
|
|
Object.defineProperty(PDFJS, 'hasCanvasTypedArrays', {
|
|
configurable: true,
|
|
get: function PDFJS_hasCanvasTypedArrays() {
|
|
return shadow(PDFJS, 'hasCanvasTypedArrays', hasCanvasTypedArrays());
|
|
}
|
|
});
|
|
|
|
var Uint32ArrayView = (function Uint32ArrayViewClosure() {
|
|
|
|
function Uint32ArrayView(buffer, length) {
|
|
this.buffer = buffer;
|
|
this.byteLength = buffer.length;
|
|
this.length = length === undefined ? (this.byteLength >> 2) : length;
|
|
ensureUint32ArrayViewProps(this.length);
|
|
}
|
|
Uint32ArrayView.prototype = Object.create(null);
|
|
|
|
var uint32ArrayViewSetters = 0;
|
|
function createUint32ArrayProp(index) {
|
|
return {
|
|
get: function () {
|
|
var buffer = this.buffer, offset = index << 2;
|
|
return (buffer[offset] | (buffer[offset + 1] << 8) |
|
|
(buffer[offset + 2] << 16) | (buffer[offset + 3] << 24)) >>> 0;
|
|
},
|
|
set: function (value) {
|
|
var buffer = this.buffer, offset = index << 2;
|
|
buffer[offset] = value & 255;
|
|
buffer[offset + 1] = (value >> 8) & 255;
|
|
buffer[offset + 2] = (value >> 16) & 255;
|
|
buffer[offset + 3] = (value >>> 24) & 255;
|
|
}
|
|
};
|
|
}
|
|
|
|
function ensureUint32ArrayViewProps(length) {
|
|
while (uint32ArrayViewSetters < length) {
|
|
Object.defineProperty(Uint32ArrayView.prototype,
|
|
uint32ArrayViewSetters,
|
|
createUint32ArrayProp(uint32ArrayViewSetters));
|
|
uint32ArrayViewSetters++;
|
|
}
|
|
}
|
|
|
|
return Uint32ArrayView;
|
|
})();
|
|
//#else
|
|
//PDFJS.hasCanvasTypedArrays = true;
|
|
//#endif
|
|
|
|
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
|
|
|
var Util = PDFJS.Util = (function UtilClosure() {
|
|
function Util() {}
|
|
|
|
var rgbBuf = ['rgb(', 0, ',', 0, ',', 0, ')'];
|
|
|
|
// makeCssRgb() can be called thousands of times. Using |rgbBuf| avoids
|
|
// creating many intermediate strings.
|
|
Util.makeCssRgb = function Util_makeCssRgb(r, g, b) {
|
|
rgbBuf[1] = r;
|
|
rgbBuf[3] = g;
|
|
rgbBuf[5] = b;
|
|
return rgbBuf.join('');
|
|
};
|
|
|
|
// Concatenates two transformation matrices together and returns the result.
|
|
Util.transform = function Util_transform(m1, m2) {
|
|
return [
|
|
m1[0] * m2[0] + m1[2] * m2[1],
|
|
m1[1] * m2[0] + m1[3] * m2[1],
|
|
m1[0] * m2[2] + m1[2] * m2[3],
|
|
m1[1] * m2[2] + m1[3] * m2[3],
|
|
m1[0] * m2[4] + m1[2] * m2[5] + m1[4],
|
|
m1[1] * m2[4] + m1[3] * m2[5] + m1[5]
|
|
];
|
|
};
|
|
|
|
// For 2d affine transforms
|
|
Util.applyTransform = function Util_applyTransform(p, m) {
|
|
var xt = p[0] * m[0] + p[1] * m[2] + m[4];
|
|
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
|
return [xt, yt];
|
|
};
|
|
|
|
Util.applyInverseTransform = function Util_applyInverseTransform(p, m) {
|
|
var d = m[0] * m[3] - m[1] * m[2];
|
|
var xt = (p[0] * m[3] - p[1] * m[2] + m[2] * m[5] - m[4] * m[3]) / d;
|
|
var yt = (-p[0] * m[1] + p[1] * m[0] + m[4] * m[1] - m[5] * m[0]) / d;
|
|
return [xt, yt];
|
|
};
|
|
|
|
// Applies the transform to the rectangle and finds the minimum axially
|
|
// aligned bounding box.
|
|
Util.getAxialAlignedBoundingBox =
|
|
function Util_getAxialAlignedBoundingBox(r, m) {
|
|
|
|
var p1 = Util.applyTransform(r, m);
|
|
var p2 = Util.applyTransform(r.slice(2, 4), m);
|
|
var p3 = Util.applyTransform([r[0], r[3]], m);
|
|
var p4 = Util.applyTransform([r[2], r[1]], m);
|
|
return [
|
|
Math.min(p1[0], p2[0], p3[0], p4[0]),
|
|
Math.min(p1[1], p2[1], p3[1], p4[1]),
|
|
Math.max(p1[0], p2[0], p3[0], p4[0]),
|
|
Math.max(p1[1], p2[1], p3[1], p4[1])
|
|
];
|
|
};
|
|
|
|
Util.inverseTransform = function Util_inverseTransform(m) {
|
|
var d = m[0] * m[3] - m[1] * m[2];
|
|
return [m[3] / d, -m[1] / d, -m[2] / d, m[0] / d,
|
|
(m[2] * m[5] - m[4] * m[3]) / d, (m[4] * m[1] - m[5] * m[0]) / d];
|
|
};
|
|
|
|
// Apply a generic 3d matrix M on a 3-vector v:
|
|
// | a b c | | X |
|
|
// | d e f | x | Y |
|
|
// | g h i | | Z |
|
|
// M is assumed to be serialized as [a,b,c,d,e,f,g,h,i],
|
|
// with v as [X,Y,Z]
|
|
Util.apply3dTransform = function Util_apply3dTransform(m, v) {
|
|
return [
|
|
m[0] * v[0] + m[1] * v[1] + m[2] * v[2],
|
|
m[3] * v[0] + m[4] * v[1] + m[5] * v[2],
|
|
m[6] * v[0] + m[7] * v[1] + m[8] * v[2]
|
|
];
|
|
};
|
|
|
|
// This calculation uses Singular Value Decomposition.
|
|
// The SVD can be represented with formula A = USV. We are interested in the
|
|
// matrix S here because it represents the scale values.
|
|
Util.singularValueDecompose2dScale =
|
|
function Util_singularValueDecompose2dScale(m) {
|
|
|
|
var transpose = [m[0], m[2], m[1], m[3]];
|
|
|
|
// Multiply matrix m with its transpose.
|
|
var a = m[0] * transpose[0] + m[1] * transpose[2];
|
|
var b = m[0] * transpose[1] + m[1] * transpose[3];
|
|
var c = m[2] * transpose[0] + m[3] * transpose[2];
|
|
var d = m[2] * transpose[1] + m[3] * transpose[3];
|
|
|
|
// Solve the second degree polynomial to get roots.
|
|
var first = (a + d) / 2;
|
|
var second = Math.sqrt((a + d) * (a + d) - 4 * (a * d - c * b)) / 2;
|
|
var sx = first + second || 1;
|
|
var sy = first - second || 1;
|
|
|
|
// Scale values are the square roots of the eigenvalues.
|
|
return [Math.sqrt(sx), Math.sqrt(sy)];
|
|
};
|
|
|
|
// Normalize rectangle rect=[x1, y1, x2, y2] so that (x1,y1) < (x2,y2)
|
|
// For coordinate systems whose origin lies in the bottom-left, this
|
|
// means normalization to (BL,TR) ordering. For systems with origin in the
|
|
// top-left, this means (TL,BR) ordering.
|
|
Util.normalizeRect = function Util_normalizeRect(rect) {
|
|
var r = rect.slice(0); // clone rect
|
|
if (rect[0] > rect[2]) {
|
|
r[0] = rect[2];
|
|
r[2] = rect[0];
|
|
}
|
|
if (rect[1] > rect[3]) {
|
|
r[1] = rect[3];
|
|
r[3] = rect[1];
|
|
}
|
|
return r;
|
|
};
|
|
|
|
// Returns a rectangle [x1, y1, x2, y2] corresponding to the
|
|
// intersection of rect1 and rect2. If no intersection, returns 'false'
|
|
// The rectangle coordinates of rect1, rect2 should be [x1, y1, x2, y2]
|
|
Util.intersect = function Util_intersect(rect1, rect2) {
|
|
function compare(a, b) {
|
|
return a - b;
|
|
}
|
|
|
|
// Order points along the axes
|
|
var orderedX = [rect1[0], rect1[2], rect2[0], rect2[2]].sort(compare),
|
|
orderedY = [rect1[1], rect1[3], rect2[1], rect2[3]].sort(compare),
|
|
result = [];
|
|
|
|
rect1 = Util.normalizeRect(rect1);
|
|
rect2 = Util.normalizeRect(rect2);
|
|
|
|
// X: first and second points belong to different rectangles?
|
|
if ((orderedX[0] === rect1[0] && orderedX[1] === rect2[0]) ||
|
|
(orderedX[0] === rect2[0] && orderedX[1] === rect1[0])) {
|
|
// Intersection must be between second and third points
|
|
result[0] = orderedX[1];
|
|
result[2] = orderedX[2];
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
// Y: first and second points belong to different rectangles?
|
|
if ((orderedY[0] === rect1[1] && orderedY[1] === rect2[1]) ||
|
|
(orderedY[0] === rect2[1] && orderedY[1] === rect1[1])) {
|
|
// Intersection must be between second and third points
|
|
result[1] = orderedY[1];
|
|
result[3] = orderedY[2];
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
Util.sign = function Util_sign(num) {
|
|
return num < 0 ? -1 : 1;
|
|
};
|
|
|
|
Util.appendToArray = function Util_appendToArray(arr1, arr2) {
|
|
Array.prototype.push.apply(arr1, arr2);
|
|
};
|
|
|
|
Util.prependToArray = function Util_prependToArray(arr1, arr2) {
|
|
Array.prototype.unshift.apply(arr1, arr2);
|
|
};
|
|
|
|
Util.extendObj = function extendObj(obj1, obj2) {
|
|
for (var key in obj2) {
|
|
obj1[key] = obj2[key];
|
|
}
|
|
};
|
|
|
|
Util.getInheritableProperty = function Util_getInheritableProperty(dict,
|
|
name) {
|
|
while (dict && !dict.has(name)) {
|
|
dict = dict.get('Parent');
|
|
}
|
|
if (!dict) {
|
|
return null;
|
|
}
|
|
return dict.get(name);
|
|
};
|
|
|
|
Util.inherit = function Util_inherit(sub, base, prototype) {
|
|
sub.prototype = Object.create(base.prototype);
|
|
sub.prototype.constructor = sub;
|
|
for (var prop in prototype) {
|
|
sub.prototype[prop] = prototype[prop];
|
|
}
|
|
};
|
|
|
|
Util.loadScript = function Util_loadScript(src, callback) {
|
|
var script = document.createElement('script');
|
|
var loaded = false;
|
|
script.setAttribute('src', src);
|
|
if (callback) {
|
|
script.onload = function() {
|
|
if (!loaded) {
|
|
callback();
|
|
}
|
|
loaded = true;
|
|
};
|
|
}
|
|
document.getElementsByTagName('head')[0].appendChild(script);
|
|
};
|
|
|
|
return Util;
|
|
})();
|
|
|
|
/**
|
|
* PDF page viewport created based on scale, rotation and offset.
|
|
* @class
|
|
* @alias PDFJS.PageViewport
|
|
*/
|
|
var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
|
|
/**
|
|
* @constructor
|
|
* @private
|
|
* @param viewBox {Array} xMin, yMin, xMax and yMax coordinates.
|
|
* @param scale {number} scale of the viewport.
|
|
* @param rotation {number} rotations of the viewport in degrees.
|
|
* @param offsetX {number} offset X
|
|
* @param offsetY {number} offset Y
|
|
* @param dontFlip {boolean} if true, axis Y will not be flipped.
|
|
*/
|
|
function PageViewport(viewBox, scale, rotation, offsetX, offsetY, dontFlip) {
|
|
this.viewBox = viewBox;
|
|
this.scale = scale;
|
|
this.rotation = rotation;
|
|
this.offsetX = offsetX;
|
|
this.offsetY = offsetY;
|
|
|
|
// creating transform to convert pdf coordinate system to the normal
|
|
// canvas like coordinates taking in account scale and rotation
|
|
var centerX = (viewBox[2] + viewBox[0]) / 2;
|
|
var centerY = (viewBox[3] + viewBox[1]) / 2;
|
|
var rotateA, rotateB, rotateC, rotateD;
|
|
rotation = rotation % 360;
|
|
rotation = rotation < 0 ? rotation + 360 : rotation;
|
|
switch (rotation) {
|
|
case 180:
|
|
rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
|
|
break;
|
|
case 90:
|
|
rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
|
|
break;
|
|
case 270:
|
|
rotateA = 0; rotateB = -1; rotateC = -1; rotateD = 0;
|
|
break;
|
|
//case 0:
|
|
default:
|
|
rotateA = 1; rotateB = 0; rotateC = 0; rotateD = -1;
|
|
break;
|
|
}
|
|
|
|
if (dontFlip) {
|
|
rotateC = -rotateC; rotateD = -rotateD;
|
|
}
|
|
|
|
var offsetCanvasX, offsetCanvasY;
|
|
var width, height;
|
|
if (rotateA === 0) {
|
|
offsetCanvasX = Math.abs(centerY - viewBox[1]) * scale + offsetX;
|
|
offsetCanvasY = Math.abs(centerX - viewBox[0]) * scale + offsetY;
|
|
width = Math.abs(viewBox[3] - viewBox[1]) * scale;
|
|
height = Math.abs(viewBox[2] - viewBox[0]) * scale;
|
|
} else {
|
|
offsetCanvasX = Math.abs(centerX - viewBox[0]) * scale + offsetX;
|
|
offsetCanvasY = Math.abs(centerY - viewBox[1]) * scale + offsetY;
|
|
width = Math.abs(viewBox[2] - viewBox[0]) * scale;
|
|
height = Math.abs(viewBox[3] - viewBox[1]) * scale;
|
|
}
|
|
// creating transform for the following operations:
|
|
// translate(-centerX, -centerY), rotate and flip vertically,
|
|
// scale, and translate(offsetCanvasX, offsetCanvasY)
|
|
this.transform = [
|
|
rotateA * scale,
|
|
rotateB * scale,
|
|
rotateC * scale,
|
|
rotateD * scale,
|
|
offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
|
|
offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
|
|
];
|
|
|
|
this.width = width;
|
|
this.height = height;
|
|
this.fontScale = scale;
|
|
}
|
|
PageViewport.prototype = /** @lends PDFJS.PageViewport.prototype */ {
|
|
/**
|
|
* Clones viewport with additional properties.
|
|
* @param args {Object} (optional) If specified, may contain the 'scale' or
|
|
* 'rotation' properties to override the corresponding properties in
|
|
* the cloned viewport.
|
|
* @returns {PDFJS.PageViewport} Cloned viewport.
|
|
*/
|
|
clone: function PageViewPort_clone(args) {
|
|
args = args || {};
|
|
var scale = 'scale' in args ? args.scale : this.scale;
|
|
var rotation = 'rotation' in args ? args.rotation : this.rotation;
|
|
return new PageViewport(this.viewBox.slice(), scale, rotation,
|
|
this.offsetX, this.offsetY, args.dontFlip);
|
|
},
|
|
/**
|
|
* Converts PDF point to the viewport coordinates. For examples, useful for
|
|
* converting PDF location into canvas pixel coordinates.
|
|
* @param x {number} X coordinate.
|
|
* @param y {number} Y coordinate.
|
|
* @returns {Object} Object that contains 'x' and 'y' properties of the
|
|
* point in the viewport coordinate space.
|
|
* @see {@link convertToPdfPoint}
|
|
* @see {@link convertToViewportRectangle}
|
|
*/
|
|
convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
|
|
return Util.applyTransform([x, y], this.transform);
|
|
},
|
|
/**
|
|
* Converts PDF rectangle to the viewport coordinates.
|
|
* @param rect {Array} xMin, yMin, xMax and yMax coordinates.
|
|
* @returns {Array} Contains corresponding coordinates of the rectangle
|
|
* in the viewport coordinate space.
|
|
* @see {@link convertToViewportPoint}
|
|
*/
|
|
convertToViewportRectangle:
|
|
function PageViewport_convertToViewportRectangle(rect) {
|
|
var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
|
|
var br = Util.applyTransform([rect[2], rect[3]], this.transform);
|
|
return [tl[0], tl[1], br[0], br[1]];
|
|
},
|
|
/**
|
|
* Converts viewport coordinates to the PDF location. For examples, useful
|
|
* for converting canvas pixel location into PDF one.
|
|
* @param x {number} X coordinate.
|
|
* @param y {number} Y coordinate.
|
|
* @returns {Object} Object that contains 'x' and 'y' properties of the
|
|
* point in the PDF coordinate space.
|
|
* @see {@link convertToViewportPoint}
|
|
*/
|
|
convertToPdfPoint: function PageViewport_convertToPdfPoint(x, y) {
|
|
return Util.applyInverseTransform([x, y], this.transform);
|
|
}
|
|
};
|
|
return PageViewport;
|
|
})();
|
|
|
|
var PDFStringTranslateTable = [
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2022, 0x2020, 0x2021, 0x2026, 0x2014,
|
|
0x2013, 0x192, 0x2044, 0x2039, 0x203A, 0x2212, 0x2030, 0x201E, 0x201C,
|
|
0x201D, 0x2018, 0x2019, 0x201A, 0x2122, 0xFB01, 0xFB02, 0x141, 0x152, 0x160,
|
|
0x178, 0x17D, 0x131, 0x142, 0x153, 0x161, 0x17E, 0, 0x20AC
|
|
];
|
|
|
|
function stringToPDFString(str) {
|
|
var i, n = str.length, strBuf = [];
|
|
if (str[0] === '\xFE' && str[1] === '\xFF') {
|
|
// UTF16BE BOM
|
|
for (i = 2; i < n; i += 2) {
|
|
strBuf.push(String.fromCharCode(
|
|
(str.charCodeAt(i) << 8) | str.charCodeAt(i + 1)));
|
|
}
|
|
} else {
|
|
for (i = 0; i < n; ++i) {
|
|
var code = PDFStringTranslateTable[str.charCodeAt(i)];
|
|
strBuf.push(code ? String.fromCharCode(code) : str.charAt(i));
|
|
}
|
|
}
|
|
return strBuf.join('');
|
|
}
|
|
|
|
function stringToUTF8String(str) {
|
|
return decodeURIComponent(escape(str));
|
|
}
|
|
|
|
function utf8StringToString(str) {
|
|
return unescape(encodeURIComponent(str));
|
|
}
|
|
|
|
function isEmptyObj(obj) {
|
|
for (var key in obj) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function isBool(v) {
|
|
return typeof v === 'boolean';
|
|
}
|
|
|
|
function isInt(v) {
|
|
return typeof v === 'number' && ((v | 0) === v);
|
|
}
|
|
|
|
function isNum(v) {
|
|
return typeof v === 'number';
|
|
}
|
|
|
|
function isString(v) {
|
|
return typeof v === 'string';
|
|
}
|
|
|
|
function isName(v) {
|
|
return v instanceof Name;
|
|
}
|
|
|
|
function isCmd(v, cmd) {
|
|
return v instanceof Cmd && (cmd === undefined || v.cmd === cmd);
|
|
}
|
|
|
|
function isDict(v, type) {
|
|
if (!(v instanceof Dict)) {
|
|
return false;
|
|
}
|
|
if (!type) {
|
|
return true;
|
|
}
|
|
var dictType = v.get('Type');
|
|
return isName(dictType) && dictType.name === type;
|
|
}
|
|
|
|
function isArray(v) {
|
|
return v instanceof Array;
|
|
}
|
|
|
|
function isStream(v) {
|
|
return typeof v === 'object' && v !== null && v.getBytes !== undefined;
|
|
}
|
|
|
|
function isArrayBuffer(v) {
|
|
return typeof v === 'object' && v !== null && v.byteLength !== undefined;
|
|
}
|
|
|
|
function isRef(v) {
|
|
return v instanceof Ref;
|
|
}
|
|
|
|
/**
|
|
* Promise Capability object.
|
|
*
|
|
* @typedef {Object} PromiseCapability
|
|
* @property {Promise} promise - A promise object.
|
|
* @property {function} resolve - Fullfills the promise.
|
|
* @property {function} reject - Rejects the promise.
|
|
*/
|
|
|
|
/**
|
|
* Creates a promise capability object.
|
|
* @alias PDFJS.createPromiseCapability
|
|
*
|
|
* @return {PromiseCapability} A capability object contains:
|
|
* - a Promise, resolve and reject methods.
|
|
*/
|
|
function createPromiseCapability() {
|
|
var capability = {};
|
|
capability.promise = new Promise(function (resolve, reject) {
|
|
capability.resolve = resolve;
|
|
capability.reject = reject;
|
|
});
|
|
return capability;
|
|
}
|
|
|
|
PDFJS.createPromiseCapability = createPromiseCapability;
|
|
|
|
/**
|
|
* Polyfill for Promises:
|
|
* The following promise implementation tries to generally implement the
|
|
* Promise/A+ spec. Some notable differences from other promise libaries are:
|
|
* - There currently isn't a seperate deferred and promise object.
|
|
* - Unhandled rejections eventually show an error if they aren't handled.
|
|
*
|
|
* Based off of the work in:
|
|
* https://bugzilla.mozilla.org/show_bug.cgi?id=810490
|
|
*/
|
|
(function PromiseClosure() {
|
|
if (globalScope.Promise) {
|
|
// Promises existing in the DOM/Worker, checking presence of all/resolve
|
|
if (typeof globalScope.Promise.all !== 'function') {
|
|
globalScope.Promise.all = function (iterable) {
|
|
var count = 0, results = [], resolve, reject;
|
|
var promise = new globalScope.Promise(function (resolve_, reject_) {
|
|
resolve = resolve_;
|
|
reject = reject_;
|
|
});
|
|
iterable.forEach(function (p, i) {
|
|
count++;
|
|
p.then(function (result) {
|
|
results[i] = result;
|
|
count--;
|
|
if (count === 0) {
|
|
resolve(results);
|
|
}
|
|
}, reject);
|
|
});
|
|
if (count === 0) {
|
|
resolve(results);
|
|
}
|
|
return promise;
|
|
};
|
|
}
|
|
if (typeof globalScope.Promise.resolve !== 'function') {
|
|
globalScope.Promise.resolve = function (value) {
|
|
return new globalScope.Promise(function (resolve) { resolve(value); });
|
|
};
|
|
}
|
|
if (typeof globalScope.Promise.reject !== 'function') {
|
|
globalScope.Promise.reject = function (reason) {
|
|
return new globalScope.Promise(function (resolve, reject) {
|
|
reject(reason);
|
|
});
|
|
};
|
|
}
|
|
if (typeof globalScope.Promise.prototype.catch !== 'function') {
|
|
globalScope.Promise.prototype.catch = function (onReject) {
|
|
return globalScope.Promise.prototype.then(undefined, onReject);
|
|
};
|
|
}
|
|
return;
|
|
}
|
|
//#if !MOZCENTRAL
|
|
var STATUS_PENDING = 0;
|
|
var STATUS_RESOLVED = 1;
|
|
var STATUS_REJECTED = 2;
|
|
|
|
// In an attempt to avoid silent exceptions, unhandled rejections are
|
|
// tracked and if they aren't handled in a certain amount of time an
|
|
// error is logged.
|
|
var REJECTION_TIMEOUT = 500;
|
|
|
|
var HandlerManager = {
|
|
handlers: [],
|
|
running: false,
|
|
unhandledRejections: [],
|
|
pendingRejectionCheck: false,
|
|
|
|
scheduleHandlers: function scheduleHandlers(promise) {
|
|
if (promise._status === STATUS_PENDING) {
|
|
return;
|
|
}
|
|
|
|
this.handlers = this.handlers.concat(promise._handlers);
|
|
promise._handlers = [];
|
|
|
|
if (this.running) {
|
|
return;
|
|
}
|
|
this.running = true;
|
|
|
|
setTimeout(this.runHandlers.bind(this), 0);
|
|
},
|
|
|
|
runHandlers: function runHandlers() {
|
|
var RUN_TIMEOUT = 1; // ms
|
|
var timeoutAt = Date.now() + RUN_TIMEOUT;
|
|
while (this.handlers.length > 0) {
|
|
var handler = this.handlers.shift();
|
|
|
|
var nextStatus = handler.thisPromise._status;
|
|
var nextValue = handler.thisPromise._value;
|
|
|
|
try {
|
|
if (nextStatus === STATUS_RESOLVED) {
|
|
if (typeof handler.onResolve === 'function') {
|
|
nextValue = handler.onResolve(nextValue);
|
|
}
|
|
} else if (typeof handler.onReject === 'function') {
|
|
nextValue = handler.onReject(nextValue);
|
|
nextStatus = STATUS_RESOLVED;
|
|
|
|
if (handler.thisPromise._unhandledRejection) {
|
|
this.removeUnhandeledRejection(handler.thisPromise);
|
|
}
|
|
}
|
|
} catch (ex) {
|
|
nextStatus = STATUS_REJECTED;
|
|
nextValue = ex;
|
|
}
|
|
|
|
handler.nextPromise._updateStatus(nextStatus, nextValue);
|
|
if (Date.now() >= timeoutAt) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (this.handlers.length > 0) {
|
|
setTimeout(this.runHandlers.bind(this), 0);
|
|
return;
|
|
}
|
|
|
|
this.running = false;
|
|
},
|
|
|
|
addUnhandledRejection: function addUnhandledRejection(promise) {
|
|
this.unhandledRejections.push({
|
|
promise: promise,
|
|
time: Date.now()
|
|
});
|
|
this.scheduleRejectionCheck();
|
|
},
|
|
|
|
removeUnhandeledRejection: function removeUnhandeledRejection(promise) {
|
|
promise._unhandledRejection = false;
|
|
for (var i = 0; i < this.unhandledRejections.length; i++) {
|
|
if (this.unhandledRejections[i].promise === promise) {
|
|
this.unhandledRejections.splice(i);
|
|
i--;
|
|
}
|
|
}
|
|
},
|
|
|
|
scheduleRejectionCheck: function scheduleRejectionCheck() {
|
|
if (this.pendingRejectionCheck) {
|
|
return;
|
|
}
|
|
this.pendingRejectionCheck = true;
|
|
setTimeout(function rejectionCheck() {
|
|
this.pendingRejectionCheck = false;
|
|
var now = Date.now();
|
|
for (var i = 0; i < this.unhandledRejections.length; i++) {
|
|
if (now - this.unhandledRejections[i].time > REJECTION_TIMEOUT) {
|
|
var unhandled = this.unhandledRejections[i].promise._value;
|
|
var msg = 'Unhandled rejection: ' + unhandled;
|
|
if (unhandled.stack) {
|
|
msg += '\n' + unhandled.stack;
|
|
}
|
|
warn(msg);
|
|
this.unhandledRejections.splice(i);
|
|
i--;
|
|
}
|
|
}
|
|
if (this.unhandledRejections.length) {
|
|
this.scheduleRejectionCheck();
|
|
}
|
|
}.bind(this), REJECTION_TIMEOUT);
|
|
}
|
|
};
|
|
|
|
function Promise(resolver) {
|
|
this._status = STATUS_PENDING;
|
|
this._handlers = [];
|
|
try {
|
|
resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
|
|
} catch (e) {
|
|
this._reject(e);
|
|
}
|
|
}
|
|
/**
|
|
* Builds a promise that is resolved when all the passed in promises are
|
|
* resolved.
|
|
* @param {array} array of data and/or promises to wait for.
|
|
* @return {Promise} New dependant promise.
|
|
*/
|
|
Promise.all = function Promise_all(promises) {
|
|
var resolveAll, rejectAll;
|
|
var deferred = new Promise(function (resolve, reject) {
|
|
resolveAll = resolve;
|
|
rejectAll = reject;
|
|
});
|
|
var unresolved = promises.length;
|
|
var results = [];
|
|
if (unresolved === 0) {
|
|
resolveAll(results);
|
|
return deferred;
|
|
}
|
|
function reject(reason) {
|
|
if (deferred._status === STATUS_REJECTED) {
|
|
return;
|
|
}
|
|
results = [];
|
|
rejectAll(reason);
|
|
}
|
|
for (var i = 0, ii = promises.length; i < ii; ++i) {
|
|
var promise = promises[i];
|
|
var resolve = (function(i) {
|
|
return function(value) {
|
|
if (deferred._status === STATUS_REJECTED) {
|
|
return;
|
|
}
|
|
results[i] = value;
|
|
unresolved--;
|
|
if (unresolved === 0) {
|
|
resolveAll(results);
|
|
}
|
|
};
|
|
})(i);
|
|
if (Promise.isPromise(promise)) {
|
|
promise.then(resolve, reject);
|
|
} else {
|
|
resolve(promise);
|
|
}
|
|
}
|
|
return deferred;
|
|
};
|
|
|
|
/**
|
|
* Checks if the value is likely a promise (has a 'then' function).
|
|
* @return {boolean} true if value is thenable
|
|
*/
|
|
Promise.isPromise = function Promise_isPromise(value) {
|
|
return value && typeof value.then === 'function';
|
|
};
|
|
|
|
/**
|
|
* Creates resolved promise
|
|
* @param value resolve value
|
|
* @returns {Promise}
|
|
*/
|
|
Promise.resolve = function Promise_resolve(value) {
|
|
return new Promise(function (resolve) { resolve(value); });
|
|
};
|
|
|
|
/**
|
|
* Creates rejected promise
|
|
* @param reason rejection value
|
|
* @returns {Promise}
|
|
*/
|
|
Promise.reject = function Promise_reject(reason) {
|
|
return new Promise(function (resolve, reject) { reject(reason); });
|
|
};
|
|
|
|
Promise.prototype = {
|
|
_status: null,
|
|
_value: null,
|
|
_handlers: null,
|
|
_unhandledRejection: null,
|
|
|
|
_updateStatus: function Promise__updateStatus(status, value) {
|
|
if (this._status === STATUS_RESOLVED ||
|
|
this._status === STATUS_REJECTED) {
|
|
return;
|
|
}
|
|
|
|
if (status === STATUS_RESOLVED &&
|
|
Promise.isPromise(value)) {
|
|
value.then(this._updateStatus.bind(this, STATUS_RESOLVED),
|
|
this._updateStatus.bind(this, STATUS_REJECTED));
|
|
return;
|
|
}
|
|
|
|
this._status = status;
|
|
this._value = value;
|
|
|
|
if (status === STATUS_REJECTED && this._handlers.length === 0) {
|
|
this._unhandledRejection = true;
|
|
HandlerManager.addUnhandledRejection(this);
|
|
}
|
|
|
|
HandlerManager.scheduleHandlers(this);
|
|
},
|
|
|
|
_resolve: function Promise_resolve(value) {
|
|
this._updateStatus(STATUS_RESOLVED, value);
|
|
},
|
|
|
|
_reject: function Promise_reject(reason) {
|
|
this._updateStatus(STATUS_REJECTED, reason);
|
|
},
|
|
|
|
then: function Promise_then(onResolve, onReject) {
|
|
var nextPromise = new Promise(function (resolve, reject) {
|
|
this.resolve = resolve;
|
|
this.reject = reject;
|
|
});
|
|
this._handlers.push({
|
|
thisPromise: this,
|
|
onResolve: onResolve,
|
|
onReject: onReject,
|
|
nextPromise: nextPromise
|
|
});
|
|
HandlerManager.scheduleHandlers(this);
|
|
return nextPromise;
|
|
},
|
|
|
|
catch: function Promise_catch(onReject) {
|
|
return this.then(undefined, onReject);
|
|
}
|
|
};
|
|
|
|
globalScope.Promise = Promise;
|
|
//#else
|
|
//throw new Error('DOM Promise is not present');
|
|
//#endif
|
|
})();
|
|
|
|
var StatTimer = (function StatTimerClosure() {
|
|
function rpad(str, pad, length) {
|
|
while (str.length < length) {
|
|
str += pad;
|
|
}
|
|
return str;
|
|
}
|
|
function StatTimer() {
|
|
this.started = {};
|
|
this.times = [];
|
|
this.enabled = true;
|
|
}
|
|
StatTimer.prototype = {
|
|
time: function StatTimer_time(name) {
|
|
if (!this.enabled) {
|
|
return;
|
|
}
|
|
if (name in this.started) {
|
|
warn('Timer is already running for ' + name);
|
|
}
|
|
this.started[name] = Date.now();
|
|
},
|
|
timeEnd: function StatTimer_timeEnd(name) {
|
|
if (!this.enabled) {
|
|
return;
|
|
}
|
|
if (!(name in this.started)) {
|
|
warn('Timer has not been started for ' + name);
|
|
}
|
|
this.times.push({
|
|
'name': name,
|
|
'start': this.started[name],
|
|
'end': Date.now()
|
|
});
|
|
// Remove timer from started so it can be called again.
|
|
delete this.started[name];
|
|
},
|
|
toString: function StatTimer_toString() {
|
|
var i, ii;
|
|
var times = this.times;
|
|
var out = '';
|
|
// Find the longest name for padding purposes.
|
|
var longest = 0;
|
|
for (i = 0, ii = times.length; i < ii; ++i) {
|
|
var name = times[i]['name'];
|
|
if (name.length > longest) {
|
|
longest = name.length;
|
|
}
|
|
}
|
|
for (i = 0, ii = times.length; i < ii; ++i) {
|
|
var span = times[i];
|
|
var duration = span.end - span.start;
|
|
out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
|
|
}
|
|
return out;
|
|
}
|
|
};
|
|
return StatTimer;
|
|
})();
|
|
|
|
PDFJS.createBlob = function createBlob(data, contentType) {
|
|
if (typeof Blob !== 'undefined') {
|
|
return new Blob([data], { type: contentType });
|
|
}
|
|
// Blob builder is deprecated in FF14 and removed in FF18.
|
|
var bb = new MozBlobBuilder();
|
|
bb.append(data);
|
|
return bb.getBlob(contentType);
|
|
};
|
|
|
|
PDFJS.createObjectURL = (function createObjectURLClosure() {
|
|
// Blob/createObjectURL is not available, falling back to data schema.
|
|
var digits =
|
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
|
|
return function createObjectURL(data, contentType) {
|
|
if (!PDFJS.disableCreateObjectURL &&
|
|
typeof URL !== 'undefined' && URL.createObjectURL) {
|
|
var blob = PDFJS.createBlob(data, contentType);
|
|
return URL.createObjectURL(blob);
|
|
}
|
|
|
|
var buffer = 'data:' + contentType + ';base64,';
|
|
for (var i = 0, ii = data.length; i < ii; i += 3) {
|
|
var b1 = data[i] & 0xFF;
|
|
var b2 = data[i + 1] & 0xFF;
|
|
var b3 = data[i + 2] & 0xFF;
|
|
var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
|
|
var d3 = i + 1 < ii ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
|
|
var d4 = i + 2 < ii ? (b3 & 0x3F) : 64;
|
|
buffer += digits[d1] + digits[d2] + digits[d3] + digits[d4];
|
|
}
|
|
return buffer;
|
|
};
|
|
})();
|
|
|
|
function MessageHandler(sourceName, targetName, comObj) {
|
|
this.sourceName = sourceName;
|
|
this.targetName = targetName;
|
|
this.comObj = comObj;
|
|
this.callbackIndex = 1;
|
|
this.postMessageTransfers = true;
|
|
var callbacksCapabilities = this.callbacksCapabilities = {};
|
|
var ah = this.actionHandler = {};
|
|
|
|
this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) {
|
|
var data = event.data;
|
|
if (data.targetName !== this.sourceName) {
|
|
return;
|
|
}
|
|
if (data.isReply) {
|
|
var callbackId = data.callbackId;
|
|
if (data.callbackId in callbacksCapabilities) {
|
|
var callback = callbacksCapabilities[callbackId];
|
|
delete callbacksCapabilities[callbackId];
|
|
if ('error' in data) {
|
|
callback.reject(data.error);
|
|
} else {
|
|
callback.resolve(data.data);
|
|
}
|
|
} else {
|
|
error('Cannot resolve callback ' + callbackId);
|
|
}
|
|
} else if (data.action in ah) {
|
|
var action = ah[data.action];
|
|
if (data.callbackId) {
|
|
var sourceName = this.sourceName;
|
|
var targetName = data.sourceName;
|
|
Promise.resolve().then(function () {
|
|
return action[0].call(action[1], data.data);
|
|
}).then(function (result) {
|
|
comObj.postMessage({
|
|
sourceName: sourceName,
|
|
targetName: targetName,
|
|
isReply: true,
|
|
callbackId: data.callbackId,
|
|
data: result
|
|
});
|
|
}, function (reason) {
|
|
if (reason instanceof Error) {
|
|
// Serialize error to avoid "DataCloneError"
|
|
reason = reason + '';
|
|
}
|
|
comObj.postMessage({
|
|
sourceName: sourceName,
|
|
targetName: targetName,
|
|
isReply: true,
|
|
callbackId: data.callbackId,
|
|
error: reason
|
|
});
|
|
});
|
|
} else {
|
|
action[0].call(action[1], data.data);
|
|
}
|
|
} else {
|
|
error('Unknown action from worker: ' + data.action);
|
|
}
|
|
}.bind(this);
|
|
comObj.addEventListener('message', this._onComObjOnMessage);
|
|
}
|
|
|
|
MessageHandler.prototype = {
|
|
on: function messageHandlerOn(actionName, handler, scope) {
|
|
var ah = this.actionHandler;
|
|
if (ah[actionName]) {
|
|
error('There is already an actionName called "' + actionName + '"');
|
|
}
|
|
ah[actionName] = [handler, scope];
|
|
},
|
|
/**
|
|
* Sends a message to the comObj to invoke the action with the supplied data.
|
|
* @param {String} actionName Action to call.
|
|
* @param {JSON} data JSON data to send.
|
|
* @param {Array} [transfers] Optional list of transfers/ArrayBuffers
|
|
*/
|
|
send: function messageHandlerSend(actionName, data, transfers) {
|
|
var message = {
|
|
sourceName: this.sourceName,
|
|
targetName: this.targetName,
|
|
action: actionName,
|
|
data: data
|
|
};
|
|
this.postMessage(message, transfers);
|
|
},
|
|
/**
|
|
* Sends a message to the comObj to invoke the action with the supplied data.
|
|
* Expects that other side will callback with the response.
|
|
* @param {String} actionName Action to call.
|
|
* @param {JSON} data JSON data to send.
|
|
* @param {Array} [transfers] Optional list of transfers/ArrayBuffers.
|
|
* @returns {Promise} Promise to be resolved with response data.
|
|
*/
|
|
sendWithPromise:
|
|
function messageHandlerSendWithPromise(actionName, data, transfers) {
|
|
var callbackId = this.callbackIndex++;
|
|
var message = {
|
|
sourceName: this.sourceName,
|
|
targetName: this.targetName,
|
|
action: actionName,
|
|
data: data,
|
|
callbackId: callbackId
|
|
};
|
|
var capability = createPromiseCapability();
|
|
this.callbacksCapabilities[callbackId] = capability;
|
|
try {
|
|
this.postMessage(message, transfers);
|
|
} catch (e) {
|
|
capability.reject(e);
|
|
}
|
|
return capability.promise;
|
|
},
|
|
/**
|
|
* Sends raw message to the comObj.
|
|
* @private
|
|
* @param message {Object} Raw message.
|
|
* @param transfers List of transfers/ArrayBuffers, or undefined.
|
|
*/
|
|
postMessage: function (message, transfers) {
|
|
if (transfers && this.postMessageTransfers) {
|
|
this.comObj.postMessage(message, transfers);
|
|
} else {
|
|
this.comObj.postMessage(message);
|
|
}
|
|
},
|
|
|
|
destroy: function () {
|
|
this.comObj.removeEventListener('message', this._onComObjOnMessage);
|
|
}
|
|
};
|
|
|
|
function loadJpegStream(id, imageUrl, objs) {
|
|
var img = new Image();
|
|
img.onload = (function loadJpegStream_onloadClosure() {
|
|
objs.resolve(id, img);
|
|
});
|
|
img.onerror = (function loadJpegStream_onerrorClosure() {
|
|
objs.resolve(id, null);
|
|
warn('Error during JPEG image loading');
|
|
});
|
|
img.src = imageUrl;
|
|
}
|