# Source: shared/util.js

    /* 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,
    };