pkijs
Version:
Public Key Infrastructure (PKI) is the basis of how identity and key management is performed on the web today. PKIjs is a pure JavaScript library implementing the formats that are used in PKI applications. It is built on WebCrypto and aspires to make it p
1,707 lines (1,552 loc) • 1.83 MB
JavaScript
'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
};
// 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);
}
}
// Fatal errors that should trigger the fallback UI and halt execution by
// throwing an exception.
function error(msg)
{
// If multiple arguments were passed, pass them all to the log function.
if(arguments.length > 1)
{
var logArguments = ['Error:'];
logArguments.push.apply(logArguments, arguments);
console.log.apply(console, logArguments);
// Join the arguments into a single string for the lines below.
msg = [].join.call(arguments, ' ');
} else
{
console.log('Error: ' + msg);
}
console.log(backtrace());
UnsupportedManager.notify(UNSUPPORTED_FEATURES.unknown);
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'
};
var UnsupportedManager = PDFJS.UnsupportedManager =
(function UnsupportedManagerClosure()
{
var listeners = [];
return {
listen: function(cb)
{
listeners.push(cb);
},
notify: function(featureId)
{
warn('Unsupported feature "' + featureId + '"');
for(var i = 0, ii = listeners.length; i < ii; i++)
{
listeners[i](featureId);
}
}
};
})();
// 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':
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;
}
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;
})();
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;
})();
var InvalidPDFException = (function InvalidPDFExceptionClosure()
{
function InvalidPDFException(msg)
{
this.name = 'InvalidPDFException';
this.message = msg;
}
InvalidPDFException.prototype = new Error();
InvalidPDFException.constructor = InvalidPDFException;
return InvalidPDFException;
})();
var MissingPDFException = (function MissingPDFExceptionClosure()
{
function MissingPDFException(msg)
{
this.name = 'MissingPDFException';
this.message = msg;
}
MissingPDFException.prototype = new Error();
MissingPDFException.constructor = MissingPDFException;
return MissingPDFException;
})();
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)
{
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 stringToArray(str)
{
var length = str.length;
var array = [];
for(var i = 0; i < length; ++i)
{
array[i] = str.charCodeAt(i);
}
return array;
}
function stringToBytes(str)
{
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 || B2G || CHROME)
//// Lazy test if the userAgant 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() { }
Util.makeCssRgb = function Util_makeCssRgb(rgb)
{
return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
};
Util.makeCssCmyk = function Util_makeCssCmyk(cmyk)
{
var rgb = ColorSpace.singletons.cmyk.getRgb(cmyk, 0);
return Util.makeCssRgb(rgb);
};
// 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;
};
// TODO(mack): Rename appendToArray
Util.concatenateToArray = function concatenateToArray(arr1, arr2)
{
Array.prototype.push.apply(arr1, arr2);
};
Util.prependToArray = function concatenateToArray(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 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 isNull(v)
{
return v === null;
}
function isName(v)
{
return v instanceof Name;
}
function isCmd(v, cmd)
{
return v instanceof Cmd && (!cmd || 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 !== undefined &&
('getBytes' in v);
}
function isArrayBuffer(v)
{
return typeof v == 'object' && v !== null && v !== undefined &&
('byteLength' in v);
}
function isRef(v)
{
return v instanceof Ref;
}
function isPDFFunction(v)
{
var fnDict;
if(typeof v != 'object')
{
return false;
} else if(isDict(v))
{
fnDict = v;
} else if(isStream(v))
{
fnDict = v.dict;
} else
{
return false;
}
return fnDict.has('FunctionType');
}
/**
* 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 implment 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);
});
};
}
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 = [];
resolver.call(this, this._resolve.bind(this), this._reject.bind(this));
}
/**
* 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 = reject;
this.reject = reject;
});
this._handlers.push({
thisPromise: this,
onResolve: onResolve,
onReject: onReject,
nextPromise: nextPromise
});
HandlerManager.scheduleHandlers(this);
return nextPromise;
}
};
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(name, comObj)
{
this.name = name;
this.comObj = comObj;
this.callbackIndex = 1;
this.postMessageTransfers = true;
var callbacksCapabilities = this.callbacksCapabilities = {};
var ah = this.actionHandler = {};
ah['console_log'] = [function ahConsoleLog(data)
{
console.log.apply(console, data);
}];
ah['console_error'] = [function ahConsoleError(data)
{
console.error.apply(console, data);
}];
ah['_unsupported_feature'] = [function ah_unsupportedFeature(data)
{
UnsupportedManager.notify(data);
}];
comObj.onmessage = function messageHandlerComObjOnMessage(event)
{
var data = event.data;
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)
{
Promise.resolve().then(function()
{
return action[0].call(action[1], data.data);
}).then(function(result)
{
comObj.postMessage({
isReply: true,
callbackId: data.callbackId,
data: result
});
}, function(reason)
{
comObj.postMessage({
isReply: true,
callbackId: data.callbackId,
error: reason
});
});
} else
{
action[0].call(action[1], data.data);
}
} else
{
error('Unknown action from worker: ' + data.action);
}
};
}
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 t