<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: shared/util.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: shared/util.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>/* 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 global, process, __pdfjsdev_webpack__ */ import './compatibility'; import { ReadableStream } from '../../external/streams/streams-lib'; var globalScope = (typeof window !== 'undefined') ? window : (typeof global !== 'undefined') ? global : (typeof self !== 'undefined') ? self : this; var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0]; const NativeImageDecoding = { NONE: 'none', DECODE: 'decode', DISPLAY: 'display', }; 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 AnnotationFieldFlag = { READONLY: 0x0000001, REQUIRED: 0x0000002, NOEXPORT: 0x0000004, MULTILINE: 0x0001000, PASSWORD: 0x0002000, NOTOGGLETOOFF: 0x0004000, RADIO: 0x0008000, PUSHBUTTON: 0x0010000, COMBO: 0x0020000, EDIT: 0x0040000, SORT: 0x0080000, FILESELECT: 0x0100000, MULTISELECT: 0x0200000, DONOTSPELLCHECK: 0x0400000, DONOTSCROLL: 0x0800000, COMB: 0x1000000, RICHTEXT: 0x2000000, RADIOSINUNISON: 0x2000000, COMMITONSELCHANGE: 0x4000000, }; 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, }; var VERBOSITY_LEVELS = { errors: 0, warnings: 1, infos: 5, }; var CMapCompressionType = { NONE: 0, BINARY: 1, STREAM: 2, }; // All the possible operations for an operator list. var 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, }; var verbosity = VERBOSITY_LEVELS.warnings; function setVerbosityLevel(level) { verbosity = level; } function getVerbosityLevel() { return verbosity; } // 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 (verbosity >= VERBOSITY_LEVELS.infos) { console.log('Info: ' + msg); } } // Non-fatal warnings. function warn(msg) { if (verbosity >= VERBOSITY_LEVELS.warnings) { console.log('Warning: ' + msg); } } // Deprecated API function -- display regardless of the PDFJS.verbosity setting. function deprecated(details) { console.log('Deprecated API usage: ' + details); } // Fatal errors that should trigger the fallback UI and halt execution by // throwing an exception. function error(msg) { if (verbosity >= 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 = { unknown: 'unknown', forms: 'forms', javaScript: 'javaScript', smask: 'smask', shadingPattern: 'shadingPattern', font: 'font', }; // Checks if URLs have the same origin. For non-HTTP based URLs, returns false. function isSameOrigin(baseUrl, otherUrl) { try { var base = new URL(baseUrl); if (!base.origin || base.origin === 'null') { return false; // non-HTTP url } } catch (e) { return false; } var other = new URL(otherUrl, base); return base.origin === other.origin; } // Checks if URLs use one of the whitelisted protocols, e.g. to avoid XSS. function isValidProtocol(url) { if (!url) { return false; } switch (url.protocol) { case 'http:': case 'https:': case 'ftp:': case 'mailto:': case 'tel:': return true; default: return false; } } /** * Attempts to create a valid absolute URL (utilizing `isValidProtocol`). * @param {URL|string} url - An absolute, or relative, URL. * @param {URL|string} baseUrl - An absolute URL. * @returns Either a valid {URL}, or `null` otherwise. */ function createValidAbsoluteUrl(url, baseUrl) { if (!url) { return null; } try { var absoluteUrl = baseUrl ? new URL(url, baseUrl) : new URL(url); if (isValidProtocol(absoluteUrl)) { return absoluteUrl; } } catch (ex) { /* `new URL()` will throw on incorrect data. */ } return null; } function shadow(obj, prop, value) { Object.defineProperty(obj, prop, { value, enumerable: true, configurable: true, writable: false, }); return value; } function getLookupTableFactory(initializer) { var lookup; return function () { if (initializer) { lookup = Object.create(null); initializer(lookup); initializer = null; } return lookup; }; } var 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 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; })(); 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; })(); var NullCharactersRegExp = /\x00/g; function removeNullCharacters(str) { if (typeof str !== 'string') { warn('The argument for removeNullCharacters must be a string.'); return str; } return str.replace(NullCharactersRegExp, ''); } 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; } /** * Gets length of the array (Array, Uint8Array, or string) in bytes. * @param {Array|Uint8Array|string} arr * @returns {number} */ function arrayByteLength(arr) { if (arr.length !== undefined) { return arr.length; } assert(arr.byteLength !== undefined); return arr.byteLength; } /** * Combines array items (arrays) into single Uint8Array object. * @param {Array} arr - the array of the arrays (Array, Uint8Array, or string). * @returns {Uint8Array} */ function arraysToBytes(arr) { // Shortcut: if first and only item is Uint8Array, return it. if (arr.length === 1 && (arr[0] instanceof Uint8Array)) { return arr[0]; } var resultLength = 0; var i, ii = arr.length; var item, itemLength; for (i = 0; i < ii; i++) { item = arr[i]; itemLength = arrayByteLength(item); resultLength += itemLength; } var pos = 0; var data = new Uint8Array(resultLength); for (i = 0; i < ii; i++) { item = arr[i]; if (!(item instanceof Uint8Array)) { if (typeof item === 'string') { item = stringToBytes(item); } else { item = new Uint8Array(item); } } itemLength = item.byteLength; data.set(item, pos); pos += itemLength; } return data; } 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(4); buffer8[0] = 1; var view32 = new Uint32Array(buffer8.buffer, 0, 1); return (view32[0] === 1); } // Checks if it's possible to eval JS expressions. function isEvalSupported() { try { new Function(''); // eslint-disable-line no-new, no-new-func return true; } catch (e) { return false; } } var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0]; var 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; }; var ROMAN_NUMBER_MAP = [ '', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM', '', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC', '', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX' ]; /** * Converts positive integers to (upper case) Roman numerals. * @param {integer} number - The number that should be converted. * @param {boolean} lowerCase - Indicates if the result should be converted * to lower case letters. The default is false. * @return {string} The resulting Roman number. */ Util.toRoman = function Util_toRoman(number, lowerCase) { assert(isInt(number) && number > 0, 'The number should be a positive integer.'); var pos, romanBuf = []; // Thousands while (number >= 1000) { number -= 1000; romanBuf.push('M'); } // Hundreds pos = (number / 100) | 0; number %= 100; romanBuf.push(ROMAN_NUMBER_MAP[pos]); // Tens pos = (number / 10) | 0; number %= 10; romanBuf.push(ROMAN_NUMBER_MAP[10 + pos]); // Ones romanBuf.push(ROMAN_NUMBER_MAP[20 + number]); var romanStr = romanBuf.join(''); return (lowerCase ? romanStr.toLowerCase() : romanStr); }; 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, getArray) { while (dict && !dict.has(name)) { dict = dict.get('Parent'); } if (!dict) { return null; } return getArray ? dict.getArray(name) : 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 PageViewport */ var 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 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 {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 isArray(v) { return v instanceof Array; } function isArrayBuffer(v) { return typeof v === 'object' && v !== null && v.byteLength !== undefined; } // Checks if ch is one of the following characters: SPACE, TAB, CR or LF. function isSpace(ch) { return (ch === 0x20 || ch === 0x09 || ch === 0x0D || ch === 0x0A); } function isNodeJS() { // The if below protected by __pdfjsdev_webpack__ check from webpack parsing. if (typeof __pdfjsdev_webpack__ === 'undefined') { return typeof process === 'object' && process + '' === '[object process]'; } return false; } /** * Promise Capability object. * * @typedef {Object} PromiseCapability * @property {Promise} promise - A promise object. * @property {function} resolve - Fulfills the promise. * @property {function} reject - Rejects the promise. */ /** * Creates a promise capability object. * @alias 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; } var StatTimer = (function StatTimerClosure() { function rpad(str, pad, length) { while (str.length < length) { str += pad; } return str; } function StatTimer() { this.started = Object.create(null); 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; })(); var createBlob = function createBlob(data, contentType) { if (typeof Blob !== 'undefined') { return new Blob([data], { type: contentType, }); } throw new Error('The "Blob" constructor is not supported.'); }; var createObjectURL = (function createObjectURLClosure() { // Blob/createObjectURL is not available, falling back to data schema. var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; return function createObjectURL(data, contentType, forceDataSchema = false) { if (!forceDataSchema && URL.createObjectURL) { var blob = 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 resolveCall(fn, args, thisArg = null) { if (!fn) { return Promise.resolve(undefined); } return new Promise((resolve, reject) => { resolve(fn.apply(thisArg, args)); }); } function resolveOrReject(capability, success, reason) { if (success) { capability.resolve(); } else { capability.reject(reason); } } function finalize(promise) { return Promise.resolve(promise).catch(() => {}); } function MessageHandler(sourceName, targetName, comObj) { this.sourceName = sourceName; this.targetName = targetName; this.comObj = comObj; this.callbackId = 1; this.streamId = 1; this.postMessageTransfers = true; this.streamSinks = Object.create(null); this.streamControllers = Object.create(null); let callbacksCapabilities = this.callbacksCapabilities = Object.create(null); let ah = this.actionHandler = Object.create(null); this._onComObjOnMessage = (event) => { let data = event.data; if (data.targetName !== this.sourceName) { return; } if (data.stream) { this._processStreamMessage(data); } else if (data.isReply) { let callbackId = data.callbackId; if (data.callbackId in callbacksCapabilities) { let 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) { let action = ah[data.action]; if (data.callbackId) { let sourceName = this.sourceName; let targetName = data.sourceName; Promise.resolve().then(function () { return action[0].call(action[1], data.data); }).then((result) => { comObj.postMessage({ sourceName, targetName, isReply: true, callbackId: data.callbackId, data: result, }); }, (reason) => { if (reason instanceof Error) { // Serialize error to avoid "DataCloneError" reason = reason + ''; } comObj.postMessage({ sourceName, targetName, isReply: true, callbackId: data.callbackId, error: reason, }); }); } else if (data.streamId) { this._createStreamSink(data); } else { action[0].call(action[1], data.data); } } else { error('Unknown action from worker: ' + data.action); } }; comObj.addEventListener('message', this._onComObjOnMessage); } MessageHandler.prototype = { on(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(actionName, data, transfers) { var message = { sourceName: this.sourceName, targetName: this.targetName, action: actionName, data, }; this.postMessage(message, transfers); }, /** * Sends a message to the comObj to invoke the action with the supplied data. * Expects that the 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(actionName, data, transfers) { var callbackId = this.callbackId++; var message = { sourceName: this.sourceName, targetName: this.targetName, action: actionName, data, callbackId, }; var capability = createPromiseCapability(); this.callbacksCapabilities[callbackId] = capability; try { this.postMessage(message, transfers); } catch (e) { capability.reject(e); } return capability.promise; }, /** * Sends a message to the comObj to invoke the action with the supplied data. * Expect that the other side will callback to signal 'start_complete'. * @param {String} actionName - Action to call. * @param {JSON} data - JSON data to send. * @param {Object} queueingStrategy - strategy to signal backpressure based on * internal queue. * @param {Array} [transfers] - Optional list of transfers/ArrayBuffers. * @return {ReadableStream} ReadableStream to read data in chunks. */ sendWithStream(actionName, data, queueingStrategy, transfers) { let streamId = this.streamId++; let sourceName = this.sourceName; let targetName = this.targetName; return new ReadableStream({ start: (controller) => { let startCapability = createPromiseCapability(); this.streamControllers[streamId] = { controller, startCall: startCapability, }; this.postMessage({ sourceName, targetName, action: actionName, streamId, data, desiredSize: controller.desiredSize, }); // Return Promise for Async process, to signal success/failure. return startCapability.promise; }, pull: (controller) => { let pullCapability = createPromiseCapability(); this.streamControllers[streamId].pullCall = pullCapability; this.postMessage({ sourceName, targetName, stream: 'pull', streamId, desiredSize: controller.desiredSize, }); // Returning Promise will not call "pull" // again until current pull is resolved. return pullCapability.promise; }, cancel: (reason) => { let cancelCapability = createPromiseCapability(); this.streamControllers[streamId].cancelCall = cancelCapability; this.postMessage({ sourceName, targetName, stream: 'cancel', reason, streamId, }); // Return Promise to signal success or failure. return cancelCapability.promise; }, }, queueingStrategy); }, _createStreamSink(data) { let self = this; let action = this.actionHandler[data.action]; let streamId = data.streamId; let desiredSize = data.desiredSize; let sourceName = this.sourceName; let targetName = data.sourceName; let capability = createPromiseCapability(); let sendStreamRequest = ({ stream, chunk, success, reason, }) => { this.comObj.postMessage({ sourceName, targetName, stream, streamId, chunk, success, reason, }); }; let streamSink = { enqueue(chunk, size = 1) { let lastDesiredSize = this.desiredSize; this.desiredSize -= size; // Enqueue decreases the desiredSize property of sink, // so when it changes from positive to negative, // set ready as unresolved promise. if (lastDesiredSize > 0 && this.desiredSize <= 0) { this.sinkCapability = createPromiseCapability(); this.ready = this.sinkCapability.promise; } sendStreamRequest({ stream: 'enqueue', chunk, }); }, close() { sendStreamRequest({ stream: 'close', }); delete self.streamSinks[streamId]; }, error(reason) { sendStreamRequest({ stream: 'error', reason, }); }, sinkCapability: capability, onPull: null, onCancel: null, desiredSize, ready: null, }; streamSink.sinkCapability.resolve(); streamSink.ready = streamSink.sinkCapability.promise; this.streamSinks[streamId] = streamSink; resolveCall(action[0], [data.data, streamSink], action[1]).then(() => { sendStreamRequest({ stream: 'start_complete', success: true, }); }, (reason) => { sendStreamRequest({ stream: 'start_complete', success: false, reason, }); }); }, _processStreamMessage(data) { let sourceName = this.sourceName; let targetName = data.sourceName; let streamId = data.streamId; let sendStreamResponse = ({ stream, success, reason, }) => { this.comObj.postMessage({ sourceName, targetName, stream, success, streamId, reason, }); }; let deleteStreamController = () => { // Delete streamController only when start, pull and // cancel callbacks are resolved, to avoid "TypeError". Promise.all([ this.streamControllers[data.streamId].startCall, this.streamControllers[data.streamId].pullCall, this.streamControllers[data.streamId].cancelCall ].map(function(capability) { return capability && finalize(capability.promise); })).then(() => { delete this.streamControllers[data.streamId]; }); }; switch (data.stream) { case 'start_complete': resolveOrReject(this.streamControllers[data.streamId].startCall, data.success, data.reason); break; case 'pull_complete': resolveOrReject(this.streamControllers[data.streamId].pullCall, data.success, data.reason); break; case 'pull': // Ignore any pull after close is called. if (!this.streamSinks[data.streamId]) { sendStreamResponse({ stream: 'pull_complete', success: true, }); break; } // Pull increases the desiredSize property of sink, // so when it changes from negative to positive, // set ready property as resolved promise. if (this.streamSinks[data.streamId].desiredSize <= 0 && data.desiredSize > 0) { this.streamSinks[data.streamId].sinkCapability.resolve(); } // Reset desiredSize property of sink on every pull. this.streamSinks[data.streamId].desiredSize = data.desiredSize; resolveCall(this.streamSinks[data.streamId].onPull).then(() => { sendStreamResponse({ stream: 'pull_complete', success: true, }); }, (reason) => { sendStreamResponse({ stream: 'pull_complete', success: false, reason, }); }); break; case 'enqueue': this.streamControllers[data.streamId].controller.enqueue(data.chunk); break; case 'close': this.streamControllers[data.streamId].controller.close(); deleteStreamController(); break; case 'error': this.streamControllers[data.streamId].controller.error(data.reason); deleteStreamController(); break; case 'cancel_complete': resolveOrReject(this.streamControllers[data.streamId].cancelCall, data.success, data.reason); deleteStreamController(); break; case 'cancel': resolveCall(this.streamSinks[data.streamId].onCancel, [data.reason]).then(() => { sendStreamResponse({ stream: 'cancel_complete', success: true, }); }, (reason) => { sendStreamResponse({ stream: 'cancel_complete', success: false, reason, }); }); delete this.streamSinks[data.streamId]; break; default: throw new Error('Unexpected stream case'); } }, /** * Sends raw message to the comObj. * @private * @param {Object} message - Raw message. * @param transfers List of transfers/ArrayBuffers, or undefined. */ postMessage(message, transfers) { if (transfers && this.postMessageTransfers) { this.comObj.postMessage(message, transfers); } else { this.comObj.postMessage(message); } }, destroy() { 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; } export { FONT_IDENTITY_MATRIX, IDENTITY_MATRIX, OPS, VERBOSITY_LEVELS, UNSUPPORTED_FEATURES, AnnotationBorderStyleType, AnnotationFieldFlag, AnnotationFlag, AnnotationType, FontType, ImageKind, CMapCompressionType, InvalidPDFException, MessageHandler, MissingDataException, MissingPDFException, NativeImageDecoding, NotImplementedException, PageViewport, PasswordException, PasswordResponses, StatTimer, StreamType, TextRenderingMode, UnexpectedResponseException, UnknownErrorException, Util, XRefParseException, arrayByteLength, arraysToBytes, assert, bytesToString, createBlob, createPromiseCapability, createObjectURL, deprecated, error, getLookupTableFactory, getVerbosityLevel, globalScope, info, isArray, isArrayBuffer, isBool, isEmptyObj, isInt, isNum, isString, isSpace, isNodeJS, isSameOrigin, createValidAbsoluteUrl, isLittleEndian, isEvalSupported, loadJpegStream, log2, readInt8, readUint16, readUint32, removeNullCharacters, ReadableStream, setVerbosityLevel, shadow, string32, stringToBytes, stringToPDFString, stringToUTF8String, utf8StringToString, warn, }; </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Externals</h3><ul><li><a href="external-Promise.html">Promise</a></li></ul><h3>Classes</h3><ul><li><a href="AnnotationBorderStyle.html">AnnotationBorderStyle</a></li><li><a href="AnnotationFactory.html">AnnotationFactory</a></li><li><a href="PageViewport.html">PageViewport</a></li><li><a href="PDFDataRangeTransport.html">PDFDataRangeTransport</a></li><li><a href="PDFDocumentLoadingTask.html">PDFDocumentLoadingTask</a></li><li><a href="PDFDocumentProxy.html">PDFDocumentProxy</a></li><li><a href="PDFJS.html">PDFJS</a></li><li><a href="PDFPageProxy.html">PDFPageProxy</a></li><li><a href="PDFWorker.html">PDFWorker</a></li><li><a href="RenderTask.html">RenderTask</a></li></ul><h3>Global</h3><ul><li><a href="global.html#_UnsupportedManager">_UnsupportedManager</a></li><li><a href="global.html#arrayByteLength">arrayByteLength</a></li><li><a href="global.html#arraysToBytes">arraysToBytes</a></li><li><a href="global.html#createPromiseCapability">createPromiseCapability</a></li><li><a href="global.html#createValidAbsoluteUrl">createValidAbsoluteUrl</a></li><li><a href="global.html#getDocument">getDocument</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.3</a> on Mon Jun 12 2017 08:43:32 GMT+0000 (Coordinated Universal Time) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>